1 #============================================================= -*-perl-*-
3 # Template::Tutorial::Datafile
9 # Dave Cross <dave@dave.org.uk>
12 # Copyright (C) 1996-2008 Andy Wardley. All Rights Reserved.
14 # This module is free software; you can redistribute it and/or
15 # modify it under the same terms as Perl itself.
17 #========================================================================
21 Template::Tutorial::Datafile - Creating Data Output Files Using the Template Toolkit
27 =head1 Introducing the Template Toolkit
29 There are a number of Perl modules that are universally
30 recognised as The Right Thing To Use for certain tasks. If you
31 accessed a database without using DBI, pulled data from the WWW
32 without using one of the LWP modules or parsed XML without using
33 XML::Parser or one of its subclasses then you'd run the risk of
34 being shunned by polite Perl society.
36 I believe that the year 2000 saw the emergence of another 'must
37 have' Perl module - the Template Toolkit. I don't think I'm
38 alone in this belief as the Template Toolkit won the 'Best New
39 Module' award at the Perl Conference last summer. Version 2.0 of
40 the Template Toolkit (known as TT2 to its friends) was recently
43 TT2 was designed and written by Andy Wardley E<lt>abw@wardley.orgE<gt>.
44 It was born out of Andy's previous templating module,
45 Text::Metatext, in best Fred Brooks 'plan to throw one away'
46 manner; and aims to be the most useful (or, at least, the most
47 I<used>) Perl templating system.
49 TT2 provides a way to take a file of fixed boilerplate text
50 (the template) and embed variable data within it. One obvious
51 use of this is in the creation of dynamic web pages and this is
52 where a lot of the attention that TT2 has received has been
53 focussed. In this article, I hope to demonstrate that TT2 is
54 just as useful in non-web applications.
56 =head1 Using the Template Toolkit
58 Let's look at how we'd use TT2 to process a simple data file.
59 TT2 is an object oriented Perl module. Having downloaded it from
60 CPAN and installed it in the usual manner, using it in your
61 program is as easy as putting the lines
64 my $tt = Template->new;
66 in your code. The constructor function, C<new>, takes
67 a number of optional parameters which are documented in the
68 copious manual pages that come with the module, but for the
69 purposes of this article we'll keep things as simple as
72 To process the template, you would call the C<process> method
75 $tt->process('my_template', \%data)
78 We pass two parameters to C<process>, the first is the name of
79 the file containing the template to process (in this case,
80 my_template) and the second is a reference to a hash which
81 contains the data items that you want to use in the template. If
82 processing the template gives any kind of error, the program
83 will die with a (hopefully) useful error message.
85 So what kinds of things can go in C<%data>? The answer is just
86 about anything. Here's an example showing data about English
87 Premier League football teams.
89 my @teams = ({ name => 'Man Utd',
100 my %data = ( name => 'English Premier League',
104 This creates three data items which can be accessed within the
105 template, called C<name>, C<season> and C<teams>. Notice that
106 C<teams> is a complex data structure.
108 Here is a template that we might use to process this data.
112 League Name: [% name %]
113 Season : [% season %]
116 [% FOREACH team = teams -%]
117 [% team.name %] [% team.played -%]
118 [% team.won %] [% team.drawn %] [% team.lost %]
121 Running this template with this data gives us the following
126 League Name: English Premier League
133 Hopefully the syntax of the template is simple enough to
134 follow. There are a few points to note.
140 Template processing directives are written using a simple
141 language which is not Perl.
145 The keys of the C<%data> have become the names of the data
146 variables within the template.
150 Template processing directives are surrounded by C<[%> and
155 If these tags are replaced with C<[%-> C<-%]> then the preceding
156 or following linefeed is suppressed.
160 In the C<FOREACH> loop, each element of the C<teams> list was
161 assigned, in turn, to the temporary variable C<team>.
165 Each item assigned to the C<team> variable is a Perl hash.
166 Individual values within the hash are accessed using a dot notation.
170 It's probably the first and last of these points which are the
171 most important. The first point emphasises the separation of the
172 data acquisition logic from the presentation logic. The person
173 creating the presentation template doesn't need to know Perl,
174 they only need to know the data items which will be passed into
177 The last point demonstrates the way that TT2 protects the
178 template designer from the implementation of the data structures.
179 The data objects passed to the template processor can be scalars,
180 arrays, hashes, objects or even subroutines. The template
181 processor will just interpret your data correctly and Do The
182 Right Thing to return the correct value to you. In this example
183 each team was a hash, but in a larger system each team might be
184 an object, in which case C<name>, C<played>, etc. would be accessor
185 methods to the underlying object attributes. No changes would be
186 required to the template as the template processor would realise
187 that it needed to call methods rather than access hash values.
189 =head2 A more complex example
191 Stats about the English Football League are usually presented in
192 a slightly more complex format than the one we used above. A
193 full set of stats will show the number of games that a team has
194 won, lost or drawn, the number of goals scored for and against
195 the team and the number of points that the team therefore has.
196 Teams gain three points for a win and one point for a draw. When
197 teams have the same number of points they are separated by the
198 goal difference, that is the number of goals the team has scored
199 minus the number of team scored against them. To complicate
200 things even further, the games won, drawn and lost and the goals
201 for and against are often split between home and away games.
203 Therefore if you have a data source which lists the team name
204 togther with the games won, drawn and lost and the goals for and
205 against split into home and away (a total of eleven data items)
206 you can calculate all of the other items (goal difference,
207 points awarded and even position in the league). Let's take such
208 a file, but we'll only look at the top three teams. It will look
211 Man Utd,7,1,0,26,4,5,2,1,15,6
212 Arsenal,7,1,0,17,4,2,3,3,7,9
213 Leicester,4,3,1,10,8,4,2,2,7,4
215 A simple script to read this data into an array of hashes will
216 look something like this (I've simplified the names of the data
217 columns - w, d, and l are games won, drawn and lost and f and a
218 are goals scored for and against; h and a at the front of a data
219 item name indicates whether it's a home or away statistic):
221 my @cols = qw(name hw hd hl hf ha aw ad al af aa);
229 @team{@cols} = split /,/;
234 We can then go thru the teams again and calculate all of the
238 $_->{w} = $_->{hw} + $_->{aw};
239 $_->{d} = $_->{hd} + $_->{ad};
240 $_->{l} = $_->{hl} + $_->{al};
242 $_->{pl} = $_->{w} + $_->{d} + $_->{l};
244 $_->{f} = $_->{hf} + $_->{af};
245 $_->{a} = $_->{ha} + $_->{aa};
247 $_->{gd} = $_->{f} - $_->{a};
248 $_->{pt} = (3 * $_->{w}) + $_->{d};
251 And then produce a list sorted in descending order:
254 $b->{pt} <=> $b->{pt} || $b->{gd} <=> $a->{gd}
257 And finally add the league position data item:
259 $teams[$_]->{pos} = $_ + 1
260 foreach 0 .. $#teams;
262 Having pulled all of our data into an internal data structure
263 we can start to produce output using out templates. A template
264 to create a CSV file containing the data split between home and
265 away stats would look like this:
267 [% FOREACH team = teams -%]
268 [% team.pos %],[% team.name %],[% team.pl %],[% team.hw %],
269 [%- team.hd %],[% team.hl %],[% team.hf %],[% team.ha %],
270 [%- team.aw %],[% team.ad %],[% team.al %],[% team.af %],
271 [%- team.aa %],[% team.gd %],[% team.pt %]
274 And processing it like this:
276 $tt->process('split.tt', { teams => \@teams }, 'split.csv')
279 produces the following output:
281 1,Man Utd,16,7,1,0,26,4,5,2,1,15,6,31,39
282 2,Arsenal,16,7,1,0,17,4,2,3,3,7,9,11,31
283 3,Leicester,16,4,3,1,10,8,4,2,2,7,4,5,29
285 Notice that we've introduced the third parameter to C<process>.
286 If this parameter is missing then the TT2 sends its output to
287 C<STDOUT>. If this parameter is a scalar then it is taken as the
288 name of a file to write the output to. This parameter can also be
289 (amongst other things) a filehandle or a reference to an object w
290 hich is assumed to implement a C<print> method.
292 If we weren't interested in the split between home and away games,
293 then we could use a simpler template like this:
295 [% FOREACH team = teams -%]
296 [% team.pos %],[% team.name %],[% team.pl %],[% team.w %],
297 [%- team.d %],[% team.l %],[% team.f %],[% team.a %],
298 [%- team.aa %],[% team.gd %],[% team.pt %]
301 Which would produce output like this:
303 1,Man Utd,16,12,3,1,41,10,6,31,39
304 2,Arsenal,16,9,4,3,24,13,9,11,31
305 3,Leicester,16,8,5,3,17,12,4,5,29
309 This is starting to show some of the power and flexibility of
310 TT2, but you may be thinking that you could just as easily produce
311 this output with a C<foreach> loop and a couple of C<print>
312 statements in your code. This is, of course, true; but that's
313 because I've chosen a deliberately simple example to explain the
314 concepts. What if we wanted to produce an XML file containing the
315 data? And what if (as I mentioned earlier) the league data was held
316 in an object? The code would then look even easier as most of the code
317 we've written earlier would be hidden away in C<FootballLeague.pm>.
322 my $league = FootballLeague->new(name => 'English Premier');
324 my $tt = Template->new;
326 $tt->process('league_xml.tt', { league => $league })
329 And the template in C<league_xml.tt> would look something like this:
331 <?xml version="1.0"?>
332 <!DOCTYPE LEAGUE SYSTEM "league.dtd">
334 <league name="[% league.name %]" season="[% league.season %]">
335 [% FOREACH team = league.teams -%]
336 <team name="[% team.name %]"
338 played="[% team.pl %]"
339 goal_diff="[% team.gd %]"
340 points="[% team.pt %]">
343 draw="[%- team.hd %]"
346 against="[% team.ha %]" />
349 draw="[%- team.ad %]"
352 against="[% team.aa %]" />
357 Notice that as we've passed the whole object into C<process> then
358 we need to put an extra level of indirection on our template
359 variables - everything is now a component of the C<league> variable.
360 Other than that, everything in the template is very similar to what
361 we've used before. Presumably now C<team.name> calls an accessor
362 function rather than carrying out a hash lookup, but all of this
363 is transparent to our template designer.
365 =head1 Multiple Formats
367 As a final example, let's suppose that we need to create output
368 football league tables in a number of formats. Perhaps we are
369 passing this data on to other people and they can't all use the
370 same format. Some of our users need CSV files and others need
371 XML. Some require data split between home and away matches and
372 other just want the totals. In total, then, we'll need four
373 different templates, but the good news is that they can use the
374 same data object. All the script needs to do is to establish
375 which template is required and process it.
380 my ($name, $type, $stats) = @_;
382 my $league = FootballLeague->new(name => $name);
384 my $tt = Template->new;
386 $tt->process("league_${type}_$stats.tt",
387 { league => $league }
388 "league_$stats.$type")
391 For example, you can call this script as
393 league.pl 'English Premier' xml split
395 This will process a template called C<league_xml_split.tt>
396 and put the results in a file called C<league_split.xml>.
398 This starts to show the true strength of the Template Toolkit.
399 If we later wanted to add another file format - perhaps we
400 wanted to create a league table HTML page or even a LaTeX
401 document - then we would just need to create the appropriate
402 template and name it according to our existing naming
403 convention. We would need to make no changes to the code.
405 I hope you can now see why the Template Toolkit is fast becoming
406 an essential part of many people's Perl installation.
410 Dave Cross E<lt>dave@dave.org.ukE<gt>
417 Template Toolkit version 2.19, released on 27 April 2007.
422 Copyright (C) 2001 Dave Cross E<lt>dave@dave.org.ukE<gt>
424 This module is free software; you can redistribute it and/or
425 modify it under the same terms as Perl itself.
433 # perl-indent-level: 4
434 # indent-tabs-mode: nil
437 # vim: expandtab shiftwidth=4: