The 2002 Perl Advent Calendar
[about] | [archives] | [contact] | [home]

On the 19th day of Advent my True Language brought to me..
Inline::TT

One of the hardest things to do when you're developing scripts is to keep your presentation separate from your programming logic. This is the problem where your script gets intertwined with a hundred or so little print statements and when your manager calls you up and informs you that the output of your program must change, you have to hunt though your code trying to work out what does what. Anyone who's ever tried to do this will tell you just how annoying and time consuming this is.

The ideal solution would of course to be to utilise one of the hundred or so templating solutions out there to generate all your output, thus keeping it in a separate place. And indeed, that's what many people creating web pages do do, with much success.

However, it's often just too much trouble when you're creating a simple script to do a simple job like reporting on the number of files modified in a filesystem. Setting up separate files that contain the templates, keeping track of them and making sure they're installed on all the machines you install the scripts on in a place where each script (which you're loath to modify on a per machine basis) can find them is hard to do.

This is where Inline::TT comes in. It allows you to define your templates Inline in the actual code itself. Now, this may sound like we've come around full circle, but it provides enough of a distinction that the presentation code is separated out (and made reusable.)

I can't believe I've managed to get to the 19th of the month, and this is the first "Hello World" example of the year. Anyway, here it is, the wonderful program that prints out "Hello World", this time making use of Inline::TT.

  #!/usr/bin/perl
  # turn on perl's safety features
  use strict;
  use warnings;
  # use Inline with the TT module.  Note that this is
  # not 'use Inline::TT' as Inline::TT is a plugin to Inline
  use Inline 'TT' ;
  # call the block defined below
  print hello()
  __END__
  __TT__
  [% BLOCK hello -%]
  Hello World!
  [% END %]

This, rather predictably, prints out the expected greeting

  [mark@gan] perl hellow 
  Hello World!

So what happened? Well, the TT section of the source code was compiled into perl code by the Template Toolkit and any blocks it found in there were mapped to functions in the same namespace as before. So any block you declare in the __TT__ section you can call directly from Perl. Inline supports a range of other syntaxes for defining inline code if you don't like this "lumping all the code together at the end" approach, all of which are well documented in the manual.

Like all Inline programs, the first time you run a Inline::TT the code runs slower than normal. That's because it's extracting out the inline data (in this case the template code) and compiling it to Perl code which it places in the _Inline directory. The second time you run the code it checks to see if the original code has changed at all, and if it hasn't it uses the version in _Inline.

  bash-2.05b$ ls -l
  total 8
  drwxr-sr-x    3 mark     mark         4096 Dec 16 18:51 _Inline
  -rw-r--r--    1 mark     mark          282 Dec 16 18:39 hellow

When you're actually installing these scripts on a box it's a good idea to create a .Inline directory in the directory that Inline can use to store the files in instead. If you're installing the script to somewhere that normal users can't write to, you'll need to be logged in as a privileged user (probably root) the first time you run the script in order to create the files.

 [root@gan] cp hellow /usr/local/bin
 [root@gan] mkdir /usr/local/bin/.Inline
 [root@gan] hellow

Right, now we've got that out the way, let's look at a few things that we can do now we've got the power of the Template Toolkit at our fingertips. The most obvious thing we can do is do conditional processing in the template

  print a_warning(text      => "You are running low on pie",
                  important => 1);
  [% BLOCK a_warning;
    # create bar if it was important
    IF important;
     "*" | repeat(60);
    END;
    # print the message with a space and a newline
    %] [% text %]
    %]
    # create bar if it was important
    IF important;
     "*" | repeat(60);
    END;
  END %]
  ******************************************************
   You are running low on pie
  ******************************************************

This example made use of the IF directive, but it also demonstrated the FILTER directive, in this case utilised to repeat the astrix sixty times in each example. The other obvious thing we can do that's normally messy in Perl code is create little templates for formatting data structures properly.

  print list_template(items => ["foo","bar","bob"])
  [% BLOCK list_template -%]
   [% FOREACH item = items %] * [% item %]
  [% END; END %]
   * foo
   * bar
   * bob

It's possible using the Template Toolkit's view system to make mappings between blocks and data structures so you can dump any data structure (including objects) out and have it automatically converted into the correct format recursively.

Of course, just because these little templates are accessible to Perl, there's nothing to stop you using them in other templates

  print important_items(vital => \@important );
  [% BLOCK important_items -%]
  The three most important items are:
  [% INCLUDE list_template items = vital.first(3) %]
  These must be delt with first.
  [% END %]
  The three most important items are:   
    * Take over the world
    * Do tomorrow's advent calendar
    * Buy pie at the supermarket
  These must be delt with first

It's perfectly simple to make use of the numerous plugins that are available for the Template Toolkit. One of the most useful plugins for printing output to the terminal is the plugin module that allows Template Toolkit to use the exceedingly useful Text::AutoFormat module to intelligently reformat text that is filtered though it.

 [% BLOCK hello;
    USE autoformat;
    FILTER autoformat(left => 5, right => 30) -%]
 Hello, and good tidings
 to you on this day.  I hope you have been
 keeping in good health.
 [% END; END %]
     Hello, and good tidings
     to you on this day. I
     hope you have been
     keeping good health.

This, of course, just is the tip of the iceberg of what the Template Toolkit can do but already we can see how it can be utilised in ways that would potentially be very messy in normal Perl.

  • The Template module documentation
  • Template Toolkit Homepage
  • The Inline module documentation
  • Inline Homepage