1Template::Tutorial::DatUasfeirleC(o3n)tributed Perl DocuTmeemnptlaattieo:n:Tutorial::Datafile(3)
2
3
4

NAME

6       Template::Tutorial::Datafile - Creating Data Output Files Using the
7       Template Toolkit
8

DESCRIPTION

Introducing the Template Toolkit

11       There are a number of Perl modules that are universally recognised as
12       The Right Thing To Use for certain tasks. If you accessed a database
13       without using DBI, pulled data from the WWW without using one of the
14       LWP modules or parsed XML without using XML::Parser or one of its
15       subclasses then you'd run the risk of being shunned by polite Perl
16       society.
17
18       I believe that the year 2000 saw the emergence of another 'must have'
19       Perl module - the Template Toolkit. I don't think I'm alone in this
20       belief as the Template Toolkit won the 'Best New Module' award at the
21       Perl Conference last summer. Version 2.0 of the Template Toolkit (known
22       as TT2 to its friends) was recently released to the CPAN.
23
24       TT2 was designed and written by Andy Wardley <abw@wardley.org>.  It was
25       born out of Andy's previous templating module, Text::Metatext, in best
26       Fred Brooks 'plan to throw one away' manner; and aims to be the most
27       useful (or, at least, the most used) Perl templating system.
28
29       TT2 provides a way to take a file of fixed boilerplate text (the
30       template) and embed variable data within it. One obvious use of this is
31       in the creation of dynamic web pages and this is where a lot of the
32       attention that TT2 has received has been focussed. In this article, I
33       hope to demonstrate that TT2 is just as useful in non-web applications.
34

Using the Template Toolkit

