Mejora y simplifica la gestión global de acciones

This commit is contained in:
Manuel Cillero 2022-05-05 21:43:00 +02:00
parent 7c8f51ba86
commit 67ddb8d899
29 changed files with 184 additions and 206 deletions

View file

@ -8,5 +8,5 @@ fn bootstrap() {
#[actix_web::main]
async fn main() -> std::io::Result<()> {
Application::prepare(bootstrap).await?.run()?.await
Application::prepare(UsingBootstrap::Fn(bootstrap)).await?.run()?.await
}

View file

@ -21,4 +21,14 @@ impl ModuleTrait for Admin {
.route("", app::web::get().to(summary::summary))
);
}
fn actions(&self) -> Vec<ActionItem> {
vec![
action_item!(ActionBeforeRenderPage => before_render_page)
]
}
}
fn before_render_page(page: &mut Page) {
page.alter_body_classes("test-admin", ClassesOp::Add);
}

View file

@ -20,12 +20,18 @@ impl ModuleTrait for Node {
cfg.route("/node", app::web::get().to(node));
}
fn migrations(&self) -> Vec<Box<dyn db::MigrationTrait>> {
fn actions(&self) -> Vec<ActionItem> {
vec![
boxed_migration!(m20220316_000001_create_table_node_type),
boxed_migration!(m20220316_000002_create_table_node),
boxed_migration!(m20220316_000003_create_table_node_access),
boxed_migration!(m20220316_000004_create_table_node_revision),
action_item!(ActionBeforeRenderPage => before_render_page, -1)
]
}
fn migrations(&self) -> Vec<MigrationItem> {
vec![
migration_item!(m20220316_000001_create_table_node_type),
migration_item!(m20220316_000002_create_table_node),
migration_item!(m20220316_000003_create_table_node_access),
migration_item!(m20220316_000004_create_table_node_revision),
]
}
}
@ -37,3 +43,7 @@ async fn node() -> app::Result<Markup> {
)
.render()
}
fn before_render_page(page: &mut Page) {
page.alter_body_classes("test-node", ClassesOp::Add);
}

View file

@ -19,7 +19,7 @@ enum NodeType {
// different from the current type name if the locked field is 0.
}
pub struct Migration;
pub_migration!(Migration);
#[async_trait::async_trait]
impl MigrationTrait for Migration {
@ -82,9 +82,3 @@ impl MigrationTrait for Migration {
.await
}
}
impl MigrationName for Migration {
fn name(&self) -> &str {
module_name!()
}
}

View file

@ -26,7 +26,7 @@ enum Node {
Translate, // A boolean indicating whether this translation page needs to be updated.
}
pub struct Migration;
pub_migration!(Migration);
#[async_trait::async_trait]
impl MigrationTrait for Migration {
@ -105,9 +105,3 @@ impl MigrationTrait for Migration {
.await
}
}
impl MigrationName for Migration {
fn name(&self) -> &str {
module_name!()
}
}

View file

@ -18,7 +18,7 @@ enum NodeAccess {
// this node.
}
pub struct Migration;
pub_migration!(Migration);
#[async_trait::async_trait]
impl MigrationTrait for Migration {
@ -65,9 +65,3 @@ impl MigrationTrait for Migration {
.await
}
}
impl MigrationName for Migration {
fn name(&self) -> &str {
module_name!()
}
}

View file

@ -20,7 +20,7 @@ enum NodeRevision {
// be displayed at the top of lists in which it appears.
}
pub struct Migration;
pub_migration!(Migration);
#[async_trait::async_trait]
impl MigrationTrait for Migration {
@ -83,9 +83,3 @@ impl MigrationTrait for Migration {
.await
}
}
impl MigrationName for Migration {
fn name(&self) -> &str {
module_name!()
}
}

View file

