A Wee Bit of Fun with Template::Declare

Sep 1, 2011 / By Yanick Champoux

Tags: ,

As I was crafting my Dancer presentation for Summercamp 2011, I noticed that there wasn’t a Dancer template for Template::Declare.

Well, now there’s one.

While I was at it, I also played with defining templates in their own files and then importing them in a Template::Declare class (for more involved templates, I like to have one template per file, to keep the strain on my brain to a minimum). With the magical help of Perl’s shared directories, it proved to be quite easy.

What I did was to use the auto directory associated with the template’s module. For example, for the template module My::Templates, I dropped the individual templates under the directory lib/auto/My/Templates:

lib/
|- auto
|  ` My
|     ` Templates
|         |- simple.td
|         ` sub
|            ` foo.td
`- My
     ` Templates.pm

Each template file only needs to have the inner template definition. For example, simple.td looks like:

html {
    body {
        h1 { 'Hello ' . $args->{name} }
    }
}

Because I’m piggy-backing on Perl’s shared directories convention, harvesting those template files is a breeze thanks to File::SharedDir.

package My::Templates;

use Template::Declare::Tags;
use base 'Template::Declare';

use File::ShareDir qw/ module_dir /;
use Path::Class;

my $base =  dir( module_dir( __PACKAGE__ ) );

$base->recurse( callback => &import_template );

sub import_template {
    my $file = shift;

    return unless $file->isa( 'Path::Class::File' )
        and $file =~ /.td$/;

    my $content = $file->slurp;

    my $name = $file->relative( $base );

    $name =~ s/.td$//;

    eval >>"END_CODE";
template "$name" => sub {
    my ( $self, $args ) = @_;

#line 1 $f
    $content;
}
END_CODE

    die $@ if $@;
}

1;

As you can see, the code is pretty straight-forward and fairly minimal. With module_dir(), I grab all the files (recursively, natch) with a .td extension within the auto directory of the module and use them to build the equivalent ‘template $name => sub { ... }‘ declarations. The only bit of cleverness, if it can be called thus, is the “#line 1 $f” preprocessing command, which will cause compilation errors to be reported at the right place in the .td template file instead than within My/Templates.pm.

In this example, I set the templates in such a way that the arguments must be passed as an hash ref and are made accessible to the template via $args, but it could easily be modified to please any other convention/preference.

And that’s all there is to it. The template module can be used like any other Template::Declare module, with no apparent difference for the outside world:

#!/usr/bin/perl

use strict;
use warnings;

use My::Templates;

Template::Declare->init( dispatch_to => [ 'My::Templates' ] );
print Template::Declare->show( 'simple', { name => 'world' }  );
print Template::Declare->show( 'sub/foo' );

Nice. Now, I should probably stop plucking the alpaca’s eyebrows and return to work on my slides…

Leave a Reply

  • (will not be published)

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>