diff options
Diffstat (limited to 'lib/Template/Tutorial/Datafile.pod')
| -rw-r--r-- | lib/Template/Tutorial/Datafile.pod | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/lib/Template/Tutorial/Datafile.pod b/lib/Template/Tutorial/Datafile.pod new file mode 100644 index 0000000..652f79d --- /dev/null +++ b/lib/Template/Tutorial/Datafile.pod @@ -0,0 +1,461 @@ +#============================================================= -*-perl-*- +# +# Template::Tutorial::Datafile +# +# DESCRIPTION +# This tutorial gives an overview of the Template Toolkit, showing in +# particular how to use it to read and write data files in various +# different formats and styles. It was written by Dave Cross and +# first appeared as a lead article at http://www.perl.com/ earlier in +# the year (2001). +# +# AUTHOR +# Dave Cross <dave@dave.org.uk> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# IMPORTANT NOTE +# This documentation is generated automatically from source +# templates. Any changes you make here may be lost. +# +# The 'docsrc' documentation source bundle is available for download +# from http://www.template-toolkit.org/docs.html and contains all +# the source templates, XML files, scripts, etc., from which the +# documentation for the Template Toolkit is built. +#------------------------------------------------------------------------ + +=head1 NAME + +Template::Tutorial::Datafile - Creating Data Output Files Using the Template Toolkit + +=head1 DESCRIPTION + +This tutorial gives an overview of the Template Toolkit, showing in +particular how to use it to read and write data files in various +different formats and styles. It was written by Dave Cross and first +appeared as a lead article at http://www.perl.com/ earlier in the year +(2001). + +=head1 Introducing the Template Toolkit + +There are a number of Perl modules that are universally +recognised as The Right Thing To Use for certain tasks. If you +accessed a database without using DBI, pulled data from the WWW +without using one of the LWP modules or parsed XML without using +XML::Parser or one of its subclasses then you'd run the risk of +being shunned by polite Perl society. + +I believe that the year 2000 saw the emergence of another 'must +have' Perl module - the Template Toolkit. I don't think I'm +alone in this belief as the Template Toolkit won the 'Best New +Module' award at the Perl Conference last summer. Version 2.0 of +the Template Toolkit (known as TT2 to its friends) was recently +released to the CPAN. + +TT2 was designed and written by Andy Wardley E<lt>abw@wardley.orgE<gt>. +It was born out of Andy's previous templating module, +Text::Metatext, in best Fred Brooks 'plan to throw one away' +manner; and aims to be the most useful (or, at least, the most +I<used>) Perl templating system. + +TT2 provides a way to take a file of fixed boilerplate text +(the template) and embed variable data within it. One obvious +use of this is in the creation of dynamic web pages and this is +where a lot of the attention that TT2 has received has been +focussed. In this article, I hope to demonstrate that TT2 is +just as useful in non-web applications. + +=head1 Using the Template Toolkit + +Let's look at how we'd use TT2 to process a simple data file. +TT2 is an object oriented Perl module. Having downloaded it from +CPAN and installed it in the usual manner, using it in your +program is as easy as putting the lines + + use Template; + my $tt = Template->new; + +in your code. The constructor function, C<new>, takes +a number of optional parameters which are documented in the +copious manual pages that come with the module, but for the +purposes of this article we'll keep things as simple as +possible. + +To process the template, you would call the C<process> method +like this + + $tt->process('my_template', \%data) + || die $tt->error; + +We pass two parameters to C<process>, the first is the name of +the file containing the template to process (in this case, +my_template) and the second is a reference to a hash which +contains the data items that you want to use in the template. If +processing the template gives any kind of error, the program +will die with a (hopefully) useful error message. + +So what kinds of things can go in C<%data>? The answer is just +about anything. Here's an example showing data about English +Premier League football teams. + + my @teams = ({ name => 'Man Utd', + played => 16, + won => 12, + drawn => 3, + lost => 1 }, + { name => 'Bradford', + played => 16, + won => 2, + drawn => 5, + lost => 9 }); + + my %data = ( name => 'English Premier League', + season => '2000/01', + teams => \@teams ); + +This creates three data items which can be accessed within the +template, called C<name>, C<season> and C<teams>. Notice that +C<teams> is a complex data structure. + +Here is a template that we might use to process this data. + + League Standings + + League Name: [% name %] + Season : [% season %] + + Teams: + [% FOREACH team = teams -%] + [% team.name %] [% team.played -%] + [% team.won %] [% team.drawn %] [% team.lost %] + [% END %] + +Running this template with this data gives us the following +output + + League Standings + + League Name: English Premier League + Season : 2000/01 + + Teams: + Man Utd 16 12 3 1 + Bradford 16 2 5 9 + +Hopefully the syntax of the template is simple enough to +follow. There are a few points to note. + +=over 4 + +=item * + +Template processing directives are written using a simple +language which is not Perl. + +=item * + +The keys of the C<%data> have become the names of the data +variables within the template. + +=item * + +Template processing directives are surrounded by C<[%> and +C<%]> sequences. + +=item * + +If these tags are replaced with C<[%-> C<-%]> then the preceding +or following linefeed is suppressed. + +=item * + +In the C<FOREACH> loop, each element of the C<teams> list was +assigned, in turn, to the temporary variable C<team>. + +=item * + +Each item assigned to the C<team> variable is a Perl hash. +Individual values within the hash are accessed using a dot notation. + +=back + +It's probably the first and last of these points which are the +most important. The first point emphasises the separation of the +data acquisition logic from the presentation logic. The person +creating the presentation template doesn't need to know Perl, +they only need to know the data items which will be passed into +the template. + +The last point demonstrates the way that TT2 protects the +template designer from the implementation of the data structures. +The data objects passed to the template processor can be scalars, +arrays, hashes, objects or even subroutines. The template +processor will just interpret your data correctly and Do The +Right Thing to return the correct value to you. In this example +each team was a hash, but in a larger system each team might be +an object, in which case C<name>, C<played>, etc. would be accessor +methods to the underlying object attributes. No changes would be +required to the template as the template processor would realise +that it needed to call methods rather than access hash values. + +=head2 A more complex example + +Stats about the English Football League are usually presented in +a slightly more complex format than the one we used above. A +full set of stats will show the number of games that a team has +won, lost or drawn, the number of goals scored for and against +the team and the number of points that the team therefore has. +Teams gain three points for a win and one point for a draw. When +teams have the same number of points they are separated by the +goal difference, that is the number of goals the team has scored +minus the number of team scored against them. To complicate +things even further, the games won, drawn and lost and the goals +for and against are often split between home and away games. + +Therefore if you have a data source which lists the team name +togther with the games won, drawn and lost and the goals for and +against split into home and away (a total of eleven data items) +you can calculate all of the other items (goal difference, +points awarded and even position in the league). Let's take such +a file, but we'll only look at the top three teams. It will look +something like this: + + Man Utd,7,1,0,26,4,5,2,1,15,6 + Arsenal,7,1,0,17,4,2,3,3,7,9 + Leicester,4,3,1,10,8,4,2,2,7,4 + +A simple script to read this data into an array of hashes will +look something like this (I've simplified the names of the data +columns - w, d, and l are games won, drawn and lost and f and a +are goals scored for and against; h and a at the front of a data +item name indicates whether it's a home or away statistic): + + my @cols = qw(name hw hd hl hf ha aw ad al af aa); + + my @teams; + while (<>) { + chomp; + + my %team; + + @team{@cols} = split /,/; + + push @teams, \%team; + } + +We can then go thru the teams again and calculate all of the +derived data items: + + foreach (@teams) { + $_->{w} = $_->{hw} + $_->{aw}; + $_->{d} = $_->{hd} + $_->{ad}; + $_->{l} = $_->{hl} + $_->{al}; + + $_->{pl} = $_->{w} + $_->{d} + $_->{l}; + + $_->{f} = $_->{hf} + $_->{af}; + $_->{a} = $_->{ha} + $_->{aa}; + + $_->{gd} = $_->{f} - $_->{a}; + $_->{pt} = (3 * $_->{w}) + $_->{d}; + } + +And then produce a list sorted in descending order: + + @teams = sort { + $b->{pt} <=> $b->{pt} || $b->{gd} <=> $a->{gd} + } @teams; + +And finally add the league position data item: + + $teams[$_]->{pos} = $_ + 1 + foreach 0 .. $#teams; + +Having pulled all of our data into an internal data structure +we can start to produce output using out templates. A template +to create a CSV file containing the data split between home and +away stats would look like this: + + [% FOREACH team = teams -%] + [% team.pos %],[% team.name %],[% team.pl %],[% team.hw %], + [%- team.hd %],[% team.hl %],[% team.hf %],[% team.ha %], + [%- team.aw %],[% team.ad %],[% team.al %],[% team.af %], + [%- team.aa %],[% team.gd %],[% team.pt %] + [%- END %] + +And processing it like this: + + $tt->process('split.tt', { teams => \@teams }, 'split.csv') + || die $tt->error; + +produces the following output: + + 1,Man Utd,16,7,1,0,26,4,5,2,1,15,6,31,39 + 2,Arsenal,16,7,1,0,17,4,2,3,3,7,9,11,31 + 3,Leicester,16,4,3,1,10,8,4,2,2,7,4,5,29 + +Notice that we've introduced the third parameter to C<process>. +If this parameter is missing then the TT2 sends its output to +C<STDOUT>. If this parameter is a scalar then it is taken as the +name of a file to write the output to. This parameter can also be +(amongst other things) a filehandle or a reference to an object w +hich is assumed to implement a C<print> method. + +If we weren't interested in the split between home and away games, +then we could use a simpler template like this: + + [% FOREACH team = teams -%] + [% team.pos %],[% team.name %],[% team.pl %],[% team.w %], + [%- team.d %],[% team.l %],[% team.f %],[% team.a %], + [%- team.aa %],[% team.gd %],[% team.pt %] + [% END -%] + +Which would produce output like this: + + 1,Man Utd,16,12,3,1,41,10,6,31,39 + 2,Arsenal,16,9,4,3,24,13,9,11,31 + 3,Leicester,16,8,5,3,17,12,4,5,29 + +=head1 Producing XML + +This is starting to show some of the power and flexibility of +TT2, but you may be thinking that you could just as easily produce +this output with a C<foreach> loop and a couple of C<print> +statements in your code. This is, of course, true; but that's +because I've chosen a deliberately simple example to explain the +concepts. What if we wanted to produce an XML file containing the +data? And what if (as I mentioned earlier) the league data was held +in an object? The code would then look even easier as most of the code +we've written earlier would be hidden away in C<FootballLeague.pm>. + + use FootballLeague; + use Template; + + my $league = FootballLeague->new(name => 'English Premier'); + + my $tt = Template->new; + + $tt->process('league_xml.tt', { league => $league }) + || die $tt->error; + +And the template in C<league_xml.tt> would look something like this: + + <?xml version="1.0"?> + <!DOCTYPE LEAGUE SYSTEM "league.dtd"> + + <league name="[% league.name %]" season="[% league.season %]"> + [% FOREACH team = league.teams -%] + <team name="[% team.name %]" + pos="[% team.pos %]" + played="[% team.pl %]" + goal_diff="[% team.gd %]" + points="[% team.pt %]"> + <stats type="home"> + win="[% team.hw %]" + draw="[%- team.hd %]" + lose="[% team.hl %]" + for="[% team.hf %]" + against="[% team.ha %]" /> + <stats type="away"> + win="[% team.aw %]" + draw="[%- team.ad %]" + lose="[% team.al %]" + for="[% team.af %]" + against="[% team.aa %]" /> + </team> + [% END -%] + &/league> + +Notice that as we've passed the whole object into C<process> then +we need to put an extra level of indirection on our template +variables - everything is now a component of the C<league> variable. +Other than that, everything in the template is very similar to what +we've used before. Presumably now C<team.name> calls an accessor +function rather than carrying out a hash lookup, but all of this +is transparent to our template designer. + +=head1 Multiple Formats + +As a final example, let's suppose that we need to create output +football league tables in a number of formats. Perhaps we are +passing this data on to other people and they can't all use the +same format. Some of our users need CSV files and others need +XML. Some require data split between home and away matches and +other just want the totals. In total, then, we'll need four +different templates, but the good news is that they can use the +same data object. All the script needs to do is to establish +which template is required and process it. + + use FootballLeague; + use Template; + + my ($name, $type, $stats) = @_; + + my $league = FootballLeague->new(name => $name); + + my $tt = Template->new; + + $tt->process("league_${type}_$stats.tt", + { league => $league } + "league_$stats.$type") + || die $tt->error; + +For example, you can call this script as + + league.pl 'English Premier' xml split + +This will process a template called C<league_xml_split.tt> +and put the results in a file called C<league_split.xml>. + +This starts to show the true strength of the Template Toolkit. +If we later wanted to add another file format - perhaps we +wanted to create a league table HTML page or even a LaTeX +document - then we would just need to create the appropriate +template and name it according to our existing naming +convention. We would need to make no changes to the code. + +I hope you can now see why the Template Toolkit is fast becoming +an essential part of many people's Perl installation. + +=head1 AUTHOR + +Dave Cross E<lt>dave@dave.org.ukE<gt> + + + + +=head1 VERSION + +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Dave Cross E<lt>dave@dave.org.ukE<gt> + +This module is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: |
