Mojolicious-Plugin-Fondation

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

# NAME

Mojolicious::Plugin::Fondation - Hierarchical plugin loader with configuration priority and resource sharing

# VERSION

version 0.03

# SYNOPSIS

    # In your Mojolicious application (myapp.pl or startup)
    plugin 'Fondation' => {
        dependencies => [
            { 'Fondation::User' => { title => 'Custom User' } },
            'Fondation::Authorization',
        ],
    };

    # In a plugin (e.g. lib/Mojolicious/Plugin/Fondation/User.pm)
    package Mojolicious::Plugin::Fondation::User;
    use Mojo::Base 'Mojolicious::Plugin', -signatures;

    sub fondation_meta {
        return {
            dependencies => [],
            defaults => {
                title => 'User Management',
                items_per_page => 20,
            },
        };
    }

    sub register ($self, $app, $conf) {
        $app->routes->get('/users' => sub ($c) { $c->render(text => "Users!") });
        return $self;   # Important for finalyze actions
    }

    sub fondation_finalyze ($self, $app, $long_name) {
        # Optional: code to run after all plugins are loaded
        $self->log->debug("User plugin fully loaded");
    }

    1;

# DESCRIPTION

Fondation attempts to provide a foundation for building websites from
pre-built bricks. It is a plugin loader for Mojolicious that enables
hierarchical, recursive plugin loading with automatic configuration
merging, resource sharing (controllers, templates), and post-load actions.

It is designed for modular applications where multiple plugins contribute
routes, templates, and behavior, while avoiding duplicate loads and
respecting configuration priorities.

Key features:

- Recursive plugin loading via `dependencies`
- Configuration cascade: direct > app config > plugin defaults
- Automatic discovery and registration of `share/templates`
- Application-level `share/templates` have priority
- Extensible post-load actions (Templates, Controllers, custom)
- Deferred initialization via `fondation_finalyze`
- Contextual logging via `$self->log` (Mojo::Log context)

# LOADING ORDER

    1. load_plugin_recursive
       └─ Fondation -> dependencies recursively

    2. run_post_load_actions
       └─ For every plugin (load order), execute all actions
          (Templates, Controllers, custom)
       └─ App-level share/templates added with highest priority

    3. run_finalyze
       └─ For every plugin (load order), call fondation_finalyze()

# QUICK START

    # myapp.pl
    use Mojolicious::Lite;

    plugin 'Fondation' => {
        dependencies => [
            'Fondation::User',
            'Fondation::Authorization',
        ],
    };

    # Fondation loads Authorization, which depends on Role + Permission.
    # Result: Role -> Permission -> Authorization -> User
    # All share/templates and controllers are auto-discovered.

## With a config file

    # myapp.conf
    {
        'Fondation' => {
            dependencies => ['Fondation::User', 'Fondation::Authorization'],
        },
        'Fondation::User' => {
            title => 'User Management',
        },
    }

    # myapp.pl
    plugin 'Config';
    plugin 'Fondation';   # reads dependencies from config

# CONFIGURATION PRIORITIES

Each plugin receives a merged configuration built from three sources,
combined with [Hash::Merge](https://metacpan.org/pod/Hash%3A%3AMerge). The cascade priority is:

### 1. Direct configuration (passed in `dependencies` array)

    plugin 'Fondation' => {
        dependencies => [
            { 'Fondation::User' => { title => 'Direct override' } },
        ],
    };

### 2. Application configuration file (e.g. myapp.conf)

    {
        'Fondation::User' => {
            title => 'From config file',
        }
    }

### 3. Plugin defaults (returned by `fondation_meta`)

    sub fondation_meta {
        return {
            defaults => { title => 'Default value' },
        };
    }

The merge rules are:

- Scalars -- overwrite. The highest-priority non-empty value wins.
- Hashes -- merged recursively. Keys present at multiple levels are resolved
by priority; keys present at only one level survive untouched.
- Arrays -- concatenated. All values from all levels are kept, ordered
by priority: direct elements first, then app config, then defaults.

Example: a plugin declares `allowed_roles => ['user']` in its defaults,
the app config adds `allowed_roles => ['editor']`, and a direct dependency
passes `allowed_roles => ['admin']`. The merged result is
`['admin', 'editor', 'user']` -- all three roles are available, with the
highest-priority one first.

The `dependencies` key is not special -- it follows the same array
concatenation rules. This means an app config can add extra dependencies
without repeating those already declared.

# RESOURCE DIRECTORIES (share/)

Fondation automatically handles shared resources from plugins:

- `share/templates` -> pushed to `$app-`renderer->paths>
- `share/public` -> pushed to `$app-`static->paths>

The application's own `share/templates` directory (if it exists) is added
with `unshift` and therefore has \*\*highest priority\*\* (application templates
override plugin templates).

# ACTIONS (POST-LOAD PROCESSING)

Actions are classes that run after \*\*all\*\* plugins are loaded, iterating
over each plugin in load order. They perform specific initialization tasks
such as registering templates or controllers. Actions are configurable via
the `actions` key in Fondation's configuration.



( run in 0.494 second using v1.01-cache-2.11-cpan-9581c071862 )