Mojolicious-Plugin-Fondation

 view release on metacpan or  search on metacpan

lib/Mojolicious/Plugin/Fondation.pm  view on Meta::CPAN


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

=head1 VERSION

version 0.03

=head1 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;

=head1 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:

=over 4

=item * Recursive plugin loading via C<dependencies>

=item * Configuration cascade: direct > app config > plugin defaults

=item * Automatic discovery and registration of C<share/templates>

=item * Application-level C<share/templates> have priority

=item * Extensible post-load actions (Templates, Controllers, custom)

=item * Deferred initialization via C<fondation_finalyze>

=item * Contextual logging via C<< $self->log >> (Mojo::Log context)

=back

=head1 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()

=head1 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.

=head2 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

=head1 CONFIGURATION PRIORITIES

Each plugin receives a merged configuration built from three sources,
combined with L<Hash::Merge>. The cascade priority is:

=head3 1. Direct configuration (passed in C<dependencies> array)

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

=head3 2. Application configuration file (e.g. myapp.conf)

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

=head3 3. Plugin defaults (returned by C<fondation_meta>)

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

The merge rules are:

=over 4

=item * Scalars -- overwrite. The highest-priority non-empty value wins.

=item * Hashes -- merged recursively. Keys present at multiple levels are resolved
by priority; keys present at only one level survive untouched.

=item * Arrays -- concatenated. All values from all levels are kept, ordered
by priority: direct elements first, then app config, then defaults.

=back

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

The C<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.

=head1 RESOURCE DIRECTORIES (share/)

Fondation automatically handles shared resources from plugins:

=over 4

=item * C<share/templates> -> pushed to C<$app->renderer->paths>

=item * C<share/public> -> pushed to C<$app->static->paths>

=back



( run in 2.114 seconds using v1.01-cache-2.11-cpan-9581c071862 )