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.