The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Module::PluginFinder - automatically choose the most appropriate plugin module.

SYNOPSIS

 use Module::PluginFinder;

 my $finder = Module::PluginFinder->new(
                 search_path => 'MyApp::Plugin',

                 filter => sub {
                    my ( $module, $searchkey ) = @_;
                    $module->can( $searchkey );
                 },
              );

 my $ball = $finder->construct( "bounce" );
 $ball->bounce();

 my $fish = $finder->construct( "swim" );
 $fish->swim();

DESCRIPTION

This module provides a factory class. Objects in this class search for a specific plugin module to fit some criteria. Each time a new object is to be constructed by the factory, the caller should provide a value which in some way indicates the kind of object required. The factory's filter function is then used to determine which plugin module fits the criteria.

The most flexible way to determine the required module is to provide a filter function. When looking for a suitable module, the function is called once for each candidate module, and is passed the module's name and the search key. The function can then return a boolean to indicate whether the module will be suitable. The value of the search key is not directly used by the Module::PluginFinder in this case, and therefore is not restricted to being a simple scalar value; any sort of reference may be passed.

Instead of a filter function, the factory can inspect a package variable or constant method in each of the candidate modules, looking for a string match with the search key; see the typevar and typefunc constructor arguments. When using this construction, a map from type names to module names will be cached at the time the Module::PluginFinder object is created, and will therefore not be sensitive to changes in the values once this is done. Because of this, the key should be a simple string, rather than a reference.

CONSTRUCTOR

$finder = Module::PluginFinder->new( %args )

Constructs a new Module::PluginFinder factory object. The constructor will search the module path for all available plugins, as determined by the search_path key and store them.

The %args hash must take the following keys:

search_path => STRING or ARRAY

A string declaring the module namespace, or an array reference of module namespaces to search for plugins (passed to Module::Pluggable::Object).

In order to specify the way candidate modules are selected, one of the following keys must be supplied.

filter => CODE

The filter function for determining whether a module is suitable as a plugin

typevar => STRING

The name of a package variable to match against the search key

typefunc => STRING

The name of a package method to call to return the type name. The method will be called in scalar context with no arguments; as

 $type = $module->$typefunc();

If it returns undef or throws an exception, then the module will be ignored

METHODS

@modules = $finder->modules()

Returns the list of module names available to the finder.

$module = $finder->find_module( $searchkey )

Search for a plugin module that matches the search key. Returns the name of the first module for which the filter returns true, or undef if no suitable module was found.

$searchkey

A value to pass to the stored filter function.

$object = $finder->construct( $searchkey, @constructorargs )

Search for a plugin module that matches the search key, then attempt to create a new object in that class. If a suitable module is found to match the $searchkey then the new method is called on it, passing the @constructorargs. If no suitable module is found then an exception is thrown.

$searchkey

A value to pass to the stored filter function.

@constructorargs

A list to pass to the class constructor.

$finder->rescan()

Perform another search for plugin modules. This method is useful whenever new modules may be present since the object was first constructed.

EXAMPLES

The filter function allows various ways to select plugin modules on different criteria. The following examples indicate a few ways to do this.

Availability of a function / method

 my $f = Module::PluginFinder->new(
            search_path => ...,

            filter => sub {
               my ( $module, $searchkey ) = @_;

               return $module->can( $searchkey );
            },
         );

Each plugin then simply has to implement the required function or method in order to be automatically selected.

Value of a method call

 my $f = Module::PluginFinder->new(
            search_path => ...,

            filter => sub {
               my ( $module, $searchkey ) = @_;

               return 0 unless $module->can( "is_plugin_for" );
               return $module->is_plugin_for( $searchkey );
            },
         );

Each plugin then needs to implement a method called is_plugin_for, that should examine the $searchkey and perform whatever testing it requires, then return a boolean to indicate if the plugin is suitable.

Value of a constant

Because a constant declared by the use constant pragma is a plain function, it can be called by the typefunc filter:

 my $f = Module::PluginFinder->new(
            search_path => ...,

            typefunc => 'PLUGIN_TYPE',
         );

Each plugin can then declare its type using a constuction like

 use constant PLUGIN_TYPE => "my type here";

Alternatively, a normal package method may be created that performs any work required to determine the plugin's type

 sub PLUGIN_TYPE
 {
    my $class = shift;

    ...

    return $typename;
 }

Note that the type function in each module will only be called once, and the returned value cached.

Value of a package scalar

The typevar constructor argument generates the filter function automatically.

 my $f = Module::PluginFinder->new(
            search_path => ...,

            typevar => 'PLUGIN_TYPE',
         );

Each plugin can then declare its type using a normal our scalar variable:

 our $PLUGIN_TYPE = "my type here";

SEE ALSO

AUTHOR

Paul Evans <leonerd@leonerd.org.uk>