Modifica la estructura de preparación de una app

This commit is contained in:
Manuel Cillero 2022-05-08 11:10:18 +02:00
parent af9afca777
commit fcc022d164
42 changed files with 295 additions and 228 deletions

View file

@ -1,12 +1,18 @@
use pagetop::prelude::*;
use pagetop::{prelude::*, core::app::AppTrait};
fn bootstrap() {
include_module(&pagetop_admin::Admin);
include_module(&pagetop_user::User);
include_module(&pagetop_node::Node);
struct Drust;
impl AppTrait for Drust {
fn enabled_modules(&self) -> Vec<&'static dyn ModuleTrait> {
vec![
&pagetop_admin::Admin,
&pagetop_user::User,
&pagetop_node::Node,
]
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
Application::prepare(UsingBootstrap::Fn(bootstrap)).await?.run()?.await
Application::prepare(Drust).await?.run()?.await
}

View file

@ -28,9 +28,9 @@ impl ModuleTrait for Admin {
);
}
fn actions(&self) -> Vec<ActionItem> {
fn actions(&self) -> Vec<HookItem> {
vec![
action_item!(ActionBeforeRenderPage => before_render_page)
hook_item!(BeforeRenderPageHook => before_render_page)
]
}
}

View file

@ -26,9 +26,9 @@ impl ModuleTrait for Node {
cfg.route("/node", app::web::get().to(node));
}
fn actions(&self) -> Vec<ActionItem> {
fn actions(&self) -> Vec<HookItem> {
vec![
action_item!(ActionBeforeRenderPage => before_render_page, -1)
hook_item!(BeforeRenderPageHook => before_render_page, -1)
]
}

View file

@ -1,17 +0,0 @@
pub use std::any::Any as AnyAction;
pub trait ActionTrait: AnyAction + Send + Sync {
fn new() -> Self where Self: Sized;
fn handler(&self) -> &'static str;
fn weight(&self) -> isize {
0
}
fn as_ref_any(&self) -> &dyn AnyAction;
}
pub fn action_ref<A: 'static>(action: &dyn ActionTrait) -> &A {
action.as_ref_any().downcast_ref::<A>().unwrap()
}

View file

@ -1,14 +0,0 @@
mod definition;
pub use definition::{
ActionTrait,
AnyAction,
action_ref,
};
mod holder;
pub use holder::ActionItem;
use holder::ActionsHolder;
mod all;
pub use all::run_actions;
pub(crate) use all::add_action;

View file

@ -1,48 +0,0 @@
use crate::api::action::{ActionTrait, AnyAction};
use super::{Assets, ComponentTrait};
pub const BEFORE_RENDER_COMPONENT_ACTION: &str = "pagetop::action::before_render_component";
pub struct BeforeRenderComponentAction {
action: Option<fn(&mut dyn ComponentTrait, &mut Assets)>,
weight: isize,
}
impl ActionTrait for BeforeRenderComponentAction {
fn new() -> Self {
BeforeRenderComponentAction {
action: None,
weight: 0,
}
}
fn handler(&self) -> &'static str {
BEFORE_RENDER_COMPONENT_ACTION
}
fn weight(&self) -> isize {
self.weight
}
fn as_ref_any(&self) -> &dyn AnyAction {
self
}
}
impl BeforeRenderComponentAction {
pub fn with_action(mut self, action: fn(&mut dyn ComponentTrait, &mut Assets)) -> Self {
self.action = Some(action);
self
}
pub fn with_weight(mut self, weight: isize) -> Self {
self.weight = weight;
self
}
pub fn run(&self, component: &mut dyn ComponentTrait, assets: &mut Assets) {
if let Some(action) = self.action {
action(component, assets)
}
}
}

View file

