Combination of data and fixed text to produce data
Boilerplate text containing tags that are filled in
Multiple copies with different data
Form letters
Dear [% name %],
According to our records you owe us GBP [% amount %]. Please pay before [% due %] or we will send the boys round.
Regards.
Values within [% ... %] tags will be expanded
Simple example of a template directive
Basis of the direct marketing industry
The Template Toolkit is a powerful templating engine written in Perl
Command line tools available
Other templating engines are available
Text::Template
HTML::Template
HTML::Mason
We won't cover those
Not specialised for web use
Use the same tool for all of your templating needs
Templating language is not Perl
Encourages separation of business logic and display logic
Don't need to be a Perl programmer to write templates
Badgers are cool
Remember the form letter template
Dear [% name %],
According to our records you owe us GBP [% amount %]. Please pay before [% due %] or we will send the boys round.
Regards.
Process it using tpage
tpage --define name='Mr Cross' --define amount='100' \
--define due='1st April' letter.tt
Writes to STDOUT
Dear Mr Cross,
According to our records you owe us GBP 100.
Please pay before 1st April or we will send the boys round.
Regards.
We can use the FORMAT plugin to ensure that numbers are displayed to two decimal places
[% USE money=format('%.2f') -%]
Dear [% name %],
According to our records you owe us GBP [% money(amount) %].
Please pay before [% due %] or we will send the boys round. Regards.
We'll see more plugins later
Note: [% ... -%] - removes trailing whitespace
Typing in every variable each time doesn't save us much time
What if the data was in a file?
name : amount : due Mr Cross : 10 : 1st April Mr Smith : 20 : 1st March Mr Jones : 50 : 1st February
Use the datafile plugin to read data from files
[% USE money = format('%.2f') -%]
[% USE debtors = datafile(file) -%]
[% FOREACH debtor = debtors %]
Dear [% debtor.name %],
According to our records you owe us GBP [% money(debtor.amount) %]
Please pay before [% debtor.date %] or we will send the boys
round.
Regards.
[%- END %]
debtors variable is set from each row in file
Field names taken from the first row of data
Out output now has all letters running together
Use form feed character to separate them when printing
[% FOREACH debtor = debtors -%] Blah blah blah... [% UNLESS loop.last -%] ^L [%- END %] [%- END %]
"unless" works as it does in Perl
"loop" is a special TT variable
The template doesn't get much more complex if the data is stored in a database
Use the DBI plugin
[% USE money = format('%.2f') -%]
[% USE DBI(database = 'dbi:mysql:accounts'
username = 'acc_user'
password = 'sekrit') -%]
[% FOREACH debtor = DBI.query('select name, amount, due
from debtors') -%]
Dear [% debtor.name %],
Blah blah blah...
[%- END %]
Most of the template is unchanged
Use the date plugin to format dates and times
[% USE date(format = '%d %B') -%] ... and then later... [% date.format(debtor.due) -%]
Input date either epoch seconds of h:m:s d/m/y
select name, amount
date_format(due, "%h:%i:%s %d/%m/%Y")
as due
from debtors
Use XPath if your data is in XML
[% USE debtors = XML.XPath(file) -%]
[% FOREACH debtor = debtors.findnodes('/debtors/debtor') -%]
Dear [% debtor.findvalue('name') %],
Blah blah blah...
[%- END %]
Other XML processors are available
Templating can be expressed like this
Data + Fixed Text = Output
Form Letters look like this
Alternative Data + Fixed Text = Alternative Output
We can also do this
Data + Alternative Fixed Text = Alternative Output
Different views of the same data
Perl objects work really well with TT
Use plugins
TT plugin is a wrapper around external Perl code
Invoice example
use My::Invoice;
my $inv = My::Invoice->new($id);
print $inv->date, $inv->customer->name;
From My::Invoice create a Template Plugin module
Template::Plugin::My::Invoice
Simple piece of boilerplate code
[% USE inv = my.invoice(id) -%] Date: [% inv.date %] etc...
tpage --define inv=123 inv.tt > inv.txt
[% USE inv = my.invoice(id) -%]
INVOICE [% inv.id | format('%05d') %]
Date: [% inv.date %]
To: [% inv.client.name %]
[% FOREACH addr_line = inv.client.address.split('\n') -%]
[% addr_line %]
[% END -%]
[% FOREACH line = inv.lines.sort('line_no') -%]
[% total = total + line.price -%]
[% line.description | format('%-40s') %] GBP [% line.price | format('%.2f') %]
[% END %]
[% 'Total:' | format('%40s') %] GBP [% total | format('%.2f') %]
[% USE inv = my.invoice(id) -%]
<html>
<head><title>Invoice [% inv.id | format('%05d') %]</title></head>
<body>
<h1>INVOICE [% inv.id | format('%05d') %]</h1>
<table>
<tr>
<td>Date:</td><td>[% inv.invdate %]</td>
</tr>
<tr><td colspan="2"> </td></tr>
<tr>
<td valign="top">To:</td>
<td>[% inv.client.name;
inv.client.address.split('\n').join('<br>') -%]
</td>
</tr>
</table>
<table>
[% FOREACH line = inv.lines.sort('line_no') -%]
<tr>
<td>[% line.description; total = total + line.price %]</td>
<td>GBP [% line.price | format('%.2f') %]</td>
</tr>
[% END %]
<tr>
<td align="right">Total:</td>
<td>EUR [% total | format('%.2f') %]</td>
</tr>
</table>
</body>
</html>
perldoc Template::Manual (and many more)
Perl Template Toolkit (the badger book)