36       Let's look at how we'd use TT2 to process a simple data file.  TT2 is
37       an object oriented Perl module. Having downloaded it from CPAN and
38       installed it in the usual manner, using it in your program is as easy
39       as putting the lines
40
41           use Template;
42           my $tt = Template->new;
43
44       in your code. The constructor function, "new", takes a number of
45       optional parameters which are documented in the copious manual pages
46       that come with the module, but for the purposes of this article we'll
47       keep things as simple as possible.
48
49       To process the template, you would call the "process" method like this
50
51           $tt->process('my_template', \%data)
52               || die $tt->error;
53
54       We pass two parameters to "process", the first is the name of the file
55       containing the template to process (in this case, my_template) and the
56       second is a reference to a hash which contains the data items that you
57       want to use in the template. If processing the template gives  any kind
58       of error, the program will die with a (hopefully) useful error message.
59
60       So what kinds of things can go in %data? The answer is just about
61       anything. Here's an example showing data about English Premier League
62       football teams.
63
64           my @teams = ({ name   => 'Man Utd',
65                          played => 16,
66                          won    => 12,
67                          drawn  => 3,
68                          lost   => 1 },
69                        { name   => 'Bradford',
70                          played => 16,
71                          won    => 2,
72                          drawn  => 5,
73                          lost   => 9 });
74
75           my %data = ( name   => 'English Premier League',
76                        season => '2000/01',
77                        teams  => \@teams );
78
79       This creates three data items which can be accessed within the
80       template, called "name", "season" and "teams". Notice that "teams" is a
81       complex data structure.
82
83       Here is a template that we might use to process this data.
84
85           League Standings
86
87           League Name: [% name %]
88           Season     : [% season %]
89
90           Teams:
91           [% FOREACH team = teams -%]
92           [% team.name %] [% team.played -%]
93            [% team.won %] [% team.drawn %] [% team.lost %]
94           [% END %]
95
96       Running this template with this data gives us the following output
97
98                       League Standings
99
100           League Name: English Premier League
101           Season     : 2000/01
102
103           Teams:
104           Man Utd 16 12 3 1
105           Bradford 16 2 5 9
106
107       Hopefully the syntax of the template is simple enough to follow. There
108       are a few points to note.
109
110       •   Template processing directives are written using a simple language
111           which is not Perl.
112
113       •   The keys of the %data have become the names of the data variables
114           within the template.
115
116       •   Template processing directives are surrounded by "[%" and "%]"
117           sequences.
118
119       •   If these tags are replaced with "[%-" "-%]" then the preceding or
120           following linefeed is suppressed.
121
122       •   In the "FOREACH" loop, each element of the "teams" list was
123           assigned, in turn, to the temporary variable "team".
124
125       •   Each item assigned to the "team" variable is a Perl hash.
126           Individual values within the hash are accessed using a dot
127           notation.
128
129       It's probably the first and last of these points which are the most
130       important. The first point emphasises the separation of the data
131       acquisition logic from the presentation logic. The person creating the
132       presentation template doesn't need to know Perl, they only need to know
133       the data items which will be passed into the template.
134
135       The last point demonstrates the way that TT2 protects the template
136       designer from the implementation of the data structures.  The data
137       objects passed to the template processor can be scalars, arrays,
138       hashes, objects or even subroutines. The template processor will just
139       interpret your data correctly and Do The Right Thing to return the
140       correct value to you. In this example each team was a hash, but in a
141       larger system each team might be an object, in which case "name",
142       "played", etc. would be accessor methods to the underlying object
143       attributes. No changes would be required to the template as the
144       template processor would realise that it needed to call methods rather
145       than access hash values.
146
147   A more complex example
148       Stats about the English Football League are usually presented in a
149       slightly more complex format than the one we used above. A full set of
150       stats will show the number of games that a team has won, lost or drawn,
151       the number of goals scored for and against the team and the number of
152       points that the team therefore has.  Teams gain three points for a win
153       and one point for a draw. When teams have the same number of points
154       they are separated by the goal difference, that is the number of goals
155       the team has scored minus the number of team scored against them. To
156       complicate things even further, the games won, drawn and lost and the
157       goals for and against are often split between home and away games.
158
159       Therefore if you have a data source which lists the team name together
160       with the games won, drawn and lost and the goals for and against split
161       into home and away (a total of eleven data items) you can calculate all
162       of the other items (goal difference, points awarded and even position
163       in the league). Let's take such a file, but we'll only look at the top
164       three teams. It will look something like this:
165
166           Man Utd,7,1,0,26,4,5,2,1,15,6
167           Arsenal,7,1,0,17,4,2,3,3,7,9
168           Leicester,4,3,1,10,8,4,2,2,7,4
169
170       A simple script to read this data into an array of hashes will look
171       something like this (I've simplified the names of the data columns - w,
172       d, and l are games won, drawn and lost and f and a are goals scored for
173       and against; h and a at the front of a data item name indicates whether
174       it's a home or away statistic):
175
176           my @cols = qw(name hw hd hl hf ha aw ad al af aa);
177
178           my @teams;
179           while (<>) {
180               chomp;
181
182               my %team;
183
184               @team{@cols} = split /,/;
185
186               push @teams, \%team;
187           }
188
189       We can then go thru the teams again and calculate all of the derived
190       data items:
191
192           foreach (@teams) {
193               $_->{w} = $_->{hw} + $_->{aw};
194               $_->{d} = $_->{hd} + $_->{ad};
195               $_->{l} = $_->{hl} + $_->{al};
196
197               $_->{pl} = $_->{w} + $_->{d} + $_->{l};
198
199               $_->{f} = $_->{hf} + $_->{af};
200               $_->{a} = $_->{ha} + $_->{aa};
201
202               $_->{gd} = $_->{f} - $_->{a};
203               $_->{pt} = (3 * $_->{w}) + $_->{d};
204           }
205
206       And then produce a list sorted in descending order:
207
208           @teams = sort {
209               $b->{pt} <=> $b->{pt} || $b->{gd} <=> $a->{gd}
210           } @teams;
211
212       And finally add the league position data item:
213
214           $teams[$_]->{pos} = $_ + 1
215               foreach 0 .. $#teams;
216
217       Having pulled all of our data into an internal data structure we can
218       start to produce output using out templates. A template to create a CSV
219       file containing the data split between home and away stats would look
220       like this:
221
222           [% FOREACH team = teams -%]
223           [% team.pos %],[% team.name %],[% team.pl %],[% team.hw %],
224           [%- team.hd %],[% team.hl %],[% team.hf %],[% team.ha %],
225           [%- team.aw %],[% team.ad %],[% team.al %],[% team.af %],
226           [%- team.aa %],[% team.gd %],[% team.pt %]
227           [%- END %]
228
229       And processing it like this:
230
231           $tt->process('split.tt', { teams => \@teams }, 'split.csv')
232             || die $tt->error;
233
234       produces the following output:
235
236           1,Man Utd,16,7,1,0,26,4,5,2,1,15,6,31,39
237           2,Arsenal,16,7,1,0,17,4,2,3,3,7,9,11,31
238           3,Leicester,16,4,3,1,10,8,4,2,2,7,4,5,29
239
240       Notice that we've introduced the third parameter to "process".  If this
241       parameter is missing then the TT2 sends its output to "STDOUT". If this
242       parameter is a scalar then it is taken as the name of a file to write
243       the output to. This parameter can also be (amongst other things) a
244       filehandle or a reference to an object which is assumed to implement a
245       "print" method.
246
247       If we weren't interested in the split between home and away games, then
248       we could use a simpler template like this:
249
250           [% FOREACH team = teams -%]
251           [% team.pos %],[% team.name %],[% team.pl %],[% team.w %],
252           [%- team.d %],[% team.l %],[% team.f %],[% team.a %],
253           [%- team.aa %],[% team.gd %],[% team.pt %]
254           [% END -%]
255
256       Which would produce output like this:
257
258           1,Man Utd,16,12,3,1,41,10,6,31,39
259           2,Arsenal,16,9,4,3,24,13,9,11,31
260           3,Leicester,16,8,5,3,17,12,4,5,29
261

Producing XML

263       This is starting to show some of the power and flexibility of TT2, but
264       you may be thinking that you could just as easily produce this output
265       with a "foreach" loop and a couple of "print" statements in your code.
266       This is, of course, true; but that's because I've chosen a deliberately
267       simple example to explain the concepts. What if we wanted to produce an
268       XML file containing the data? And what if (as I mentioned earlier) the
269       league data was held in an object? The code would then look even easier
270       as most of the code we've written earlier would be hidden away in
271       "FootballLeague.pm".
272
273           use FootballLeague;
274           use Template;
275
276           my $league = FootballLeague->new(name => 'English Premier');
277
278           my $tt = Template->new;
279
280           $tt->process('league_xml.tt', { league => $league })
281               || die $tt->error;
282
283       And the template in "league_xml.tt" would look something like this:
284
285           <?xml version="1.0"?>
286           <!DOCTYPE LEAGUE SYSTEM "league.dtd">
287
288           <league name="[% league.name %]" season="[% league.season %]">
289           [% FOREACH team = league.teams -%]
290             <team name="[% team.name %]"
291                   pos="[% team.pos %]"
292                   played="[% team.pl %]"
293                   goal_diff="[% team.gd %]"
294                   points="[% team.pt %]">
295                <stats type="home">
296                       win="[% team.hw %]"
297                       draw="[%- team.hd %]"
298                       lose="[% team.hl %]"
299                       for="[% team.hf %]"
300                       against="[% team.ha %]" />
301                <stats type="away">
302                       win="[% team.aw %]"
303                       draw="[%- team.ad %]"
304                       lose="[% team.al %]"
305                       for="[% team.af %]"
306                       against="[% team.aa %]" />
307             </team>
308           [% END -%]
309           &/league>
310
311       Notice that as we've passed the whole object into "process" then we
312       need to put an extra level of indirection on our template variables -
313       everything is now a component of the "league" variable.  Other than
314       that, everything in the template is very similar to what we've used
315       before. Presumably now "team.name" calls an accessor function rather
316       than carrying out a hash lookup, but all of this is transparent to our
317       template designer.
318

Multiple Formats

320       As a final example, let's suppose that we need to create output
321       football league tables in a number of formats. Perhaps we are passing
322       this data on to other people and they can't all use the same format.
323       Some of our users need CSV files and others need XML. Some require data
324       split between home and away matches and other just want the totals. In
325       total, then, we'll need four different templates, but the good news is
326       that they can use the same data object. All the script needs to do is
327       to establish which template is required and process it.
328
329           use FootballLeague;
330           use Template;
331
332           my ($name, $type, $stats) = @_;
333
334           my $league = FootballLeague->new(name => $name);
335
336           my $tt = Template->new;
337
338           $tt->process("league_${type}_$stats.tt",
339                        { league => $league }
340                        "league_$stats.$type")
341               || die $tt->error;
342
343       For example, you can call this script as
344
345           league.pl 'English Premier' xml split
346
347       This will process a template called "league_xml_split.tt" and put the
348       results in a file called "league_split.xml".
349
350       This starts to show the true strength of the Template Toolkit.  If we
351       later wanted to add another file format - perhaps we wanted to create a
352       league table HTML page or even a LaTeX document - then we would just
353       need to create the appropriate template and name it according to our
354       existing naming convention. We would need to make no changes to the
355       code.
356
357       I hope you can now see why the Template Toolkit is fast becoming an
358       essential part of many people's Perl installation.
359

AUTHOR

361       Dave Cross <dave@dave.org.uk>
362

VERSION

364       Template Toolkit version 2.19, released on 27 April 2007.
365
367       Copyright (C) 2001 Dave Cross <dave@dave.org.uk>
368
369       This module is free software; you can redistribute it and/or modify it
370       under the same terms as Perl itself.
371
372
373
374perl v5.32.1                      2021-01-27   Template::Tutorial::Datafile(3)
Impressum