@ -95,7 +95,7 @@ impl Form {
self
}
pub fn with_action(mut self, action: &str) -> Self {
pub fn with_hook(mut self, action: &str) -> Self {
self.alter_action(action);
self
}

View file

@ -1,57 +1,63 @@
use crate::{Lazy, app, base, trace};
use crate::{Lazy, base, trace};
use crate::config::SETTINGS;
use crate::api::{module, theme};
use crate::core::{module, theme};
use super::AppTrait;
use std::io::Error;
use actix_web::middleware::normalize::{NormalizePath, TrailingSlash};
pub struct Application {
server: app::Server,
server: super::Server,
}
pub enum UsingBootstrap {Fn(fn()), No}
impl Application {
pub async fn prepare(bootstrap: UsingBootstrap) -> Result<Self, Error> {
pub async fn prepare(brrrz: impl AppTrait) -> Result<Self, Error> {
// Rótulo de presentación.
app::banner::print_on_startup();
super::banner::print_on_startup();
// Inicia registro de trazas y eventos.
Lazy::force(&app::tracing::TRACING);
Lazy::force(&super::tracing::TRACING);
// Valida el identificador de idioma.
Lazy::force(&app::locale::LANGID);
Lazy::force(&super::locale::LANGID);
// Conecta con la base de datos (opcional).
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
Lazy::force(&app::db::DBCONN);
Lazy::force(&super::db::DBCONN);
// Registra los temas predeterminados.
theme::register_theme(&base::theme::aliner::Aliner);
theme::register_theme(&base::theme::minimal::Minimal);
theme::register_theme(&base::theme::bootsier::Bootsier);
theme::register_theme(&base::theme::bulmix::Bulmix);
theme::register_themes(vec![
&base::theme::aliner::Aliner,
&base::theme::minimal::Minimal,
&base::theme::bootsier::Bootsier,
&base::theme::bulmix::Bulmix,
]);
theme::register_themes(brrrz.register_themes());
// Habilita los módulos predeterminados.
module::enable_modules(brrrz.enabled_modules());
// Habilita el módulo de presentación de PageTop.
// Normalmente se sobrecargará en la función de inicio.
module::enable_module(&base::module::demopage::Demopage);
// Registra las acciones de todos los módulos.
module::all::register_hooks();
// Ejecuta la función de inicio de la aplicación.
trace::info!("Calling application bootstrap");
brrrz.bootstrap();
/*
if let UsingBootstrap::Fn(bootstrap) = bootstrap {
let _ = &bootstrap();
}
// Registra el módulo de presentación de PageTop.
// Normalmente se sobrecargará en la función de inicio.
module::include_module(&base::module::demopage::Demopage);
// Registra las acciones de todos los módulos.
module::all::register_actions();
}*/
// Actualizaciones pendientes de la base de datos (opcional).
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
module::all::run_migrations();
// Prepara el servidor web.
let server = app::HttpServer::new(move || {
app::App::new()
let server = super::HttpServer::new(move || {
super::App::new()
.wrap(tracing_actix_web::TracingLogger)
.wrap(NormalizePath::new(TrailingSlash::Trim))
.configure(&module::all::modules)
@ -66,7 +72,7 @@ impl Application {
Ok(Self { server })
}
pub fn run(self) -> Result<app::Server, Error> {
pub fn run(self) -> Result<super::Server, Error> {
Ok(self.server)
}
}

View file

@ -0,0 +1,19 @@
use crate::core::module::ModuleTrait;
use crate::core::theme::ThemeTrait;
pub trait AppTrait: Send + Sync {
fn bootstrap(&self) {
}
fn enabled_modules(&self) -> Vec<&'static dyn ModuleTrait> {
vec![]
}
fn disabled_modules(&self) -> Vec<&'static dyn ModuleTrait> {
vec![]
}
fn register_themes(&self) -> Vec<&'static dyn ThemeTrait> {
vec![]
}
}

View file

@ -12,4 +12,7 @@ pub mod locale;
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
pub mod db;
mod definition;
pub use definition::AppTrait;
pub mod application;

View file

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

View file

@ -1,7 +1,7 @@
use crate::util;
use crate::html::{Markup, html};
use crate::api::action::{action_ref, run_actions};
use super::{BEFORE_RENDER_COMPONENT_ACTION, BeforeRenderComponentAction};
use crate::core::hook::{hook_ref, run_hooks};
use super::{BEFORE_RENDER_COMPONENT_HOOK, BeforeRenderComponentHook};
use super::Assets;
pub use std::any::Any as AnyComponent;
@ -54,9 +54,9 @@ pub fn render_component(component: &mut dyn ComponentTrait, assets: &mut Assets)
component.before_render(assets);
// Acciones de los módulos antes de renderizar el componente.
run_actions(
BEFORE_RENDER_COMPONENT_ACTION,
|a| action_ref::<BeforeRenderComponentAction>(&**a).run(component, assets)
run_hooks(
BEFORE_RENDER_COMPONENT_HOOK,
|a| hook_ref::<BeforeRenderComponentHook>(&**a).run(component, assets)
);
// Acciones del tema antes de renderizar el componente.

View file

@ -0,0 +1,48 @@
use crate::core::hook::{HookTrait, AnyHook};
use super::{Assets, ComponentTrait};
pub const BEFORE_RENDER_COMPONENT_HOOK: &str = "pagetop::action::before_render_component";
pub struct BeforeRenderComponentHook {
hook: Option<fn(&mut dyn ComponentTrait, &mut Assets)>,
weight: isize,
}
impl HookTrait for BeforeRenderComponentHook {
fn new() -> Self {
BeforeRenderComponentHook {
hook: None,
weight: 0,
}
}
fn handler(&self) -> &'static str {
BEFORE_RENDER_COMPONENT_HOOK
}
fn weight(&self) -> isize {
self.weight
}
fn as_ref_any(&self) -> &dyn AnyHook {
self
}
}
impl BeforeRenderComponentHook {
pub fn with_hook(mut self, hook: fn(&mut dyn ComponentTrait, &mut Assets)) -> Self {
self.hook = Some(hook);
self
}
pub fn with_weight(mut self, weight: isize) -> Self {
self.weight = weight;
self
}
pub fn run(&self, component: &mut dyn ComponentTrait, assets: &mut Assets) {
if let Some(hook) = self.hook {
hook(component, assets)
}
}
}

View file

@ -1,7 +1,7 @@
mod action;
pub use action::{
BEFORE_RENDER_COMPONENT_ACTION,
BeforeRenderComponentAction,
mod hook;
pub use hook::{
BEFORE_RENDER_COMPONENT_HOOK,
BeforeRenderComponentHook,
};
mod assets;

View file

@ -1,25 +1,25 @@
use crate::Lazy;
use super::{ActionItem, ActionsHolder};
use super::{HookItem, HooksHolder};
use std::sync::RwLock;
use std::collections::HashMap;
// Registered actions.
static ACTIONS: Lazy<RwLock<HashMap<&str, ActionsHolder>>> = Lazy::new(|| {
static ACTIONS: Lazy<RwLock<HashMap<&str, HooksHolder>>> = Lazy::new(|| {
RwLock::new(HashMap::new())
});
pub fn add_action(action: ActionItem) {
pub fn add_hook(action: HookItem) {
let mut hmap = ACTIONS.write().unwrap();
let action_handler = action.handler();
if let Some(actions) = hmap.get_mut(action_handler) {
actions.add(action);
} else {
hmap.insert(action_handler, ActionsHolder::new_with(action));
hmap.insert(action_handler, HooksHolder::new_with(action));
}
}
pub fn run_actions<B, F>(action_handler: &str, f: F) where F: FnMut(&ActionItem) -> B {
pub fn run_hooks<B, F>(action_handler: &str, f: F) where F: FnMut(&HookItem) -> B {
if let Some(actions) = ACTIONS.read().unwrap().get(action_handler) {
actions.iter_map(f)
}

View file

@ -0,0 +1,17 @@
pub use std::any::Any as AnyHook;
pub trait HookTrait: AnyHook + Send + Sync {
fn new() -> Self where Self: Sized;
fn handler(&self) -> &'static str;
fn weight(&self) -> isize {
0
}
fn as_ref_any(&self) -> &dyn AnyHook;
}
pub fn hook_ref<A: 'static>(action: &dyn HookTrait) -> &A {
action.as_ref_any().downcast_ref::<A>().unwrap()
}

View file

@ -1,36 +1,36 @@
use super::ActionTrait;
use super::HookTrait;
use std::sync::{Arc, RwLock};
pub type ActionItem = Box<dyn ActionTrait>;
pub type HookItem = Box<dyn HookTrait>;
#[macro_export]
macro_rules! action_item {
macro_rules! hook_item {
( $action:ident => $f:ident $(, $weight:expr)? ) => {{
Box::new($action::new().with_action($f)$(.with_weight($weight))?)
Box::new($action::new().with_hook($f)$(.with_weight($weight))?)
}};
}
pub struct ActionsHolder(Arc<RwLock<Vec<ActionItem>>>);
pub struct HooksHolder(Arc<RwLock<Vec<HookItem>>>);
impl ActionsHolder {
impl HooksHolder {
pub fn new() -> Self {
ActionsHolder(Arc::new(RwLock::new(Vec::new())))
HooksHolder(Arc::new(RwLock::new(Vec::new())))
}
pub fn new_with(action: ActionItem) -> Self {
let mut container = ActionsHolder::new();
pub fn new_with(action: HookItem) -> Self {
let mut container = HooksHolder::new();
container.add(action);
container
}
pub fn add(&mut self, action: ActionItem) {
pub fn add(&mut self, action: HookItem) {
let mut actions = self.0.write().unwrap();
actions.push(action);
actions.sort_by_key(|a| a.weight());
}
pub fn iter_map<B, F>(&self, f: F) where Self: Sized, F: FnMut(&ActionItem) -> B {
pub fn iter_map<B, F>(&self, f: F) where Self: Sized, F: FnMut(&HookItem) -> B {
let _: Vec<_> = self.0.read().unwrap().iter().map(f).collect();
}
}

View file

@ -0,0 +1,14 @@
mod definition;
pub use definition::{
HookTrait,
AnyHook,
hook_ref,
};
mod holder;
pub use holder::HookItem;
use holder::HooksHolder;
mod all;
pub use all::run_hooks;
pub(crate) use all::add_hook;

View file

@ -1,4 +1,5 @@
pub mod action; // API to define functions that alter the behavior of PageTop core.
pub mod app; // API to initialize the PageTop base application.
pub mod component; // API to build new components.
pub mod hook; // API to define functions that alter the behavior of PageTop core.
pub mod module; // API to add new features with modules.
pub mod theme; // API to create themes.

View file

@ -1,26 +1,38 @@
use crate::{Lazy, app, run_now, trace};
use crate::api::action::add_action;
use crate::{Lazy, run_now, trace};
use crate::core::app;
use crate::core::hook::add_hook;
use crate::db::*;
use super::ModuleTrait;
use std::sync::RwLock;
// Módulos registrados.
static MODULES: Lazy<RwLock<Vec<&dyn ModuleTrait>>> = Lazy::new(|| {
// Enabled modules.
static ENABLED_MODULES: Lazy<RwLock<Vec<&dyn ModuleTrait>>> = Lazy::new(|| {
RwLock::new(Vec::new())
});
pub fn include_module(module: &'static dyn ModuleTrait) {
/* Disabled modules.
static DISABLED_MODULES: Lazy<RwLock<Vec<&dyn ModuleTrait>>> = Lazy::new(|| {
RwLock::new(Vec::new())
}); */
pub fn enable_modules(modules: Vec<&'static dyn ModuleTrait>) {
for m in modules {
enable_module(m)
}
}
pub fn enable_module(module: &'static dyn ModuleTrait) {
let mut list: Vec<&dyn ModuleTrait> = Vec::new();
add_to(&mut list, module);
list.reverse();
MODULES.write().unwrap().append(&mut list);
ENABLED_MODULES.write().unwrap().append(&mut list);
}
fn add_to(list: &mut Vec<&dyn ModuleTrait>, module: &'static dyn ModuleTrait) {
if !MODULES.read().unwrap().iter().any(|m| m.handler() == module.handler()) {
if !ENABLED_MODULES.read().unwrap().iter().any(|m| m.handler() == module.handler()) {
if !list.iter().any(|m| m.handler() == module.handler()) {
trace::debug!("Including module \"{}\"", module.single_name());
trace::debug!("Enabling module \"{}\"", module.single_name());
list.push(module);
let mut dependencies = module.dependencies();
@ -32,16 +44,20 @@ fn add_to(list: &mut Vec<&dyn ModuleTrait>, module: &'static dyn ModuleTrait) {
}
}
#[allow(unused_variables)]
pub fn disable_module(module: &'static dyn ModuleTrait) {
}
pub fn modules(cfg: &mut app::web::ServiceConfig) {
for m in MODULES.read().unwrap().iter() {
for m in ENABLED_MODULES.read().unwrap().iter() {
m.configure_service(cfg);
}
}
pub fn register_actions() {
for m in MODULES.read().unwrap().iter() {
pub fn register_hooks() {
for m in ENABLED_MODULES.read().unwrap().iter() {
for a in m.actions().into_iter() {
add_action(a);
add_hook(a);
}
}
}
@ -53,7 +69,7 @@ pub fn run_migrations() {
impl MigratorTrait for Migrator {
fn migrations() -> Vec<MigrationItem> {
let mut migrations = vec![];
for m in MODULES.read().unwrap().iter() {
for m in ENABLED_MODULES.read().unwrap().iter() {
migrations.append(&mut m.migrations());
}
migrations

View file

@ -1,5 +1,6 @@
use crate::{app, util};
use crate::api::action::ActionItem;
use crate::util;
use crate::core::app;
use crate::core::hook::HookItem;
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
use crate::db::MigrationItem;
@ -28,7 +29,7 @@ pub trait ModuleTrait: BaseModule + Send + Sync {
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {
}
fn actions(&self) -> Vec<ActionItem> {
fn actions(&self) -> Vec<HookItem> {
vec![]
}

View file

@ -6,5 +6,7 @@ pub use definition::{
pub(crate) mod all;
pub use all::{
include_module,
disable_module,
enable_module,
enable_modules,
};

View file

@ -1,4 +1,5 @@
use crate::{Lazy, app, theme_static_files, trace};
use crate::{Lazy, theme_static_files, trace};
use crate::core::app;
use super::ThemeTrait;
use std::sync::RwLock;
@ -10,6 +11,12 @@ static THEMES: Lazy<RwLock<Vec<&dyn ThemeTrait>>> = Lazy::new(|| {
RwLock::new(Vec::new())
});
pub fn register_themes(themes: Vec<&'static dyn ThemeTrait>) {
for t in themes {
register_theme(t)
}
}
pub fn register_theme(theme: &'static dyn ThemeTrait) {
let mut themes = THEMES.write().unwrap();
if !themes.iter().any(|t| t.handler() == theme.handler()) {

View file

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

View file

@ -7,5 +7,6 @@ pub use definition::{
pub(crate) mod all;
pub use all::{
register_theme,
register_themes,
theme_by_single_name,
};

View file

@ -18,10 +18,10 @@ 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 api; // Main APIs for actions, components, modules and themes.
pub mod core; // 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 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

@ -7,7 +7,7 @@ pub use fluent_templates::fluent_bundle::FluentValue;
macro_rules! localize {
( $dir_locales:literal $(, $core_locales:literal)? ) => {
use $crate::locale::*;
use $crate::app::locale::LANGID;
use $crate::core::app::locale::LANGID;
static_locale! {
static LOCALES = {

View file

@ -20,17 +20,20 @@ pub use crate::{
migration_item,
};
pub use crate::{action_item, api::{
action::*,
pub use crate::{hook_item, core::{
// app::*,
component::*,
hook::*,
module::*,
theme::*,
}};
pub use crate::core::app;
pub use crate::core::app::application::Application;
pub use crate::response::page::*;
pub use crate::app;
pub use crate::app::application::{Application, UsingBootstrap};
//pub use crate::app;
//pub use crate::app::application::{Application, UsingBootstrap};
pub use crate::base::component::*;

View file

@ -1,48 +0,0 @@
use crate::api::action::{ActionTrait, AnyAction};
use super::Page;
pub const BEFORE_RENDER_PAGE_ACTION: &str = "pagetop::action::before_render_page";
pub struct ActionBeforeRenderPage {
action: Option<fn(&mut Page)>,
weight: isize,
}
impl ActionTrait for ActionBeforeRenderPage {
fn new() -> Self {
ActionBeforeRenderPage {
action: None,
weight: 0,
}
}
fn handler(&self) -> &'static str {
BEFORE_RENDER_PAGE_ACTION
}
fn weight(&self) -> isize {
self.weight
}
fn as_ref_any(&self) -> &dyn AnyAction {
self
}
}
impl ActionBeforeRenderPage {
pub fn with_action(mut self, action: fn(&mut Page)) -> Self {
self.action = Some(action);
self
}
pub fn with_weight(mut self, weight: isize) -> Self {
self.weight = weight;
self
}
pub fn run(&self, page: &mut Page) {
if let Some(action) = self.action {
action(page)
}
}
}

View file

@ -0,0 +1,48 @@
use crate::core::hook::{HookTrait, AnyHook};
use super::Page;
pub const BEFORE_RENDER_PAGE_HOOK: &str = "pagetop::action::before_render_page";
pub struct BeforeRenderPageHook {
hook: Option<fn(&mut Page)>,
weight: isize,
}
impl HookTrait for BeforeRenderPageHook {
fn new() -> Self {
BeforeRenderPageHook {
hook: None,
weight: 0,
}
}
fn handler(&self) -> &'static str {
BEFORE_RENDER_PAGE_HOOK
}
fn weight(&self) -> isize {
self.weight
}
fn as_ref_any(&self) -> &dyn AnyHook {
self
}
}
impl BeforeRenderPageHook {
pub fn with_hook(mut self, hook: fn(&mut Page)) -> Self {
self.hook = Some(hook);
self
}
pub fn with_weight(mut self, weight: isize) -> Self {
self.weight = weight;
self
}
pub fn run(&self, page: &mut Page) {
if let Some(hook) = self.hook {
hook(page)
}
}
}

View file

@ -1,7 +1,7 @@
mod action;
pub use action::{
BEFORE_RENDER_PAGE_ACTION,
ActionBeforeRenderPage,
mod hook;
pub use hook::{
BEFORE_RENDER_PAGE_HOOK,
BeforeRenderPageHook,
};
mod page;

View file

@ -1,9 +1,10 @@
use crate::{Lazy, app, trace};
use crate::{Lazy, trace};
use crate::config::SETTINGS;
use crate::html::*;
use crate::api::action::{action_ref, run_actions};
use crate::api::component::*;
use super::{BEFORE_RENDER_PAGE_ACTION, ActionBeforeRenderPage};
use crate::core::app;
use crate::core::hook::{hook_ref, run_hooks};
use crate::core::component::*;
use super::{BEFORE_RENDER_PAGE_HOOK, BeforeRenderPageHook};
use std::collections::HashMap;
@ -151,9 +152,9 @@ impl<'a> Page<'a> {
pub fn render(&mut self) -> app::Result<Markup> {
// Acciones de los módulos antes de renderizar la página.
run_actions(
BEFORE_RENDER_PAGE_ACTION,
|a| action_ref::<ActionBeforeRenderPage>(&**a).run(self)
run_hooks(
BEFORE_RENDER_PAGE_HOOK,
|a| hook_ref::<BeforeRenderPageHook>(&**a).run(self)
);
// Acciones del tema antes de renderizar la página.