466 lines
15 KiB
Perl
466 lines
15 KiB
Perl
#============================================================= -*-Perl-*-
|
|
#
|
|
# Template::Plugins
|
|
#
|
|
# DESCRIPTION
|
|
# Plugin provider which handles the loading of plugin modules and
|
|
# instantiation of plugin objects.
|
|
#
|
|
# AUTHORS
|
|
# Andy Wardley <abw@wardley.org>
|
|
#
|
|
# COPYRIGHT
|
|
# Copyright (C) 1996-2006 Andy Wardley. All Rights Reserved.
|
|
# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
|
|
#
|
|
# This module is free software; you can redistribute it and/or
|
|
# modify it under the same terms as Perl itself.
|
|
#
|
|
# REVISION
|
|
# $Id$
|
|
#
|
|
#============================================================================
|
|
|
|
package Template::Plugins;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use base 'Template::Base';
|
|
use Template::Constants;
|
|
|
|
our $VERSION = '3.009';
|
|
our $DEBUG = 0 unless defined $DEBUG;
|
|
our $PLUGIN_BASE = 'Template::Plugin';
|
|
our $STD_PLUGINS = {
|
|
'assert' => 'Template::Plugin::Assert',
|
|
'cgi' => 'Template::Plugin::CGI',
|
|
'datafile' => 'Template::Plugin::Datafile',
|
|
'date' => 'Template::Plugin::Date',
|
|
'debug' => 'Template::Plugin::Debug',
|
|
'directory' => 'Template::Plugin::Directory',
|
|
'dbi' => 'Template::Plugin::DBI',
|
|
'dumper' => 'Template::Plugin::Dumper',
|
|
'file' => 'Template::Plugin::File',
|
|
'format' => 'Template::Plugin::Format',
|
|
'html' => 'Template::Plugin::HTML',
|
|
'image' => 'Template::Plugin::Image',
|
|
'iterator' => 'Template::Plugin::Iterator',
|
|
'latex' => 'Template::Plugin::Latex',
|
|
'pod' => 'Template::Plugin::Pod',
|
|
'scalar' => 'Template::Plugin::Scalar',
|
|
'table' => 'Template::Plugin::Table',
|
|
'url' => 'Template::Plugin::URL',
|
|
'view' => 'Template::Plugin::View',
|
|
'wrap' => 'Template::Plugin::Wrap',
|
|
'xml' => 'Template::Plugin::XML',
|
|
'xmlstyle' => 'Template::Plugin::XML::Style',
|
|
};
|
|
|
|
|
|
#========================================================================
|
|
# -- PUBLIC METHODS --
|
|
#========================================================================
|
|
|
|
#------------------------------------------------------------------------
|
|
# fetch($name, \@args, $context)
|
|
#
|
|
# General purpose method for requesting instantiation of a plugin
|
|
# object. The name of the plugin is passed as the first parameter.
|
|
# The internal FACTORY lookup table is consulted to retrieve the
|
|
# appropriate factory object or class name. If undefined, the _load()
|
|
# method is called to attempt to load the module and return a factory
|
|
# class/object which is then cached for subsequent use. A reference
|
|
# to the calling context should be passed as the third parameter.
|
|
# This is passed to the _load() class method. The new() method is
|
|
# then called against the factory class name or prototype object to
|
|
# instantiate a new plugin object, passing any arguments specified by
|
|
# list reference as the second parameter. e.g. where $factory is the
|
|
# class name 'MyClass', the new() method is called as a class method,
|
|
# $factory->new(...), equivalent to MyClass->new(...) . Where
|
|
# $factory is a prototype object, the new() method is called as an
|
|
# object method, $myobject->new(...). This latter approach allows
|
|
# plugins to act as Singletons, cache shared data, etc.
|
|
#
|
|
# Returns a reference to a plugin, (undef, STATUS_DECLINE) to decline
|
|
# the request or ($error, STATUS_ERROR) on error.
|
|
#------------------------------------------------------------------------
|
|
|
|
sub fetch {
|
|
my ($self, $name, $args, $context) = @_;
|
|
my ($factory, $plugin, $error);
|
|
|
|
$self->debug("fetch($name, ",
|
|
defined $args ? ('[ ', join(', ', @$args), ' ]') : '<no args>', ', ',
|
|
defined $context ? $context : '<no context>',
|
|
')') if $self->{ DEBUG };
|
|
|
|
# NOTE:
|
|
# the $context ref gets passed as the first parameter to all regular
|
|
# plugins, but not to those loaded via LOAD_PERL; to hack around
|
|
# this until we have a better implementation, we pass the $args
|
|
# reference to _load() and let it unshift the first args in the
|
|
# LOAD_PERL case
|
|
|
|
$args ||= [ ];
|
|
unshift @$args, $context;
|
|
|
|
$factory = $self->{ FACTORY }->{ $name } ||= do {
|
|
($factory, $error) = $self->_load($name, $context);
|
|
return ($factory, $error) if $error; ## RETURN
|
|
$factory;
|
|
};
|
|
|
|
# call the new() method on the factory object or class name
|
|
eval {
|
|
if (ref $factory eq 'CODE') {
|
|
defined( $plugin = &$factory(@$args) )
|
|
|| die "$name plugin failed\n";
|
|
}
|
|
else {
|
|
defined( $plugin = $factory->new(@$args) )
|
|
|| die "$name plugin failed: ", $factory->error(), "\n";
|
|
}
|
|
};
|
|
if ($error = $@) {
|
|
# chomp $error;
|
|
return $self->{ TOLERANT }
|
|
? (undef, Template::Constants::STATUS_DECLINED)
|
|
: ($error, Template::Constants::STATUS_ERROR);
|
|
}
|
|
|
|
return $plugin;
|
|
}
|
|
|
|
|
|
|
|
#========================================================================
|
|
# -- PRIVATE METHODS --
|
|
#========================================================================
|
|
|
|
#------------------------------------------------------------------------
|
|
# _init(\%config)
|
|
#
|
|
# Private initialisation method.
|
|
#------------------------------------------------------------------------
|
|
|
|
sub _init {
|
|
my ($self, $params) = @_;
|
|
my ($pbase, $plugins, $factory) =
|
|
@$params{ qw( PLUGIN_BASE PLUGINS PLUGIN_FACTORY ) };
|
|
|
|
$plugins ||= { };
|
|
|
|
# update PLUGIN_BASE to an array ref if necessary
|
|
$pbase = [ ] unless defined $pbase;
|
|
$pbase = [ $pbase ] unless ref($pbase) eq 'ARRAY';
|
|
|
|
# add default plugin base (Template::Plugin) if set
|
|
push(@$pbase, $PLUGIN_BASE) if $PLUGIN_BASE;
|
|
|
|
$self->{ PLUGIN_BASE } = $pbase;
|
|
$self->{ PLUGINS } = { %$STD_PLUGINS, %$plugins };
|
|
$self->{ TOLERANT } = $params->{ TOLERANT } || 0;
|
|
$self->{ LOAD_PERL } = $params->{ LOAD_PERL } || 0;
|
|
$self->{ FACTORY } = $factory || { };
|
|
$self->{ DEBUG } = ( $params->{ DEBUG } || 0 )
|
|
& Template::Constants::DEBUG_PLUGINS;
|
|
|
|
return $self;
|
|
}
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
# _load($name, $context)
|
|
#
|
|
# Private method which attempts to load a plugin module and determine the
|
|
# correct factory name or object by calling the load() class method in
|
|
# the loaded module.
|
|
#------------------------------------------------------------------------
|
|
|
|
sub _load {
|
|
my ($self, $name, $context) = @_;
|
|
my ($factory, $module, $base, $pkg, $file, $ok, $error);
|
|
|
|
if ($module = $self->{ PLUGINS }->{ $name } || $self->{ PLUGINS }->{ lc $name }) {
|
|
# plugin module name is explicitly stated in PLUGIN_NAME
|
|
$pkg = $module;
|
|
($file = $module) =~ s|::|/|g;
|
|
$file =~ s|::|/|g;
|
|
$self->debug("loading $module.pm (PLUGIN_NAME)")
|
|
if $self->{ DEBUG };
|
|
$ok = eval { require "$file.pm" };
|
|
$error = $@;
|
|
}
|
|
else {
|
|
# try each of the PLUGIN_BASE values to build module name
|
|
($module = $name) =~ s/\./::/g;
|
|
|
|
foreach $base (@{ $self->{ PLUGIN_BASE } }) {
|
|
$pkg = $base . '::' . $module;
|
|
($file = $pkg) =~ s|::|/|g;
|
|
|
|
$self->debug("loading $file.pm (PLUGIN_BASE)")
|
|
if $self->{ DEBUG };
|
|
|
|
$ok = eval { require "$file.pm" };
|
|
last unless $@;
|
|
|
|
$error .= "$@\n"
|
|
unless ($@ =~ /^Can\'t locate $file\.pm/);
|
|
}
|
|
}
|
|
|
|
if ($ok) {
|
|
$self->debug("calling $pkg->load()") if $self->{ DEBUG };
|
|
|
|
$factory = eval { $pkg->load($context) };
|
|
$error = '';
|
|
if ($@ || ! $factory) {
|
|
$error = $@ || 'load() returned a false value';
|
|
}
|
|
}
|
|
elsif ($self->{ LOAD_PERL }) {
|
|
# fallback - is it a regular Perl module?
|
|
($file = $module) =~ s|::|/|g;
|
|
eval { require "$file.pm" };
|
|
if ($@) {
|
|
$error = $@;
|
|
}
|
|
else {
|
|
# this is a regular Perl module so the new() constructor
|
|
# isn't expecting a $context reference as the first argument;
|
|
# so we construct a closure which removes it before calling
|
|
# $module->new(@_);
|
|
$factory = sub {
|
|
shift;
|
|
$module->new(@_);
|
|
};
|
|
$error = '';
|
|
}
|
|
}
|
|
|
|
if ($factory) {
|
|
$self->debug("$name => $factory") if $self->{ DEBUG };
|
|
return $factory;
|
|
}
|
|
elsif ($error) {
|
|
return $self->{ TOLERANT }
|
|
? (undef, Template::Constants::STATUS_DECLINED)
|
|
: ($error, Template::Constants::STATUS_ERROR);
|
|
}
|
|
else {
|
|
return (undef, Template::Constants::STATUS_DECLINED);
|
|
}
|
|
}
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
# _dump()
|
|
#
|
|
# Debug method which constructs and returns text representing the current
|
|
# state of the object.
|
|
#------------------------------------------------------------------------
|
|
|
|
sub _dump {
|
|
my $self = shift;
|
|
my $output = "[Template::Plugins] {\n";
|
|
my $format = " %-16s => %s\n";
|
|
my $key;
|
|
|
|
foreach $key (qw( TOLERANT LOAD_PERL )) {
|
|
$output .= sprintf($format, $key, $self->{ $key });
|
|
}
|
|
|
|
local $" = ', ';
|
|
my $fkeys = join(", ", keys %{$self->{ FACTORY }});
|
|
my $plugins = $self->{ PLUGINS };
|
|
$plugins = join('', map {
|
|
sprintf(" $format", $_, $plugins->{ $_ });
|
|
} keys %$plugins);
|
|
$plugins = "{\n$plugins }";
|
|
|
|
$output .= sprintf($format, 'PLUGIN_BASE', "[ @{ $self->{ PLUGIN_BASE } } ]");
|
|
$output .= sprintf($format, 'PLUGINS', $plugins);
|
|
$output .= sprintf($format, 'FACTORY', $fkeys);
|
|
$output .= '}';
|
|
return $output;
|
|
}
|
|
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
Template::Plugins - Plugin provider module
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Template::Plugins;
|
|
|
|
$plugin_provider = Template::Plugins->new(\%options);
|
|
|
|
($plugin, $error) = $plugin_provider->fetch($name, @args);
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
The C<Template::Plugins> module defines a provider class which can be used
|
|
to load and instantiate Template Toolkit plugin modules.
|
|
|
|
=head1 METHODS
|
|
|
|
=head2 new(\%params)
|
|
|
|
Constructor method which instantiates and returns a reference to a
|
|
C<Template::Plugins> object. A reference to a hash array of configuration
|
|
items may be passed as a parameter. These are described below.
|
|
|
|
Note that the L<Template> front-end module creates a C<Template::Plugins>
|
|
provider, passing all configuration items. Thus, the examples shown
|
|
below in the form:
|
|
|
|
$plugprov = Template::Plugins->new({
|
|
PLUGIN_BASE => 'MyTemplate::Plugin',
|
|
LOAD_PERL => 1,
|
|
...
|
|
});
|
|
|
|
can also be used via the L<Template> module as:
|
|
|
|
$ttengine = Template->new({
|
|
PLUGIN_BASE => 'MyTemplate::Plugin',
|
|
LOAD_PERL => 1,
|
|
...
|
|
});
|
|
|
|
as well as the more explicit form of:
|
|
|
|
$plugprov = Template::Plugins->new({
|
|
PLUGIN_BASE => 'MyTemplate::Plugin',
|
|
LOAD_PERL => 1,
|
|
...
|
|
});
|
|
|
|
$ttengine = Template->new({
|
|
LOAD_PLUGINS => [ $plugprov ],
|
|
});
|
|
|
|
=head2 fetch($name, @args)
|
|
|
|
Called to request that a plugin of a given name be provided. The relevant
|
|
module is first loaded (if necessary) and the
|
|
L<load()|Template::Plugin#load()> class method called to return the factory
|
|
class name (usually the same package name) or a factory object (a prototype).
|
|
The L<new()|Template::Plugin#new()> method is then called as a class or object
|
|
method against the factory, passing all remaining parameters.
|
|
|
|
Returns a reference to a new plugin object or C<($error, STATUS_ERROR)>
|
|
on error. May also return C<(undef, STATUS_DECLINED)> to decline to
|
|
serve the request. If C<TOLERANT> is set then all errors will be
|
|
returned as declines.
|
|
|
|
=head1 CONFIGURATION OPTIONS
|
|
|
|
The following list summarises the configuration options that can be provided
|
|
to the C<Template::Plugins> L<new()> constructor. Please consult
|
|
L<Template::Manual::Config> for further details and examples of each
|
|
configuration option in use.
|
|
|
|
=head2 PLUGINS
|
|
|
|
The L<PLUGINS|Template::Manual::Config#PLUGINS> option can be used to provide
|
|
a reference to a hash array that maps plugin names to Perl module names.
|
|
|
|
my $plugins = Template::Plugins->new({
|
|
PLUGINS => {
|
|
cgi => 'MyOrg::Template::Plugin::CGI',
|
|
foo => 'MyOrg::Template::Plugin::Foo',
|
|
bar => 'MyOrg::Template::Plugin::Bar',
|
|
},
|
|
});
|
|
|
|
=head2 PLUGIN_BASE
|
|
|
|
If a plugin is not defined in the L<PLUGINS|Template::Manual::Config#PLUGINS>
|
|
hash then the L<PLUGIN_BASE|Template::Manual::Config#PLUGIN_BASE> is used to
|
|
attempt to construct a correct Perl module name which can be successfully
|
|
loaded.
|
|
|
|
# single value PLUGIN_BASE
|
|
my $plugins = Template::Plugins->new({
|
|
PLUGIN_BASE => 'MyOrg::Template::Plugin',
|
|
});
|
|
|
|
# multiple value PLUGIN_BASE
|
|
my $plugins = Template::Plugins->new({
|
|
PLUGIN_BASE => [ 'MyOrg::Template::Plugin',
|
|
'YourOrg::Template::Plugin' ],
|
|
});
|
|
|
|
=head2 LOAD_PERL
|
|
|
|
The L<LOAD_PERL|Template::Manual::Config#LOAD_PERL> option can be set to allow
|
|
you to load regular Perl modules (i.e. those that don't reside in the
|
|
C<Template::Plugin> or another user-defined namespace) as plugins.
|
|
|
|
If a plugin cannot be loaded using the
|
|
L<PLUGINS|Template::Manual::Config#PLUGINS> or
|
|
L<PLUGIN_BASE|Template::Manual::Config#PLUGIN_BASE> approaches then,
|
|
if the L<LOAD_PERL|Template::Manual::Config#LOAD_PERL> is set, the
|
|
provider will make a final attempt to load the module without prepending any
|
|
prefix to the module path.
|
|
|
|
Unlike regular plugins, modules loaded using L<LOAD_PERL|Template::Manual::Config#LOAD_PERL>
|
|
do not receive a L<Template::Context> reference as the first argument to the
|
|
C<new()> constructor method.
|
|
|
|
=head2 TOLERANT
|
|
|
|
The L<TOLERANT|Template::Manual::Config#TOLERANT> flag can be set to indicate
|
|
that the C<Template::Plugins> module should ignore any errors encountered while
|
|
loading a plugin and instead return C<STATUS_DECLINED>.
|
|
|
|
=head2 DEBUG
|
|
|
|
The L<DEBUG|Template::Manual::Config#DEBUG> option can be used to enable
|
|
debugging messages for the C<Template::Plugins> module by setting it to
|
|
include the C<DEBUG_PLUGINS> value.
|
|
|
|
use Template::Constants qw( :debug );
|
|
|
|
my $template = Template->new({
|
|
DEBUG => DEBUG_FILTERS | DEBUG_PLUGINS,
|
|
});
|
|
|
|
=head1 TEMPLATE TOOLKIT PLUGINS
|
|
|
|
Please see L<Template::Manual::Plugins> For a complete list of all the plugin
|
|
modules distributed with the Template Toolkit.
|
|
|
|
=head1 AUTHOR
|
|
|
|
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
|
|
|
|
This module is free software; you can redistribute it and/or
|
|
modify it under the same terms as Perl itself.
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<Template::Manual::Plugins>, L<Template::Plugin>, L<Template::Context>, L<Template>.
|
|
|
|
=cut
|
|
|
|
# Local Variables:
|
|
# mode: perl
|
|
# perl-indent-level: 4
|
|
# indent-tabs-mode: nil
|
|
# End:
|
|
#
|
|
# vim: expandtab shiftwidth=4:
|