Modifica las extensiones por acciones registradas
This commit is contained in:
parent
9eede3321a
commit
d0e566aede
26 changed files with 309 additions and 100 deletions
27
pagetop/src/api/action/all.rs
Normal file
27
pagetop/src/api/action/all.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
30
pagetop/src/api/action/holder.rs
Normal file
30
pagetop/src/api/action/holder.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
19
pagetop/src/api/action/mod.rs
Normal file
19
pagetop/src/api/action/mod.rs
Normal 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,
|
||||
};
|
||||
55
pagetop/src/api/component/action.rs
Normal file
55
pagetop/src/api/component/action.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
22
pagetop/src/api/component/all.rs
Normal file
22
pagetop/src/api/component/all.rs
Normal 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()
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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};
|
||||
|
||||
|
|
@ -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! {}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
4
pagetop/src/api/mod.rs
Normal 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.
|
||||
|
|
@ -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({
|
||||
|
|
@ -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>> {
|
||||
10
pagetop/src/api/module/mod.rs
Normal file
10
pagetop/src/api/module/mod.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
mod definition;
|
||||
pub use definition::{
|
||||
BaseModule,
|
||||
ModuleTrait,
|
||||
};
|
||||
|
||||
pub(crate) mod all;
|
||||
pub use all::{
|
||||
register_module,
|
||||
};
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
63
pagetop/src/response/page/action.rs
Normal file
63
pagetop/src/response/page/action.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
4
pagetop/src/response/page/mod.rs
Normal file
4
pagetop/src/response/page/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod action;
|
||||
|
||||
mod page;
|
||||
pub use page::Page;
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue