Over the last couple of months we have been looking at using the Template Toolkit from the command line using the utilities “tpage” and “ttree”. None of the examples that we have looked at have involved us writing a single line of of Perl code.
That’s fine for simple projects, but as things get more complex it makes sense to do a lot of the heavy lifting in something a bit more powerful than the Template Toolkit’s presentation language. The TT language was never designed to be a general purpose programming language. It’s a specialised language for controlling the presentation of data.
As the Template Toolkit is written in Perl, it’s easiest to use it from within Perl programs. So that’s what we will use in this article.
Having decided that we are going to split the processing between a Perl program and a template the next thing we need to do is to decide exactly where to make this division. In my experience this is usually a pretty simple decision to make. Most programs fall quite neatly into one section that gathers all of the required data and another which presents the data to the user.
If you’re having trouble deciding where to make this split then it’s often useful to consider an alternative display medium for your data. For example, if you’re building a plain text file consider what you would need to change if you were to build an HTML page containing the same data. The data is exactly the same, it’s just the presentation that has changed. So the bits of processing that need to change are the bits that should be in the template.
Using the Template Toolkit from Perl
The main interface to the Template Toolkit from a Perl program is the Template module. Template is an object oriented module, but don’t let that scare you. It’s really very simple to use.
Like all Perl modules, you load the Template module into your program with the “use” statement like this.
1 |
use Template; |
You then need to create a Template processor object using the “new” method. This can be as simple as this:
1 |
my $tt = Template->new; |
But there is also an optional parameter to “new”. We’ll look at that a bit later on.
To use the Template processor object, we call the “process” method, passing it the name of a template to process.
1 |
$tt->process('template.tt') or die $tt->error; |
The template processor looks for the template file in the current directory (we’ll see how to change that later) and processes it in exactly the same way as “tpage” or “ttree” would. The results of processing the template are written to STDOUT (but we’ll see how to change that very soon).
Notice that if there is any problem processing the template then “process” returns a false value. We can check for that and use the “error” method to produce a suitable error message as we terminate the program.
Passing Variables to the Template
Of course most templates need some kind of input in order to do anything useful. With “tpage” and “ttree” we used the “–define var=value” options to pass variables into the template. There must be a way to do something similar from Perl.
And, of course, there is. The “process” method takes an optional second parameter which defines the variables that the template will use. This parameter is a reference to hash. The keys of the hash are the names of the variables and the values are the associated data. You can therefore define variables like this
1 2 3 4 |
my %vars = (name => 'Dave Cross', email => 'dave@example.com'); $tt->process('template.tt', \%vars) or die $tt->error; |
This code defines two variables called “name” and “email” which can be referenced within the template. You don’t have to stop at scalar values like the ones seen here. You can build any kind of complex data structure.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
my %vars = (invoice => { number => '101', date => '1st April 2004', client => 'Example Inc.', addr => '1000 Example Road, Exampleton', lines => [ { desc => 'Reversing polarity', price => '1000' }, { desc => 'Regeneration care', price => '2000' } ] }); |
This example shows a complex, multi-levelled data structure that models an invoice. The “invoice” variable is a hash and its parts can be accessed within a template as, for example, “invoice.number” and “invoice.date”. The value for “invoice.lines” is an array, so you can access the individual items as, for example, “invoice.lines.0.desc” or you could use it in a FOREACH loop. We’ll see more of this example later, but if you want more information about using complex data structures in Perl, see the “perlreftut” and “perldsc” manual pages.
Most Perl programs that use the Template Toolkit will spend a large part of their time building an appropriate data structure to pass to “process”.
Controlling Output
As I mentioned previously, “process” sends its results to STDOUT by default. You can change by using its optional third parameter. This can take a number of different types of value. The most common of them is a string which is assumed to be the name of a file. The output from the template is written to this file.
Another option is to pass a reference to a scalar variable. In this case. the output from the template is put into that variable. This is useful if you want to post-process the output in some way.
There are a few other more esoteric alternatives. For details of these see the documentation that comes with the Template Toolkit.
More Options
Last month we saw some other options that “ttree” uses to control exactly how the template is processed. We can do the same thing with the Template module. In fact this method gives us even more options. The processing options are set up when you create a template processor object with “new”. The “new” method take an optional argument which is a reference to a hash of options.
1 2 3 4 5 6 |
my %options = (INCLUDE_PATH => './lib', OUTPUT_PATH => './out', PRE_PROCESS => 'header.tt, POST_PROCESS => 'footer.tt'); my $tt = Template->new(\%options); |
In this code we set four options. INCLUDE_PATH defines a directory where the template processor will look for any templates. If you want more than one directory then set this option to a reference to an array of directories.
1 |
INCLUDE_PATH => [ '.', './lib', '/opt/templates' ] |
OUTPUT_PATH defines the directory where any output files will be written.
PRE_PROCESS and POST_PROCESS define templates that will always be processed before and after and templates passed to “process”. This can be useful if, as in this example, you want to add a header and footer to every template. I often use PRE_PROCESS to process library templates that contain configuration data. Both of these values can also be set to an array reference if you want to pre- or post-process multiple templates.
1 |
PRE_PROCESS => [ 'config.tt', 'header.tt' ] |
Creating Invoices
For this month’s example we’ll look at creating invoices. Assume that we have details of invoices in a database and that we have a Perl module called “Invoice” that gives us access to the invoice data. See the boxouts on “The Invoice database” and “Invoice.pm” for more details on how these are set up.
Here’s a simple text template for an invoice
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
INVOICE [% invoice.id | format('%05d') %] Date: [% invoice.invdate %] To: [% invoice.client.name %] [% FOREACH addr_line = invoice.client.address.split('\n') -%] [% addr_line %] [% END -%] [% FOREACH line = invoice.lines.sort('line_no') -%] [% line.description | format('%-40s') %] £[% line.price | format('%.2f') %] [% END %] [% 'Total:' | format('%40s') %] £[% total | format('%.2f') %] |
This template expects an Invoice object to be passed to it in the variable “invoice” and also the total value of the invoice in the variable “total”. Most of the data that it needs is in the invoice object and it can access this by calling the object’s various methods using the dot notation. Notice that these dots can be strung together to create expressions like “invoice.client.name” to get the name of the client associated with the invoice.
We’ve also made good use of the “format” plugin. We use it to ensure that the invoice number always has five digits and to ensure that the prices all have two decimal points, but we also use it to ensure that the descriptions are all padded to forty characters and therefore the price column lines up correctly. All of these considerations are about the presentation of the data, so they quite rightly belong in the template.
The Invoice Program
Here’s the program that calls the invoice template.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!/usr/bin/perl use strict; use warnings; use Template; use Invoice; my $id = shift || die "No invoice number given\n"; my $fmt = shift || 'txt'; my $inv = Invoice->retrieve($id) || die "Invoice $id not found\n"; my $total; $total += $_->price foreach $inv->lines; my $tt = Template->new; my %vars = ( invoice => $inv, total => $total ); $tt->process("$fmt.tt", \%vars, "inv$id.$fmt") or die $tt->error; |
The program starts by loading the “strict” and “warnings” modules – which no Perl program should be without. It then loads the Template and Invoice modules which we will specifically need for this application.
The program expects two arguments. The first is the number of the invoice to process. This is mandatory and the program dies if it isn’t given. The second argument is optional and defines an output format. The default format is ‘txt’.
The program then retrieves the invoice from the database using a method that Class::DBI has created for us in our Invoice class. Having successfully retrieved the invoice we calculate the total by adding togther the price fields from all of the lines in the invoice. Again, the “lines” method was automatically created by Class::DBI.
Then all that’s left to do is to create our template processor object and process the template. We use three arguments to the “process” method. The first argument is a string containing the name of the template to use. For the text format we’ll use “txt.tt”. The second argument is a hash containing the variables. The third is the name of the output file. For text invoices, the name will be something like “inv1.txt”.
Running this program with some simple test data gives the following output.
1 2 3 4 5 6 7 8 9 10 11 12 |
INVOICE 00001 Date: 2004-05-01 To: The Doctor The TARDIS Somewhere in space and time Reversing polarity £1000.00 Regeneration care £2000.00 Total: £3000.00 |
But what happens when we decide that we also want to put out invoices on our web site? Or that we want to create RTF versions of invoices that can be edited in OpenOffice.org?
This is where the work of separating the data collection from the presentation really pays off. By just creating a new template, we can create a new view of our data very easily. Here is an example HTML template.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<html> <head> <title>Invoice [% invoice.id | format('%05d') %]</title> </head> <body> <h1>INVOICE [% invoice.id | format('%05d') %]</h1> <table> <tr> <td>Date:</td><td>[% invoice.invdate %]</td> </tr> <tr><td colspan="2"> </td></tr> <tr> <td valign="top">To:</td> <td>[% invoice.client.name; invoice.client.address.split('\n').join('<br>') -%] </td> </tr> </table> <table> [% FOREACH line = invoice.lines.sort('line_no') -%] <tr> <td>[% line.description %]</td> <td>£[% line.price | format('%.2f') %]</td> </tr> [% END %] <tr> <td align="right">Total:</td> <td>£[% total | format('%.2f') %]</td> </tr> </table> </body> </html> |
Basically, this does the same things as the text template, but it produces HTML instead of plain text. Instead of worrying about lining up the text columns, we have used HTML tables. Instead of displaying a pound sign, we use the £ HTML entity. Simply put this template into html.tt and our existing script can be used unchanged to create HTML pages. The fact that we find it so easy to create another view of the data means that we must have got the separation of processing and presentation just about right.
The Invoice database
We’ll assume that we have a very simple database that stores details of our invoices. The database has three tables which contain data about clients, invoices and invoice lines. Let’s look at the tables one at a time.
The Client table
Each of our clients has one row of data in the client table. Each row contains the client’s name and address togther with a unique identifier for the client. In MySQL, the definition of the table looks like this
1 2 3 4 5 6 |
CREATE TABLE client ( id int(11) NOT NULL, name varchar(50) default NULL, address varchar(250) default NULL, PRIMARY KEY (id) ); |
The invoice table
Each invoice that we send will create one new row in the invoice table. It contains the invoice number and date along with the id of the client that the invoice was sent to. The table definition looks like this
1 2 3 4 5 6 7 8 |
CREATE TABLE invoice ( id int(11) NOT NULL default '0', invdate date default NULL, client int(11) default NULL, PRIMARY KEY (id), INDEX fk_cli (client), FOREIGN KEY (client) REFERENCES client(id) ); |
Notice that we have also given the table a foreign key which declares that the “client” column in this table is a reference to the “id” column in the client table.
The line table
Within an invoice, we have a number of invoice lines. Each of these represents one of the items that the invoice charges for. An invoice line has a number, a description of the goods or services sold and a price. It also contains the number of the invoice that it belongs to. Here’s the table definition
1 2 3 4 5 6 7 8 9 |
CREATE TABLE line ( invoice int(11) NOT NULL default '0', line_no int(11) NOT NULL default '0', description varchar(250) default NULL, price float(10,2) default NULL, PRIMARY KEY (invoice,line_no), INDEX fk_inv (invoice), FOREIGN KEY (invoice) REFERENCES invoice(id) ); |
Notice that once more we’ve defined a foreign key linking the line table back to the invoice table.
Invoice.pm
When you are creating a Perl application that talks to a database it’s generally a good idea to isolate all of the database interaction in one place. And usually, it makes sense to write a module to handle it all.
Writing Perl modules really isn’t as hard as you might think and there are plenty of tools out there to make it as easy as possible. I have recently started using the module Class::DBI for all of my database work. It’s a wrapper around Perl’s standard database interface module (called DBI) and it’s a very easy way to create very useful classes built around database tables. Here are the contents of my file Invoice.pm which contains all the database code for this application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package Invoice; use Class::DBI::Loader; use Class::DBI::Loader::Relationship; my $loader = Class::DBI::Loader->new(dsn => 'dbi:mysql:invoice:tma2', user => 'invoice', password => 'inv01ce'); my @rels = ( 'a client has invoices', 'an invoice has lines'); $loader->relationship($_) for @rels; 1; |
That’s about a dozen lines of code and when we load it into our program (with “use Invoice”) we’ll get three new classes “Client”, “Invoice” and “Line” which are object-oriented interfaces to our three tables.
The cleverest thing about this tool is that it also understands the relationships between our tables. This means that if we’ve got an Invoice object, it is very easy to get its associated Client and Line objects.
For more information about how this all works, go to https://metacpan.org/ and look for Class::DBI, Class::DBI::Loader and Class::DBI::Loader::Relationship.
Where are parts 1 and 2?
See here
thank you very much
It’s simple and understood
Thanks Dave been battling away at an invoice program for years your article has biven a clear direction for the rewriite. Cheers Terry