Modifica las extensiones por acciones registradas

This commit is contained in:
Manuel Cillero 2022-05-04 18:07:56 +02:00
parent 9eede3321a
commit d0e566aede
26 changed files with 309 additions and 100 deletions

View file

@ -0,0 +1,27 @@
use crate::Lazy;
use super::{ActionItem, ActionsHolder, ActionTrait};
use std::sync::RwLock;
use std::collections::HashMap;
// Registered actions.
static ACTIONS: Lazy<RwLock<HashMap<&str, ActionsHolder>>> = Lazy::new(|| {
RwLock::new(HashMap::new())
});
pub fn register_action(action: impl ActionTrait) {
let mut hmap = ACTIONS.write().unwrap();
let action_name = action.type_name();
if let Some(actions) = hmap.get_mut(action_name) {
actions.add(action);
} else {
hmap.insert(action_name, ActionsHolder::new_with(action));
}
}
pub fn run_actions<B, F>(type_name: &'static str, f: F) where F: FnMut(&ActionItem) -> B {
let hmap = ACTIONS.read().unwrap();
if let Some(actions) = hmap.get(type_name) {
actions.iter_map(f)
}
}

View file

@ -1,6 +1,8 @@
use crate::util;
pub trait BaseExtension {
pub use std::any::Any as AnyAction;
pub trait BaseAction {
fn type_name(&self) -> &'static str;
fn single_name(&self) -> &'static str;
@ -8,14 +10,17 @@ pub trait BaseExtension {
fn qualified_name(&self, last: u8) -> &'static str;
}
/// Las extensiones deben extender este *trait*.
pub trait ExtensionTrait: BaseExtension + Send + Sync {
pub trait ActionTrait: AnyAction + BaseAction + Send + Sync {
fn new() -> Self where Self: Sized;
fn weight(&self) -> i8 {
0
}
fn as_ref_any(&self) -> &dyn AnyAction;
}
impl<E: ?Sized + ExtensionTrait> BaseExtension for E {
impl<C: ?Sized + ActionTrait> BaseAction for C {
fn type_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
@ -28,3 +33,7 @@ impl<E: ?Sized + ExtensionTrait> BaseExtension for E {
util::partial_type_name(std::any::type_name::<Self>(), last)
}
}
pub fn action_ref<A: 'static>(action: &dyn ActionTrait) -> &A {
action.as_ref_any().downcast_ref::<A>().unwrap()
}

View file

@ -0,0 +1,30 @@
use super::ActionTrait;
use std::sync::{Arc, RwLock};
pub type ActionItem = Box<dyn ActionTrait>;
#[derive(Clone)]
pub struct ActionsHolder(Arc<RwLock<Vec<ActionItem>>>);
impl ActionsHolder {
pub fn new() -> Self {
ActionsHolder(Arc::new(RwLock::new(Vec::new())))
}
pub fn new_with(action: impl ActionTrait) -> Self {
let mut container = ActionsHolder::new();
container.add(action);
container
}
pub fn add(&mut self, action: impl ActionTrait) {
let mut actions = self.0.write().unwrap();
actions.push(Box::new(action));
actions.sort_by_key(|a| a.weight());
}
pub fn iter_map<B, F>(&self, f: F) where Self: Sized, F: FnMut(&ActionItem) -> B {
let _ = self.0.read().unwrap().iter().map(f);
}
}

View file

@ -0,0 +1,19 @@
mod definition;
pub use definition::{
ActionTrait,
AnyAction,
BaseAction,
action_ref,
};
mod holder;
pub use holder::{
ActionItem,
ActionsHolder,
};
mod all;
pub use all::{
register_action,
run_actions,
};

View file

@ -0,0 +1,55 @@
use crate::api::action::{ActionTrait, AnyAction};
use super::{ComponentTrait, PageAssets};
pub enum TypeAction {
BeforeRenderComponent(fn(&mut dyn ComponentTrait, &mut PageAssets)),
None,
}
pub struct ComponentAction {
action: TypeAction,
weight: i8,
}
impl ActionTrait for ComponentAction {
fn new() -> Self {
ComponentAction {
action: TypeAction::None,
weight: 0,
}
}
fn weight(&self) -> i8 {
self.weight
}
fn as_ref_any(&self) -> &dyn AnyAction {
self
}
}
impl ComponentAction {
pub fn new(action: TypeAction) -> Self {
ComponentAction {
action,
weight: 0,
}
}
pub fn new_with_weight(action: TypeAction, weight: i8) -> Self {
ComponentAction {
action,
weight,
}
}
pub fn before_render_component(
&self,
component: &mut dyn ComponentTrait,
assets: &mut PageAssets)
{
if let TypeAction::BeforeRenderComponent(f) = self.action {
f(component, assets)
}
}
}

View file

@ -0,0 +1,22 @@
use crate::Lazy;
use super::{ComponentTrait, PageContainer};
use std::sync::RwLock;
use std::collections::HashMap;
static COMPONENTS: Lazy<RwLock<HashMap<&str, PageContainer>>> = Lazy::new(|| {
RwLock::new(HashMap::new())
});
pub fn add_component_to(region: &'static str, component: impl ComponentTrait) {
let mut hmap = COMPONENTS.write().unwrap();
if let Some(regions) = hmap.get_mut(region) {
regions.add(component);
} else {
hmap.insert(region, PageContainer::new_with(component));
}
}
pub fn common_components() -> HashMap<&'static str, PageContainer> {
COMPONENTS.read().unwrap().clone()
}

View file

@ -1,7 +1,7 @@
use crate::{Lazy, base, concat_string};
use crate::config::SETTINGS;
use crate::html::{Markup, PreEscaped, html};
use crate::core::theme::*;
use crate::api::theme::*;
static DEFAULT_THEME: Lazy<&dyn ThemeTrait> = Lazy::new(|| {
match theme_by_name(&SETTINGS.app.theme) {

View file

@ -1,5 +1,5 @@
use crate::html::{Markup, html};
use crate::core::response::page::{PageAssets, ComponentTrait, render_component};
use super::{PageAssets, ComponentTrait, render_component};
use std::sync::{Arc, RwLock};

View file

@ -1,6 +1,8 @@
use crate::html::{Markup, html};
use crate::core::response::page::PageAssets;
use crate::util;
use crate::api::action::{action_ref, run_actions};
use super::PageAssets;
use super::action::ComponentAction;
pub use std::any::Any as AnyComponent;
@ -67,3 +69,27 @@ pub fn component_ref<C: 'static>(component: &dyn ComponentTrait) -> &C {
pub fn component_mut<C: 'static>(component: &mut dyn ComponentTrait) -> &mut C {
component.as_mut_any().downcast_mut::<C>().unwrap()
}
pub fn render_component(component: &mut dyn ComponentTrait, assets: &mut PageAssets) -> Markup {
// Acciones del componente antes de renderizar.
component.before_render(assets);
// Acciones de los módulos antes de renderizar el componente.
run_actions(
"",
|a| action_ref::<ComponentAction>(&**a).before_render_component(component, assets)
);
// Acciones del tema antes de renderizar el componente.
assets.theme().before_render_component(component, assets);
match component.is_renderable() {
true => {
match assets.theme().render_component(component, assets) {
Some(html) => html,
None => component.default_render(assets)
}
},
false => html! {}
}
}

View file

@ -1,3 +1,5 @@
pub mod action;
mod assets;
pub use assets::{
Favicon,
@ -6,22 +8,24 @@ pub use assets::{
PageAssets,
};
mod component;
pub use component::{
mod definition;
pub use definition::{
AnyComponent,
BaseComponent,
ComponentTrait,
component_ref,
component_mut,
};
use definition::render_component;
mod container;
pub use container::PageContainer;
mod page;
pub use page::Page;
pub use page::render_component;
pub use page::add_component_to;
mod all;
pub use all::{
add_component_to,
common_components,
};
pub fn render_always() -> bool { true }

4
pagetop/src/api/mod.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod action; // API to define functions that alter the behavior of PageTop core.
pub mod component; // API para crear nuevos componentes.
pub mod module; // API para añadir módulos con nuevas funcionalidades.
pub mod theme; // API para crear temas.

View file

@ -1,20 +1,14 @@
use crate::{Lazy, app, run_now, trace};
use crate::db::*;
use super::{ExtensionTrait, ModuleTrait};
use super::ModuleTrait;
use std::sync::{Arc, RwLock};
use std::collections::HashMap;
use std::sync::RwLock;
// Módulos registrados.
static MODULES: Lazy<RwLock<Vec<&dyn ModuleTrait>>> = Lazy::new(|| {
RwLock::new(Vec::new())
});
// Extensiones registradas.
static EXTENSIONS: Lazy<RwLock<HashMap<&str, Arc<Vec<&dyn ExtensionTrait>>>>> = Lazy::new(|| {
RwLock::new(HashMap::new())
});
pub fn register_module(module: &'static dyn ModuleTrait) {
let mut list: Vec<&dyn ModuleTrait> = Vec::new();
add_to(&mut list, module);
@ -28,19 +22,6 @@ fn add_to(list: &mut Vec<&dyn ModuleTrait>, module: &'static dyn ModuleTrait) {
trace::debug!("Registering \"{}\" module", module.single_name());
list.push(module);
let mut hmap = EXTENSIONS.write().unwrap();
for e in module.extensions().iter() {
if let Some(extensions) = hmap.get_mut(e.type_name()) {
let v = Arc::get_mut(extensions).unwrap();
v.push(*e);
v.sort_by_key(|e| e.weight());
} else {
hmap.insert(e.type_name(), Arc::new(vec![*e]));
}
}
let mut dependencies = module.dependencies();
dependencies.reverse();
for d in dependencies.iter() {
@ -56,13 +37,6 @@ pub fn modules(cfg: &mut app::web::ServiceConfig) {
}
}
pub fn extensions(type_name: &'static str) -> Option<Arc<Vec<&dyn ExtensionTrait>>> {
match EXTENSIONS.read().unwrap().get(type_name) {
Some(extensions) => Some(extensions.clone()),
_ => None,
}
}
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
pub fn migrations() {
run_now({

View file

@ -3,8 +3,6 @@ use crate::{app, util};
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
use crate::db;
use super::ExtensionTrait;
pub trait BaseModule {
fn type_name(&self) -> &'static str;
@ -27,10 +25,6 @@ pub trait ModuleTrait: BaseModule + Send + Sync {
fn configure_module(&self, cfg: &mut app::web::ServiceConfig) {
}
fn extensions(&self) -> Vec<&'static dyn ExtensionTrait> {
vec![]
}
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
#[allow(unused_variables)]
fn migrations(&self) -> Vec<Box<dyn db::MigrationTrait>> {

View file

@ -0,0 +1,10 @@
mod definition;
pub use definition::{
BaseModule,
ModuleTrait,
};
pub(crate) mod all;
pub use all::{
register_module,
};

View file

@ -1,7 +1,8 @@
use crate::{app, concat_string};
use crate::config::SETTINGS;
use crate::html::{Markup, html};
use crate::core::response::page::{ComponentTrait, Favicon, Page, PageAssets};
use crate::api::component::{ComponentTrait, Favicon, PageAssets};
use crate::response::page::Page;
use crate::base::component::Chunck;
use crate::util;

View file

@ -1,6 +1,6 @@
use crate::{Lazy, app, base, trace};
use crate::config::SETTINGS;
use crate::core::{module, theme};
use crate::api::{module, theme};
use std::io::Error;
use actix_web::middleware::normalize::{NormalizePath, TrailingSlash};

View file

@ -1,3 +0,0 @@
pub mod module; // API para añadir módulos con nuevas funcionalidades.
pub mod response; // Tipos de respuestas web.
pub mod theme; // Temas predefinidos y API para crear temas.

View file

@ -1,16 +0,0 @@
mod definition;
pub use definition::{
BaseModule,
ModuleTrait,
};
mod extension;
pub use extension::{
BaseExtension,
ExtensionTrait,
};
pub(crate) mod all;
pub use all::{
extensions,
register_module
};

View file

@ -18,7 +18,9 @@ pub mod html; // HTML en código.
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
pub mod db; // Acceso a base de datos.
pub mod core; // APIs para módulos, respuestas web y temas.
pub mod api; // Main APIs for actions, components, modules and themes.
pub mod response; // Tipos de respuestas web.
pub mod app; // Aplicación y servidor web.
pub mod base; // Base de componentes, módulos y temas.
pub mod util; // Macros y funciones útiles.

View file

@ -16,12 +16,15 @@ pub use crate::html::*;
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
pub use crate::{db, db::*, boxed_migration};
pub use crate::core::{
pub use crate::api::{
action::*,
component::*,
module::*,
response::page::*,
theme::*,
};
pub use crate::response::page::*;
pub use crate::app;
pub use crate::app::application::{Application, essence};

View file

@ -0,0 +1,63 @@
use crate::api::action::{ActionTrait, AnyAction};
use crate::api::component::{ComponentTrait, PageAssets};
use super::Page;
pub enum TypeAction {
BeforeRenderPage(fn(&mut Page)),
BeforeRenderComponent(fn(&mut dyn ComponentTrait, &mut PageAssets)),
None,
}
pub struct PageAction {
action: TypeAction,
weight: i8,
}
impl ActionTrait for PageAction {
fn new() -> Self {
PageAction {
action: TypeAction::None,
weight: 0,
}
}
fn weight(&self) -> i8 {
self.weight
}
fn as_ref_any(&self) -> &dyn AnyAction {
self
}
}
impl PageAction {
pub fn new(action: TypeAction) -> Self {
PageAction {
action,
weight: 0,
}
}
pub fn new_with_weight(action: TypeAction, weight: i8) -> Self {
PageAction {
action,
weight,
}
}
pub fn before_render_page(&self, page: &mut Page) {
if let TypeAction::BeforeRenderPage(f) = self.action {
f(page)
}
}
pub fn before_render_component(
&self,
component: &mut dyn ComponentTrait,
assets: &mut PageAssets)
{
if let TypeAction::BeforeRenderComponent(f) = self.action {
f(component, assets)
}
}
}

View file

@ -0,0 +1,4 @@
pub mod action;
mod page;
pub use page::Page;

View file

@ -1,14 +1,12 @@
use crate::{Lazy, app, trace};
use crate::config::SETTINGS;
use crate::html::*;
use crate::core::response::page::*;
use crate::api::action::{action_ref, run_actions};
use crate::api::component::*;
use std::sync::RwLock;
use std::collections::HashMap;
static COMPONENTS: Lazy<RwLock<HashMap<&str, PageContainer>>> = Lazy::new(|| {
RwLock::new(HashMap::new())
});
use super::action::PageAction;
static DEFAULT_LANGUAGE: Lazy<Option<String>> = Lazy::new(|| {
let language = SETTINGS.app.language[..2].to_lowercase();
@ -65,7 +63,7 @@ impl<'a> Page<'a> {
title : OptAttr::new(),
description : OptAttr::new(),
assets : PageAssets::new(),
regions : COMPONENTS.read().unwrap().clone(),
regions : common_components(),
body_classes: Classes::new_with_default("body"),
template : "default".to_owned(),
}
@ -153,6 +151,12 @@ impl<'a> Page<'a> {
// Page RENDER.
pub fn render(&mut self) -> app::Result<Markup> {
// Acciones de los módulos antes de renderizar el tema.
run_actions(
"",
|a| action_ref::<PageAction>(&**a).before_render_page(self)
);
// Acciones del tema antes de renderizar la página.
self.assets.theme().before_render_page(self);
@ -186,26 +190,3 @@ impl<'a> Page<'a> {
self
}
}
pub fn render_component(component: &mut dyn ComponentTrait, assets: &mut PageAssets) -> Markup {
component.before_render(assets);
assets.theme().before_render_component(component, assets);
match component.is_renderable() {
true => {
match assets.theme().render_component(component, assets) {
Some(html) => html,
None => component.default_render(assets)
}
},
false => html! {}
}
}
pub fn add_component_to(region: &'static str, component: impl ComponentTrait) {
let mut hmap = COMPONENTS.write().unwrap();
if let Some(regions) = hmap.get_mut(region) {
regions.add(component);
} else {
hmap.insert(region, PageContainer::new_with(component));
}
}