The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Plugin::System - A plugin system for your Perl framework or application

VERSION

This document describes version 0.000001 of Plugin::System (from Perl distribution Plugin-System), released on 2024-03-02.

SYNOPSIS

Use in framework

In your lib/Your/Framework.pm:

 package Your::Framework;
 use Plugin::System;

 use Plugin::System (
     # optional, default to caller package (i.e. in this case, Your::Framework)
     #app => 'Your::Framework',

     # optional, namespace to search for the plugins, default to ${app}::Plugin.
     # can also be an arrayref to search in multiple namespaces.
     plugin_ns => 'Your::FrameworkX',

     # required, list of known hooks along with their specification
     hooks => {
         check_input => { ... },
         output => { ... },
         ...
     },
     ...

 );

 # after this, Plugin::System will install hook_XXX(;&@) routines for each
 # defined hook so you can use them:

 sub run {
     my %args = @_;

     my $res = hook_check_input {
         # the code block for a hook. plugins can run before and/or after this
         # block. a "before" plugin can ask to skip this block. an "after"
         # plugin can ask to repeat this block.
         check_args(%args);
     };
     # the hook will return the result of the code block or, if configured, the
     # result of the first handler that returns result.

     # this hook does not provide the main call block. the hook might require
     # that there is at least one plugin that installs a handler for this hook.
     hook_output;
 }

 1;

Plugin code

In your lib/Your/FrameworkX/Foo.pm:

 package Your::FrameworkX::Foo;

 sub meta {
     # must return a DefHash, see DefHash specification
     return +{
         # optional, default priority for all handlers (0-100), defaults to 50
         #prio => ...,

         # optional, define arguments/configuration parameters that your plugin
         # recognizes
         args => {
             # a DefHash
             arg1 => {
                 # a DefHash
                 summary => 'Blah blah ...',
                 schema => 'str*', # a Sah schema
                 req => 0,
                 ...
             },
         },
     };
 }

 sub new {
     my ($self, %args) = @_;
     ...
 }

 # required handler if we want to handle the 'check_input' hook
 sub on_check_input {
     ...

     # plugin can signal success by returning 200 or error by returning 4xx or
     # 5xx status. it can also return 201 to instruct to skip calling the rest
     # of the handlers for the hook. it can also return 204 to "decline".

     # a handler can be configured to return (replace) the result of the
 }

 # metadata for the 'check_input' handler. required.
 sub meta_on_check_input {
     return +{ prio => ..., ... };
 }

 # a before_ handler is optional
 sub before_check_input {
     my ($self, $r) = @_;
     ...

     # plugin can instruct to cancel the hook by returning 601.
 }

 # required if before_check_input is defined
 sub meta_before_check_input { ... }

 # an after_ handler is optional
 sub after_check_input {
     my ($self, $r) = @_;
     ...
     # plugin can instruct to repeat an hook by returning 602.
 }

 # required if after_check_input is defined
 sub meta_after_check_input { ... }

 1;

Using plugins for users

You can use Plugin::System::Exporter, e.g. in lib/Your/Framework.pm:

 package Your::Framework;
 ...
 use Plugin::System::Exporter (
     # optional, see Synopsis/Use in framework
     # app => 'Your::Framework',

     # optional, see Synopsis/Use in framework
     plugin_ns => 'Your::FrameworkX',
 );

so your users can activate plugins this way:

 use Your::Framework 'Foo' => {arg1=>..., arg2 => ..., ...};

DESCRIPTION

A plugin approach offers flexibility. Users can enable or disable plugins which they need, in the order that they want. Each plugin can supply behavior at various code points (hooks) in an application. More than one plugin (also multiple instances of the same plugin) can supply behavior for a single hook.

This module, Plugin::System, offers a fast, highly flexible plugin system with a (hopefully) nice syntax. A plugin can modify the flow of the application by skipping (aborting) or repeating a hook. For all features of this plugin system, see Plugin::System::_ModuleFeatures.

GLOSSARY

Hook

A named point in code. Plugins can define handlers to add behaviors for a hook. A hook can also contain the main code block. When no handlers are registered for a hook, only the main code block will be executed and the result returned. When there are handlers, the handler can add behavior before and/or after the main code block, and can also replace or remove the execution of the main code block.

Main code block

The code to be executed by default during an hook. See "Hook".

Handler

Code to execute for an hook. Handlers are supplied by plugins.

Plugin

A module defining a class which provides methods to generate handlers for hooks. In Plugin::System, the user can also register plugin from another application/framework (as long as it also uses Plugin::System). The user can also customize the target hook and priority of handlers from the plugin.

Priority

A number between 0 and 100 to specify order of execution of handlers for a hook. Smaller number means higher priority (earlier execution). If two handlers have the same priority, then handlers registered first will be executed first.

Target package

The package which uses Plugin::System and defines the hooks it wants to have. Plugin::System will install the hook subroutines (by default hook_NAMEs) to this package.

Registration

The act of loading a plugin then adding the handlers it provides to a target package.

Initialization

The process of generating the hook subroutines for a target package.

IMPORT ARGUMENTS

Plugin::System accepts a key-value pairs of arguments. Known arguments:

hooks

Define known hooks.

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/Plugin-System.

SOURCE

Source repository is at https://github.com/perlancar/perl-Plugin-System.

SEE ALSO

Other plugin systems: Module::Pluggable and its variants like Module::Pluggable::Fast. See comparison and benchmarks at Acme::CPANModules::PluginSystems.

Examples of frameworks using Plugin::System: Require::HookPlugin, ScriptX, Data::DumpX.

AUTHOR

perlancar <perlancar@cpan.org>

CONTRIBUTING

To contribute, you can send patches by email/via RT, or send pull requests on GitHub.

Most of the time, you don't need to build the distribution yourself. You can simply modify the code, then test via:

 % prove -l

If you want to build the distribution (e.g. to try to install it locally on your system), you can install Dist::Zilla, Dist::Zilla::PluginBundle::Author::PERLANCAR, Pod::Weaver::PluginBundle::Author::PERLANCAR, and sometimes one or two other Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond that are considered a bug and can be reported to me.

COPYRIGHT AND LICENSE

This software is copyright (c) 2024, 2023 by perlancar <perlancar@cpan.org>.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

BUGS

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=Plugin-System

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.