@ -19,12 +19,12 @@ impl ModuleTrait for User {
cfg.route("/user/login", app::web::get().to(login));
}
fn migrations(&self) -> Vec<Box<dyn db::MigrationTrait>> {
fn migrations(&self) -> Vec<MigrationItem> {
vec![
boxed_migration!(m20220312_000001_create_table_role),
boxed_migration!(m20220312_000002_create_table_role_permission),
boxed_migration!(m20220312_000003_create_table_user),
boxed_migration!(m20220312_000004_create_table_user_role),
migration_item!(m20220312_000001_create_table_role),
migration_item!(m20220312_000002_create_table_role_permission),
migration_item!(m20220312_000003_create_table_user),
migration_item!(m20220312_000004_create_table_user_role),
]
}
}

View file

@ -9,7 +9,7 @@ enum Role {
Weight, // The weight of this role in listings and the user interface.
}
pub struct Migration;
pub_migration!(Migration);
#[async_trait::async_trait]
impl MigrationTrait for Migration {
@ -62,9 +62,3 @@ impl MigrationTrait for Migration {
.await
}
}
impl MigrationName for Migration {
fn name(&self) -> &str {
module_name!()
}
}

View file

@ -11,7 +11,7 @@ enum RolePermission {
#[derive(Iden)]
enum Role { Table, Rid, /* ... */ }
pub struct Migration;
pub_migration!(Migration);
#[async_trait::async_trait]
impl MigrationTrait for Migration {
@ -56,9 +56,3 @@ impl MigrationTrait for Migration {
.await
}
}
impl MigrationName for Migration {
fn name(&self) -> &str {
module_name!()
}
}

View file

@ -16,7 +16,7 @@ enum User {
Timezone, // User's time zone.
}
pub struct Migration;
pub_migration!(Migration);
#[async_trait::async_trait]
impl MigrationTrait for Migration {
@ -77,9 +77,3 @@ impl MigrationTrait for Migration {
.await
}
}
impl MigrationName for Migration {
fn name(&self) -> &str {
module_name!()
}
}

View file

@ -14,7 +14,7 @@ enum User { Table, Uid, /* ... */ }
#[derive(Iden)]
enum Role { Table, Rid, /* ... */ }
pub struct Migration;
pub_migration!(Migration);
#[async_trait::async_trait]
impl MigrationTrait for Migration {
@ -62,9 +62,3 @@ impl MigrationTrait for Migration {
.await
}
}
impl MigrationName for Migration {
fn name(&self) -> &str {
module_name!()
}
}

View file

@ -1,5 +1,5 @@
use crate::Lazy;
use super::{ActionItem, ActionsHolder, ActionTrait};
use super::{ActionItem, ActionsHolder};
use std::sync::RwLock;
use std::collections::HashMap;
@ -9,9 +9,9 @@ static ACTIONS: Lazy<RwLock<HashMap<&str, ActionsHolder>>> = Lazy::new(|| {
RwLock::new(HashMap::new())
});
pub fn register_action(action: impl ActionTrait) {
pub fn add_action(action: ActionItem) {
let mut hmap = ACTIONS.write().unwrap();
let action_name = action.type_name();
let action_name = action.machine_name();
if let Some(actions) = hmap.get_mut(action_name) {
actions.add(action);
} else {
@ -19,9 +19,11 @@ pub fn register_action(action: impl ActionTrait) {
}
}
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) {
pub fn run_actions<B, F>(machine_name: &'static str, f: F)
where
F: FnMut(&ActionItem) -> B
{
if let Some(actions) = ACTIONS.read().unwrap().get(machine_name) {
actions.iter_map(f)
}
}

View file

@ -1,18 +1,12 @@
use crate::util;
pub use std::any::Any as AnyAction;
pub trait BaseAction {
fn type_name(&self) -> &'static str;
fn single_name(&self) -> &'static str;
fn qualified_name(&self, last: usize) -> &'static str;
}
pub trait ActionTrait: AnyAction + BaseAction + Send + Sync {
pub trait ActionTrait: AnyAction + Send + Sync {
fn new() -> Self where Self: Sized;
fn machine_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
fn weight(&self) -> isize {
0
}
@ -20,20 +14,6 @@ pub trait ActionTrait: AnyAction + BaseAction + Send + Sync {
fn as_ref_any(&self) -> &dyn AnyAction;
}
impl<C: ?Sized + ActionTrait> BaseAction for C {
fn type_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
fn single_name(&self) -> &'static str {
util::partial_type_name(std::any::type_name::<Self>(), 1)
}
fn qualified_name(&self, last: usize) -> &'static str {
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

@ -4,7 +4,13 @@ use std::sync::{Arc, RwLock};
pub type ActionItem = Box<dyn ActionTrait>;
#[derive(Clone)]
#[macro_export]
macro_rules! action_item {
( $action:ident => $f:ident $(, $weight:expr)? ) => {{
Box::new($action::new().with_action($f)$(.with_weight($weight))?)
}};
}
pub struct ActionsHolder(Arc<RwLock<Vec<ActionItem>>>);
impl ActionsHolder {
@ -12,19 +18,23 @@ impl ActionsHolder {
ActionsHolder(Arc::new(RwLock::new(Vec::new())))
}
pub fn new_with(action: impl ActionTrait) -> Self {
pub fn new_with(action: ActionItem) -> Self {
let mut container = ActionsHolder::new();
container.add(action);
container
}
pub fn add(&mut self, action: impl ActionTrait) {
pub fn add(&mut self, action: ActionItem) {
let mut actions = self.0.write().unwrap();
actions.push(Box::new(action));
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 {
let _ = self.0.read().unwrap().iter().map(f);
pub fn iter_map<B, F>(&self, f: F)
where
Self: Sized,
F: FnMut(&ActionItem) -> B,
{
let _: Vec<_> = self.0.read().unwrap().iter().map(f).collect();
}
}

View file

@ -2,18 +2,21 @@ mod definition;
pub use definition::{
ActionTrait,
AnyAction,
BaseAction,
action_ref,
};
mod holder;
pub use holder::{
ActionItem,
};
pub(crate) use holder::{
ActionsHolder,
};
mod all;
pub use all::{
register_action,
run_actions,
};
pub(crate) use all::{
add_action,
};

View file

@ -1,24 +1,25 @@
use crate::api::action::{ActionTrait, AnyAction};
use super::{ComponentTrait, PageAssets};
pub enum TypeAction {
BeforeRenderComponent(fn(&mut dyn ComponentTrait, &mut PageAssets)),
None,
}
pub const ACTION_BEFORE_RENDER_COMPONENT: &str = "pagetop::render::before_render_component";
pub struct ComponentAction {
action: TypeAction,
pub struct ActionBeforeRenderComponent {
action: Option<fn(&mut dyn ComponentTrait, &mut PageAssets)>,
weight: isize,
}
impl ActionTrait for ComponentAction {
impl ActionTrait for ActionBeforeRenderComponent {
fn new() -> Self {
ComponentAction {
action: TypeAction::None,
ActionBeforeRenderComponent {
action: None,
weight: 0,
}
}
fn machine_name(&self) -> &'static str {
ACTION_BEFORE_RENDER_COMPONENT
}
fn weight(&self) -> isize {
self.weight
}
@ -28,28 +29,20 @@ impl ActionTrait for ComponentAction {
}
}
impl ComponentAction {
pub fn new(action: TypeAction) -> Self {
ComponentAction {
action,
weight: 0,
}
impl ActionBeforeRenderComponent {
pub fn with_action(mut self, action: fn(&mut dyn ComponentTrait, &mut PageAssets)) -> Self {
self.action = Some(action);
self
}
pub fn new_with_weight(action: TypeAction, weight: isize) -> Self {
ComponentAction {
action,
weight,
}
pub fn with_weight(mut self, weight: isize) -> Self {
self.weight = weight;
self
}
pub fn before_render_component(
&self,
component: &mut dyn ComponentTrait,
assets: &mut PageAssets)
{
if let TypeAction::BeforeRenderComponent(f) = self.action {
f(component, assets)
pub fn run(&self, component: &mut dyn ComponentTrait, assets: &mut PageAssets) {
if let Some(action) = self.action {
action(component, assets)
}
}
}

View file

@ -2,7 +2,7 @@ use crate::html::{Markup, html};
use crate::util;
use crate::api::action::{action_ref, run_actions};
use super::PageAssets;
use super::action::ComponentAction;
use super::{ACTION_BEFORE_RENDER_COMPONENT, ActionBeforeRenderComponent};
pub use std::any::Any as AnyComponent;
@ -76,8 +76,8 @@ pub fn render_component(component: &mut dyn ComponentTrait, assets: &mut PageAss
// Acciones de los módulos antes de renderizar el componente.
run_actions(
"",
|a| action_ref::<ComponentAction>(&**a).before_render_component(component, assets)
ACTION_BEFORE_RENDER_COMPONENT,
|a| action_ref::<ActionBeforeRenderComponent>(&**a).run(component, assets)
);
// Acciones del tema antes de renderizar el componente.

View file

@ -1,4 +1,8 @@
pub mod action;
mod action;
pub use action::{
ACTION_BEFORE_RENDER_COMPONENT,
ActionBeforeRenderComponent,
};
mod assets;
pub use assets::{

View file

@ -1,4 +1,5 @@
use crate::{Lazy, app, run_now, trace};
use crate::api::action::add_action;
use crate::db::*;
use super::ModuleTrait;
@ -37,12 +38,20 @@ pub fn modules(cfg: &mut app::web::ServiceConfig) {
}
}
pub fn register_actions() {
for m in MODULES.read().unwrap().iter() {
for a in m.actions().into_iter() {
add_action(a);
}
}
}
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
pub fn migrations() {
pub fn run_migrations() {
run_now({
struct Migrator;
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
fn migrations() -> Vec<MigrationItem> {
let mut migrations = vec![];
for m in MODULES.read().unwrap().iter() {
migrations.append(&mut m.migrations());

View file

@ -1,7 +1,8 @@
use crate::{app, util};
use crate::api::action::ActionItem;
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
use crate::db;
use crate::db::MigrationItem;
pub trait BaseModule {
fn type_name(&self) -> &'static str;
@ -25,9 +26,13 @@ pub trait ModuleTrait: BaseModule + Send + Sync {
fn configure_module(&self, cfg: &mut app::web::ServiceConfig) {
}
fn actions(&self) -> Vec<ActionItem> {
vec![]
}
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
#[allow(unused_variables)]
fn migrations(&self) -> Vec<Box<dyn db::MigrationTrait>> {
fn migrations(&self) -> Vec<MigrationItem> {
vec![]
}

View file

@ -9,12 +9,10 @@ pub struct Application {
server: app::Server,
}
pub fn essence() {
trace::info!("No bootstrap configured");
}
pub enum UsingBootstrap {Fn(fn()), No}
impl Application {
pub async fn prepare(bootstrap: fn()) -> Result<Self, Error> {
pub async fn prepare(bootstrap: UsingBootstrap) -> Result<Self, Error> {
// Rótulo de presentación.
app::banner::print_on_startup();
@ -36,15 +34,20 @@ impl Application {
// Ejecuta la función de inicio de la aplicación.
trace::info!("Calling application bootstrap");
let _ = &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::register_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::migrations();
module::all::run_migrations();
// Prepara el servidor web.
let server = app::HttpServer::new(move || {

View file

@ -4,8 +4,23 @@ pub use sea_orm::{DatabaseConnection as DbConn, ExecResult, QueryResult};
pub use sea_schema::migration::prelude::*;
pub type MigrationItem = Box<dyn MigrationTrait>;
#[macro_export]
macro_rules! boxed_migration {
macro_rules! pub_migration {
( $migration:ident ) => {
pub struct $migration;
impl MigrationName for $migration {
fn name(&self) -> &str {
crate::util::partial_type_name(module_path!(), 1)
}
}
};
}
#[macro_export]
macro_rules! migration_item {
( $migration_module:ident ) => {{
Box::new(migration::$migration_module::Migration)
}};

View file

@ -72,7 +72,7 @@ impl Classes {
ClassesOp::SetDefault => self.default = classes.to_owned(),
}
self.option = Some(concat_string!(self.default, " ", self.added).trim().to_owned());
self.option = Some(concat_string!(self.default, " ", self.added.trim()).to_owned());
self
}

View file

@ -4,7 +4,6 @@
pub use crate::{
args,
concat_string,
module_name,
theme_static_files,
};
@ -14,19 +13,24 @@ pub use crate::localize;
pub use crate::html::*;
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
pub use crate::{db, db::*, boxed_migration};
pub use crate::{
db,
db::*,
pub_migration,
migration_item,
};
pub use crate::api::{
pub use crate::{action_item, api::{
action::*,
component::*,
module::*,
theme::*,
};
}};
pub use crate::response::page::*;
pub use crate::app;
pub use crate::app::application::{Application, essence};
pub use crate::app::application::{Application, UsingBootstrap};
pub use crate::base::component::*;

View file

@ -1,26 +1,25 @@
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 const ACTION_BEFORE_RENDER_PAGE: &str = "pagetop::render::before_render_page";
pub struct PageAction {
action: TypeAction,
pub struct ActionBeforeRenderPage {
action: Option<fn(&mut Page)>,
weight: isize,
}
impl ActionTrait for PageAction {
impl ActionTrait for ActionBeforeRenderPage {
fn new() -> Self {
PageAction {
action: TypeAction::None,
ActionBeforeRenderPage {
action: None,
weight: 0,
}
}
fn machine_name(&self) -> &'static str {
ACTION_BEFORE_RENDER_PAGE
}
fn weight(&self) -> isize {
self.weight
}
@ -30,34 +29,20 @@ impl ActionTrait for PageAction {
}
}
impl PageAction {
pub fn new(action: TypeAction) -> Self {
PageAction {
action,
weight: 0,
}
impl ActionBeforeRenderPage {
pub fn with_action(mut self, action: fn(&mut Page)) -> Self {
self.action = Some(action);
self
}
pub fn new_with_weight(action: TypeAction, weight: isize) -> Self {
PageAction {
action,
weight,
}
pub fn with_weight(mut self, weight: isize) -> Self {
self.weight = weight;
self
}
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)
pub fn run(&self, page: &mut Page) {
if let Some(action) = self.action {
action(page)
}
}
}

View file

@ -1,4 +1,8 @@
pub mod action;
mod action;
pub use action::{
ACTION_BEFORE_RENDER_PAGE,
ActionBeforeRenderPage,
};
mod page;
pub use page::Page;

View file

@ -6,7 +6,7 @@ use crate::api::component::*;
use std::collections::HashMap;
use super::action::PageAction;
use super::{ACTION_BEFORE_RENDER_PAGE, ActionBeforeRenderPage};
static DEFAULT_LANGUAGE: Lazy<Option<String>> = Lazy::new(|| {
let language = SETTINGS.app.language[..2].to_lowercase();
@ -151,10 +151,10 @@ impl<'a> Page<'a> {
// Page RENDER.
pub fn render(&mut self) -> app::Result<Markup> {
// Acciones de los módulos antes de renderizar el tema.
// Acciones de los módulos antes de renderizar la página.
run_actions(
"",
|a| action_ref::<PageAction>(&**a).before_render_page(self)
ACTION_BEFORE_RENDER_PAGE,
|a| action_ref::<ActionBeforeRenderPage>(&**a).run(self)
);
// Acciones del tema antes de renderizar la página.

View file

@ -18,17 +18,6 @@ macro_rules! args {
}};
}
#[macro_export]
macro_rules! module_name {
() => {{
let name = module_path!();
match name.rfind("::") {
Some(position) => &name[(position + 2)..],
None => name
}
}};
}
#[macro_export]
macro_rules! theme_static_files {
( $cfg:ident, $dir:expr ) => {{
@ -47,7 +36,7 @@ macro_rules! theme_static_files {
}};
}
pub(crate) fn partial_type_name(type_name: &'static str, last: usize) -> &'static str {
pub fn partial_type_name(type_name: &'static str, last: usize) -> &'static str {
if last == 0 {
return type_name;
}