From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

=head1 NAME
Templer::Plugin::Factory - A simple plugin class
=cut
=head1 DESCRIPTION
This class implements a singleton within which plugin classes may
be registered and retrieved.
The plugins used by C<templer> are of two forms:
=over 8
=item formatters
These plugins operate upon the text contained in L<Templer::Site::Page> objects
and transform the input into HTML.
=item variable expanders
These plugins, also operating on L<Templer::Site::Page> objects, are allowed
the opportunity to modify, update, replace, or delete the various per-page
variables.
=back
Plugins of each type register themselves by calling the appropriate methods
in this class.
=cut
=head1 LICENSE
This module is free software; you can redistribute it and/or modify it
under the terms of either:
a) the GNU General Public License as published by the Free Software
Foundation; either version 2, or (at your option) any later version,
or
b) the Perl "Artistic License".
=cut
=head1 AUTHOR
Steve Kemp <steve@steve.org.uk>
=cut
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2012-2015 Steve Kemp <steve@steve.org.uk>.
This library is free software. You can modify and or distribute it under
the same terms as Perl itself.
=cut
=head1 METHODS
=cut
use strict;
my $singleton;
#
# Look for plugins beneath the `Templer::Plugin` namespace.
#
search_path => ['Templer::Plugin'],
require => 1;
#
# Invoking `plugins` ensures that we've actually loaded our plugins
# by the time this module is loaded.
#
my @plugins = plugins();
=head2 new
Constructor.
This class is a singleton, so this method will either construct
an instance of this class or return the global instance.
=cut
sub new
{
my $class = shift;
$singleton ||= bless {}, $class;
}
=head2 load_plugins
This method loads "*.pm" from the given directory.
=cut
sub load_plugins
{
my ( $self, $directory ) = (@_);
return unless ( -d $directory );
foreach my $file ( sort( glob( $directory . "/*.pm" ) ) )
{
require $file;
}
}
=head2 init
For each loaded plugin invoke the "init" method, if it exists.
=cut
sub init
{
my ( $self, $site ) = (@_);
foreach my $plugin ( @{ $self->{ 'plugins' } } )
{
if ( UNIVERSAL::can( $plugin, "init" ) )
{
$plugin->init($site);
}
}
}
=head2 register_formatter
This method should be called by all formatting plugins to register
themselves. The two arguments are the name of the input-format,
and the class-name which may be instantiated to process that kind
of input.
L<Templer::Plugin::Textile> and L<Templer::Plugin::Markdown> are
example classes.
=cut
sub register_formatter
{
my ( $self, $name, $obj ) = (@_);
die "No name" unless ($name);
$name = lc($name);
$self->{ 'formatters' }{ $name } = $obj;
}
=head2 register_filter
This method should be called by all template filter plugins to register
themselves. The two arguments are the name of the template-filter,
and the class-name which may be instantiated to process that kind
of input.
L<Templer::Plugin::Dollar> and L<Templer::Plugin::Strict> are
example classes.
=cut
sub register_filter
{
my ( $self, $name, $obj ) = (@_);
die "No name" unless ($name);
$name = lc($name);
$self->{ 'filters' }{ $name } = $obj;
}
=head2 register_plugin
This method should be called by all variable-expanding plugins to register
themselves. The expected argument is the class-name which may be instantiated
to expand variables.
L<Templer::Plugin::ShellCommand>, L<Templer::Plugin::FileGlob>, and
L<Templer::Plugin::FileContents> are examples of such plugins.
NOTE: The plugin is instantiated immediately, and kept alive for the duration
of a templer-run.
=cut
sub register_plugin
{
my ( $self, $obj ) = (@_);
push( @{ $self->{ 'plugins' } }, $obj->new() );
}
=head2 expand_variables
Expand variables via all loaded plugins.
=cut
sub expand_variables
{
my ( $self, $site, $page, $data ) = (@_);
my $out;
foreach my $plugin ( @{ $self->{ 'plugins' } } )
{
my %in = %$data;
$out = $plugin->expand_variables( $site, $page, \%in );
$data = \%$out;
}
return ($data);
}
=head2 cleanup
For each loaded plugin invoke the "cleanup" method, if it exists.
This can be useful if you wish a plugin to generate a site-map, or similar.
=cut
sub cleanup
{
my ($self) = (@_);
foreach my $plugin ( @{ $self->{ 'plugins' } } )
{
if ( UNIVERSAL::can( $plugin, "cleanup" ) )
{
$plugin->cleanup();
}
}
}
=head2 formatter
Return a new instance of the formatter class with the given name.
C<undef> is returned if no such plugin is registered.
=cut
sub formatter
{
my ( $self, $name ) = (@_);
die "No name" unless ($name);
$name = lc($name);
#
# Lookup the formatter by name, if it is found
# then instantiate the clsee.
#
my $obj = $self->{ 'formatters' }{ $name } || undef;
$obj = $obj->new() if ($obj);
return ($obj);
}
=head2 formatters
Return the names of each registered formatter-plugin, this is only
used by the test-suite.
=cut
sub formatters
{
my ($self) = (@_);
keys( %{ $self->{ 'formatters' } } );
}
=head2 filter
Return a new instance of the filter class with the given name.
C<undef> is returned if no such plugin is registered.
=cut
sub filter
{
my ( $self, $name ) = (@_);
die "No name" unless ($name);
$name = lc($name);
#
# Lookup the filter by name, if it is found
# then instantiate the class.
#
my $obj = $self->{ 'filters' }{ $name } || undef;
$obj = $obj->new() if ($obj);
return ($obj);
}
=head2 filters
Return the names of each registered filter-plugin, this is only
used by the test-suite.
=cut
sub filters
{
my ($self) = (@_);
keys( %{ $self->{ 'filters' } } );
}
1;