1#============================================================= -*-perl-*-
2#
3# Template::Tutorial::Datafile
4#
5# DESCRIPTION
6
7#
8# AUTHOR
9#   Dave Cross  <dave@dave.org.uk>
10#
11# COPYRIGHT
12#   Copyright (C) 1996-2008 Andy Wardley.  All Rights Reserved.
13#
14#   This module is free software; you can redistribute it and/or
15#   modify it under the same terms as Perl itself.
16#
17#========================================================================
18
19=head1 NAME
20
21Template::Tutorial::Datafile - Creating Data Output Files Using the Template Toolkit
22
23=head1 DESCRIPTION
24
25
26
27=head1 Introducing the Template Toolkit
28
29There are a number of Perl modules that are universally 
30recognised as The Right Thing To Use for certain tasks. If you 
31accessed a database without using DBI, pulled data from the WWW 
32without using one of the LWP modules or parsed XML without using 
33XML::Parser or one of its subclasses then you'd run the risk of 
34being shunned by polite Perl society.
35
36I believe that the year 2000 saw the emergence of another 'must 
37have' Perl module - the Template Toolkit. I don't think I'm 
38alone in this belief as the Template Toolkit won the 'Best New 
39Module' award at the Perl Conference last summer. Version 2.0 of 
40the Template Toolkit (known as TT2 to its friends) was recently 
41released to the CPAN.
42
43TT2 was designed and written by Andy Wardley E<lt>abw@wardley.orgE<gt>. 
44It was born out of Andy's previous templating module, 
45Text::Metatext, in best Fred Brooks 'plan to throw one away' 
46manner; and aims to be the most useful (or, at least, the most 
47I<used>) Perl templating system.
48
49TT2 provides a way to take a file of fixed boilerplate text 
50(the template) and embed variable data within it. One obvious 
51use of this is in the creation of dynamic web pages and this is 
52where a lot of the attention that TT2 has received has been 
53focussed. In this article, I hope to demonstrate that TT2 is 
54just as useful in non-web applications.
55
56=head1 Using the Template Toolkit
57
58Let's look at how we'd use TT2 to process a simple data file. 
59TT2 is an object oriented Perl module. Having downloaded it from 
60CPAN and installed it in the usual manner, using it in your 
61program is as easy as putting the lines
62
63    use Template;
64    my $tt = Template->new;
65
66in your code. The constructor function, C<new>, takes 
67a number of optional parameters which are documented in the 
68copious manual pages that come with the module, but for the 
69purposes of this article we'll keep things as simple as 
70possible.
71
72To process the template, you would call the C<process> method 
73like this
74
75    $tt->process('my_template', \%data)
76        || die $tt->error;
77
78We pass two parameters to C<process>, the first is the name of 
79the file containing the template to process (in this case, 
80my_template) and the second is a reference to a hash which 
81contains the data items that you want to use in the template. If 
82processing the template gives  any kind of error, the program 
83will die with a (hopefully) useful error message.
84
85So what kinds of things can go in C<%data>? The answer is just 
86about anything. Here's an example showing data about English 
87Premier League football teams.
88
89    my @teams = ({ name   => 'Man Utd',
90                   played => 16,
91                   won    => 12,
92                   drawn  => 3,
93                   lost   => 1 },
94                 { name   => 'Bradford',
95                   played => 16,
96                   won    => 2,
97                   drawn  => 5,
98                   lost   => 9 });
99
100    my %data = ( name   => 'English Premier League',
101                 season => '2000/01',
102                 teams  => \@teams );
103
104This creates three data items which can be accessed within the 
105template, called C<name>, C<season> and C<teams>. Notice that 
106C<teams> is a complex data structure.
107
108Here is a template that we might use to process this data.
109
110    League Standings
111
112    League Name: [% name %]
113    Season     : [% season %]
114
115    Teams:
116    [% FOREACH team = teams -%]
117    [% team.name %] [% team.played -%] 
118     [% team.won %] [% team.drawn %] [% team.lost %]
119    [% END %]
120
121Running this template with this data gives us the following 
122output
123
124	        League Standings
125
126    League Name: English Premier League
127    Season     : 2000/01
128
129    Teams:
130    Man Utd 16 12 3 1
131    Bradford 16 2 5 9
132
133Hopefully the syntax of the template is simple enough to 
134follow. There are a few points to note.
135
136=over 4
137
138=item *
139
140Template processing directives are written using a simple 
141language which is not Perl.
142
143=item *
144
145The keys of the C<%data> have become the names of the data 
146variables within the template.
147
148=item *
149
150Template processing directives are surrounded by C<[%> and 
151C<%]> sequences.
152
153=item *
154
155If these tags are replaced with C<[%-> C<-%]> then the preceding 
156or following linefeed is suppressed.
157
158=item *
159
160In the C<FOREACH> loop, each element of the C<teams> list was 
161assigned, in turn, to the temporary variable C<team>.
162
163=item *
164
165Each item assigned to the C<team> variable is a Perl hash. 
166Individual values within the hash are accessed using a dot notation.
167
168=back
169
170It's probably the first and last of these points which are the 
171most important. The first point emphasises the separation of the 
172data acquisition logic from the presentation logic. The person 
173creating the presentation template doesn't need to know Perl, 
174they only need to know the data items which will be passed into
175the template.
176
177The last point demonstrates the way that TT2 protects the 
178template designer from the implementation of the data structures.
179The data objects passed to the template processor can be scalars,
180arrays, hashes, objects or even subroutines. The template 
181processor will just interpret your data correctly and Do The 
182Right Thing to return the correct value to you. In this example 
183each team was a hash, but in a larger system each team might be 
184an object, in which case C<name>, C<played>, etc. would be accessor 
185methods to the underlying object attributes. No changes would be 
186required to the template as the template processor would realise 
187that it needed to call methods rather than access hash values.
188
189=head2 A more complex example
190
191Stats about the English Football League are usually presented in
192a slightly more complex format than the one we used above. A 
193full set of stats will show the number of games that a team has 
194won, lost or drawn, the number of goals scored for and against 
195the team and the number of points that the team therefore has.
196Teams gain three points for a win and one point for a draw. When
197teams have the same number of points they are separated by the 
198goal difference, that is the number of goals the team has scored
199minus the number of team scored against them. To complicate 
200things even further, the games won, drawn and lost and the goals 
201for and against are often split between home and away games.
202
203Therefore if you have a data source which lists the team name 
204togther with the games won, drawn and lost and the goals for and 
205against split into home and away (a total of eleven data items) 
206you can calculate all of the other items (goal difference, 
207points awarded and even position in the league). Let's take such 
208a file, but we'll only look at the top three teams. It will look 
209something like this:
210
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
214
215A simple script to read this data into an array of hashes will 
216look something like this (I've simplified the names of the data 
217columns - w, d, and l are games won, drawn and lost and f and a 
218are goals scored for and against; h and a at the front of a data 
219item name indicates whether it's a home or away statistic):
220
221    my @cols = qw(name hw hd hl hf ha aw ad al af aa);
222
223    my @teams;
224    while (<>) {
225        chomp;
226
227        my %team;
228
229        @team{@cols} = split /,/;
230
231        push @teams, \%team;
232    }
233
234We can then go thru the teams again and calculate all of the 
235derived data items:
236
237    foreach (@teams) {
238        $_->{w} = $_->{hw} + $_->{aw};
239        $_->{d} = $_->{hd} + $_->{ad};
240        $_->{l} = $_->{hl} + $_->{al};
241
242        $_->{pl} = $_->{w} + $_->{d} + $_->{l};
243
244        $_->{f} = $_->{hf} + $_->{af};
245        $_->{a} = $_->{ha} + $_->{aa};
246
247        $_->{gd} = $_->{f} - $_->{a};
248        $_->{pt} = (3 * $_->{w}) + $_->{d};
249    }
250
251And then produce a list sorted in descending order:
252
253    @teams = sort { 
254	$b->{pt} <=> $b->{pt} || $b->{gd} <=> $a->{gd} 
255    } @teams;
256
257And finally add the league position data item:
258
259    $teams[$_]->{pos} = $_ + 1 
260        foreach 0 .. $#teams;
261
262Having pulled all of our data into an internal data structure 
263we can start to produce output using out templates. A template 
264to create a CSV file containing the data split between home and 
265away stats would look like this:
266
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 %]
272    [%- END %]
273
274And processing it like this:
275
276    $tt->process('split.tt', { teams => \@teams }, 'split.csv')
277      || die $tt->error;
278
279produces the following output:
280
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
284
285Notice that we've introduced the third parameter to C<process>. 
286If this parameter is missing then the TT2 sends its output to 
287C<STDOUT>. If this parameter is a scalar then it is taken as the 
288name 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
290hich is assumed to implement a C<print> method.
291
292If we weren't interested in the split between home and away games, 
293then we could use a simpler template like this:
294
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 %]
299    [% END -%]
300
301Which would produce output like this:
302
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
306
307=head1 Producing XML
308
309This is starting to show some of the power and flexibility of 
310TT2, but you may be thinking that you could just as easily produce 
311this output with a C<foreach> loop and a couple of C<print> 
312statements in your code. This is, of course, true; but that's 
313because I've chosen a deliberately simple example to explain the 
314concepts. What if we wanted to produce an XML file containing the 
315data? And what if (as I mentioned earlier) the league data was held 
316in an object? The code would then look even easier as most of the code 
317we've written earlier would be hidden away in C<FootballLeague.pm>.
318
319    use FootballLeague;
320    use Template;
321
322    my $league = FootballLeague->new(name => 'English Premier');
323
324    my $tt = Template->new;
325
326    $tt->process('league_xml.tt', { league => $league })
327        || die $tt->error;
328
329And the template in C<league_xml.tt> would look something like this:
330
331    <?xml version="1.0"?>
332    <!DOCTYPE LEAGUE SYSTEM "league.dtd">
333
334    <league name="[% league.name %]" season="[% league.season %]">
335    [% FOREACH team = league.teams -%]
336      <team name="[% team.name %]"
337            pos="[% team.pos %]"
338            played="[% team.pl %]"
339            goal_diff="[% team.gd %]"
340            points="[% team.pt %]">
341         <stats type="home">
342        	win="[% team.hw %]"
343        	draw="[%- team.hd %]"
344        	lose="[% team.hl %]"
345        	for="[% team.hf %]"
346        	against="[% team.ha %]" />
347         <stats type="away">
348        	win="[% team.aw %]"
349        	draw="[%- team.ad %]"
350        	lose="[% team.al %]"
351        	for="[% team.af %]"
352        	against="[% team.aa %]" />
353      </team>
354    [% END -%]
355    &/league>
356
357Notice that as we've passed the whole object into C<process> then 
358we need to put an extra level of indirection on our template 
359variables - everything is now a component of the C<league> variable. 
360Other than that, everything in the template is very similar to what 
361we've used before. Presumably now C<team.name> calls an accessor 
362function rather than carrying out a hash lookup, but all of this 
363is transparent to our template designer.
364
365=head1 Multiple Formats
366
367As a final example, let's suppose that we need to create output
368football league tables in a number of formats. Perhaps we are 
369passing this data on to other people and they can't all use the 
370same format. Some of our users need CSV files and others need 
371XML. Some require data split between home and away matches and 
372other just want the totals. In total, then, we'll need four 
373different templates, but the good news is that they can use the 
374same data object. All the script needs to do is to establish 
375which template is required and process it.
376
377    use FootballLeague;
378    use Template;
379
380    my ($name, $type, $stats) = @_;
381
382    my $league = FootballLeague->new(name => $name);
383
384    my $tt = Template->new;
385
386    $tt->process("league_${type}_$stats.tt", 
387                 { league => $league }
388                 "league_$stats.$type")
389        || die $tt->error;
390
391For example, you can call this script as
392
393    league.pl 'English Premier' xml split
394
395This will process a template called C<league_xml_split.tt> 
396and put the results in a file called C<league_split.xml>.
397
398This starts to show the true strength of the Template Toolkit. 
399If we later wanted to add another file format - perhaps we 
400wanted to create a league table HTML page or even a LaTeX 
401document - then we would just need to create the appropriate 
402template and name it according to our existing naming 
403convention. We would need to make no changes to the code.
404
405I hope you can now see why the Template Toolkit is fast becoming
406an essential part of many people's Perl installation.
407
408=head1 AUTHOR
409
410Dave Cross E<lt>dave@dave.org.ukE<gt>
411
412
413
414
415=head1 VERSION
416
417Template Toolkit version 2.19, released on 27 April 2007.
418
419=head1 COPYRIGHT
420
421
422Copyright (C) 2001 Dave Cross E<lt>dave@dave.org.ukE<gt>
423
424This module is free software; you can redistribute it and/or
425modify it under the same terms as Perl itself.
426
427
428
429=cut
430
431# Local Variables:
432# mode: perl
433# perl-indent-level: 4
434# indent-tabs-mode: nil
435# End:
436#
437# vim: expandtab shiftwidth=4:
438