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

USING PLUGINS TO EXTEND FUNCTIONALITY

As we've already shown, it is possible to bind Perl data and functions to template variables when creating dynamic content via a CGI script or Apache/mod_perl process. The Template Toolkit also supports a plugin interface which allows you define such additional data and/or functionality in a separate module and then load and use it as required with the USE directive.

The main benefit to this approach is that you can load the extension into any template document, even those that are processed "statically" by tpage or ttree. You don't need to write a Perl wrapper to explicitly load the module and make it available via the stash.

Let's demonstrate this principle using the DBI plugin written by Simon Matthews <sam@knowledgepool.com>. You can create this template in your 'src' directory and process it using ttree to see the results. Of course, this example relies on the existence of the appropriate SQL database but you should be able to adapt it to your own resources, or at least use it as a demonstrative example of what's possible.

    [% INCLUDE header
       title = 'User Info'
    %]
    
    [% USE DBI('dbi:mSQL:mydbname') %]
    
    <table border=0 width="100%">
    <tr>
      <th>User ID</th> 
      <th>Name</th>  
      <th>Email</th>
    </tr>
    
    [% FOREACH user = DBI.query('SELECT * FROM user ORDER BY id') %]
    <tr>
      <td>[% user.id %]</td> 
      <td>[% user.name %]</td> 
      <td>[% user.email %]</td>
    </tr>
    [% END %]
    
    </table>
    
    [% INCLUDE footer %]

A plugin is simply a Perl module in a known location and conforming to a known standard such that the Template Toolkit can find and load it automatically. You can create your own plugin by inheriting from the Template::Plugin module.

Here's an example which defines some data items ('foo' and 'people') and also an object method ('bar'). We'll call the plugin 'FooBar' for want of a better name and create it in the 'MyOrg::Template::Plugin::FooBar' package. We've added a 'MyOrg' to the regular 'Template::Plugin::*' package to avoid any conflict with existing plugins.

You can create a module stub using the Perl utlity h2xs:

    h2xs -A -X -n MyOrg::Template::Plugin::FooBar

This will create a directory structure representing the package name along with a set of files comprising your new module. You can then edit FooBar.pm to look something like this:

    package MyOrg::Template::Plugin::FooBar;

    use Template::Plugin;
    use vars qw( $VERSION @ISA );

    @ISA = qw( Template::Plugin );
    $VERSION = 1.23;

    sub new {
        my ($class, $context, @params) = @_;

        bless {
            _CONTEXT => $context,
            foo      => 25,
            people   => [ 'tom', 'dick', 'harry' ],
        }, $class;
    }

    sub bar {
        my ($self, @params) = @_;
        # ...do something...    
        return $some_value;
    }

The plugin constructor new() receives the class name as the first parameter, as is usual in Perl, followed by a reference to something called a Template::Context object. You don't need to worry too much about this at the moment, other than to know that it's the main processing object for the Template Toolkit. It provides access to the functionality of the processor and some plugins may need to communicate with it. We don't at this stage, but we'll save the reference anyway in the '_CONTEXT' member. The leading underscore is a convention which indicates that this item is private and the Template Toolkit won't attempt to access this member. The other members defined, 'foo' and 'people' are regular data items which will be made available to templates using this plugin. Following the context reference are passed any additional parameters specified with the USE directive, such as the data source parameter, 'dbi:mSQL:mydbname', that we used in the earlier DBI example.

If you used h2xs to create the module stub then you'll already have a Makefile.PL and you can incite the familiar incantation to build and install it. Don't forget to add some tests to test.pl!

    perl Makefile.PL
    make
    make test
    make install

If you don't or can't install it to the regular place for your Perl modules (perhaps because you don't have the required privileges) then you can set the PERL5LIB environment variable to specify another location. If you're using ttree then you can add the following line to your configuration file instead. This has the effect of add '/path/to/modules' to the @INC array to a similar end.

$HOME/.ttreerc: perl5lib = /path/to/modules

One further configuration item must be added to inform the toolkit of the new package name we have adopted for our plugins:

$HOME/.ttreerc: plugin_base = 'MyOrg::Template::Plugin'

If you're writing Perl code to control the Template modules directly, then this value can be passed as a configuration parameter when you create the module.

    use Template;

    my $template = Template->new({ 
        PLUGIN_BASE => 'MyOrg::Template::Plugin' 
    });

Now we can create a template which uses this plugin:

    [% INCLUDE header
       title = 'FooBar Plugin Test'
    %]

    [% USE FooBar %]

    Some values available from this plugin:
      [% FooBar.foo %] [% FooBar.bar %]

    The users defined in the 'people' list:
    [% FOREACH uid = FooBar.people %]
      * [% uid %]
    [% END %]

    [% INCLUDE footer %]

The 'foo', 'bar' and 'people' items of the FooBar plugin are automatically resolved to the appropriate data items or method calls on the underlying object.

Using this approach, it is possible to create application functionality in a single module which can then be loaded and used on demand in any template. The simple interface between template directives and plugin objects allows complex, dynamic content to be built from a few simple template documents without knowing anything about the underlying implementation.