NAME
   Plugin::Tiny - A tiny plugin system for perl

VERSION
   version 0.012

SYNOPSIS
     #a complete 'Hello World' plugin
     package My::Plugin;
     use Moose; #optional; required is an object with new as constructor
     sub do_something { print "Hello World @_\n" }
     1;


     #in your core
     use Plugin::Tiny;
     my $ps=Plugin::Tiny->new(); #plugin system

     #load My::Plugin: require, import, return My::Plugin->new(@_)
     my $plugin=$ps->register(plugin=>'My::Plugin');


     #elsewhere in core: execute your plugin's methods
     my $plugin=$ps->get_plugin ($phase);
     $plugin->do_something(@args);

DESCRIPTION
   Plugin::Tiny is minimalistic plugin system for perl. Each plugin is
   associated with a keyword (referred to as phase). A limitation of
   Plugin::Tiny is that each phase can have only one plugin.

   Plugin::Tiny calls itself tiny because it doesn't attempt to solve all
   problems plugin systems could solve, because it consists of one smallish
   package, and it doesn't depend on a whole lot.

ATTRIBUTES
 debug
   Optional. Expects a boolean. Prints additional info to STDOUT.

 prefix
   Optional. You can have the prefix added to all plugin classes you
   register so save some typing and force plugins in your namespace:

     #without prefix
     my $ps=Plugin::Tiny->new
     $ps->register(plugin='Your::App::Plugin::Example1');
     $ps->register(plugin='Your::App::Plugin::Example2');

     #with prefix
     my $ps=Plugin::Tiny->new (  prefix=>'Your::App::Plugin::' );
     $ps->register(plugin='Example1');
     $ps->register(plugin='Example2');

 role
   Optional. One or more roles that all plugins have to be able to do. Can
   be overwritten in "register".

       role=>['Role::One', Role::Two]      #either as ArrayRef
       role=>'Role::One'                   #or a scalar

METHODS
 register
   Registers a plugin, i.e. loads it and makes a new plugin object. Needs a
   plugin package name (plugin). Returns the newly created plugin object on
   success. Confesses on error. Remaining arguments are passed down to the
   plugin constructor:

       $obj=$ps->register(
           plugin=>$plugin_class,   #required
           args=>$more_args,        #optional
       ); #returns result of $plugin_class->new (args=>$args);

   N.B. Your plugin cannot use 'phase', 'plugin', 'role', 'force' as named
   arguments.

   plugin
       The package name of the plugin. Required. Internally, the value of
       "prefix" is prepended to plugin.

   phase
       A phase asociated with the plugin. Optional. If not specified,
       Plugin::Tiny uses "default_phase" to determine the phase.

   role
       One or more roles that the plugin has to appply. Optional. Specify
       role=>undef to unset global roles. Currently, you can't mix global
       roles (defined via new) with local roles (defined via register).

           role=>'Single::Role' #or
           role=>['Role::One','Role:Two']
           role=>undef #unset global roles

   force
       Force re-registration of a previously used phase. Optional.

       Normally, Plugin::Tiny confesses if you try to register a phase that
       has previously been assigned. To overwrite this message make force
       true.

       With force both plugins will be loaded (required, imported) and both
       return new objects for their respective plugin classes, but after
       the second plugin is made, the first one cannot be accessed anymore
       through get_plugin.

 register_bundle
   Registers a bundle of plugins in no particular order. A bundle is just a
   hashRef with info needed to issue a series of register calls (see
   "register").

   Confesses if a plugin cannot be registered. Otherwise returns $bundle or
   undef.

     sub bundle{
       return {
         'Store::One' => {
             phase  => 'Store',
             role   => undef,
             dbfile => $self->core->config->{main}{dbfile},
           },
          'Scan::Monitor'=> {
             core   => $self->core
           },
       };
     }
     $ps->register_bundle(bundle)

   If you want to add or remove plugins, use hashref as usual:

     undef $bundle->{$plugin};                #remove a plugin using package name
     $bundle->{'My::Plugin'}={phase=>'foo'};  #add another plugin

   To facilitate inheritance of your plugins perhaps you put the hashref in
   a separate sub, so a child bundle can extend or remove plugins from
   yours.

 get_plugin
   Returns the plugin object associated with the phase. Returns undef on
   failure.

     my $plugin=$ps->get_plugin ($phase);

 default_phase
   Makes a default phase from (the plugin's) class name. Expects a
   $plugin_class. Returns scalar or undef. If prefix is defined it use tail
   and removes all '::'. If no prefix is set default_phase returns the last
   element of the class name:

       my $ps=Plugin-Tiny->new;
       $ps->default_phase(My::Plugin::Long::Example); #returns 'Example'

       $ps=Plugin-Tiny->new(prefix=>'My::Plugin::');
       $ps->default_phase(My::Plugin::Long::Example); #returns 'LongExample'

 get_class
   Returns the plugin's class (package name). Expects plugin (not its
   package name). Croaks on error.

     my $class=$ps->get_class ($plugin);

 get_phase
   returns the plugin's phase. Expects plugin (not its package name).
   Returns undef on failure. (You will not normally need get_phase, because
   typically your code knows the phases.)

     my $phase=$ps->get_phase ($plugin);

Recommendation: First Register Then Do Things
   Plugin::Tiny suggests that you first register (load) all your plugins
   before you actually do something with them. Internal "require" / "use"
   of your packages is deferred until runtime. You can control the order in
   which plugins are loaded (in the order you call "register"), but if you
   manage to load all of them before you do anything, you can forget about
   order.

   You may know Plugin::Tiny's phases at compile time, but not which
   plugins will be loaded.

Recommendation: Require a Plugin Role
   You may want to do a plugin role for all you plugins, e.g. to
   standardize the interface for your plugins. Perhaps to make sure that a
   specific sub is available in the plugin:

     package My::Plugin;
     use Moose;
     with 'Your::App::Role::Plugin';
     #...

Plugin Bundles
   You can create bundles of plugins if you pass the plugin system to the
   (bundling) plugin. That way you can load multiple plugins for one phase.
   You still need unique phases for each plugin:

     package My::Core;
     use Moose;
     has 'plugin_system'=>(
       is=>'ro',
       isa=>'Plugin::Tiny',
       default=>sub{Plugin::Tiny->new},
     );

     sub BUILD {
       $self->plugins->register(
         plugin=>'PluginBundle',
         phase=>'Bundle',
         plugin_system=>$self->plugins,
       );
     }

     #elsewhere in core
     my $b=$self->plugin_system->get_plugin ('Bundle');
     $b->start();


     package PluginBundle;
     use Moose;
     has 'plugin_system'=>(is=>'ro', isa=>'Plugin::Tiny', required=>1);

     sub bundle {
         {Plugin::One=>{},Plugin::Two=>{}}
     }
     sub BUILD {
       #phase defaults to 'One' and 'Two':
       $self->plugins->register_bundle(bundle());

       #more or less the same as:
       #$self->plugins->register (plugin=>'Plugin::One');
       #$self->plugins->register (plugin=>'Plugin::Two');
     }

     sub start {
       my $one=$self->plugins->get('One');
       $one->do_something(@args);
     }

CONTRIBUTORS
   Thanks to Toby Inkster for making Plugin::Tiny tinier.

SEE ALSO
   Object::Pluggable Module::Pluggable MooX::Role::Pluggable
   MooseX::Object::Pluggable MooseX::Role::Pluggable

AUTHOR
   Maurice Mengel <[email protected]>

COPYRIGHT AND LICENSE
   This software is copyright (c) 2012 by Maurice Mengel.

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