Introduction To
   Template::Toolkit
         Introduction
       Basics: Part One
     Exercises (Hands On)
       Basics: Part Two
     Exercises (Hands On)
References and Further Reading
Introduction
•Who am I?
•What level of Perl expertise is required?
•What is Template::Toolkit?
Part One

•Simple mail-merge type example
Template for a letter
Dear [% name %],

It has come to our attention that your account is in
arrears to the sum of [% debt %].

Please settle your account before [% deadline %] or we
will be forced to revoke your Licence to Thrill.

The Management.
Perl script to call it
use strict;
use Template;
my $tt = Template->new({
     INCLUDE_PATH => '/usr/local/templates',
     INTERPOLATE => 1,
}) || die "$Template::ERRORn";

my $vars = {
    name => 'Count Edward van Halen',
    debt => '3 riffs and a solo',
    deadline => 'the next chorus',
 };

$tt->process('letters/overdrawn', $vars)
    || die $tt->error(), "n";
Multiple letters
[% FOREACH d IN debtors %]
Dear [% d.name %],

It has come to our attention that your account is in
arrears to the sum of [% d.debt %].

Please settle your account before [% d.deadline %] or we
will be forced to revoke your Licence to Thrill.

The Management.
[% END %]
Perl fragment
my $vars = { debtors => [
   {
      name => 'Count Edward van Halen',
      debt => '3 riffs and a solo',
      deadline => 'the next chorus',
   },
   {
      name => ‘Baron Eric Clapton',
      debt => ‘1 badge',
      deadline => ‘tomorrow',
    }
]
};

$tt->process('letters/overdrawn', $vars)
    || die $tt->error(), "n";
Multiple letters with logic
[% FOREACH d = debtors %]
Dear [% d.name %],

It has come to our attention that your account is in
arrears to the sum of [% d.debt %].

Please settle your account before [% d.deadline %] or we
will be forced to revoke your Licence to Thrill.

[% IF d.final %]This is your last chance.[% END %]

The Management.
[% END %]
Perl fragment
my $debtors = [
   {
      name => 'Count Edward van Halen',
      debt => '3 riffs and a solo',
      deadline => 'the next chorus',
   },
   {
      name => ‘Baron Eric Clapton',
      debt => ‘1 badge',
      deadline => ‘tomorrow',
      final => 1
    }
];

$tt->process('letters/overdrawn', $debtors)
    || die $tt->error(), "n";
Boolean logic: IF ELSE
[% IF age < 10 %]
[% ELSIF age < 18 %]
[% ELSE %]
[% END %]

The following conditional and boolean operators may be used:
  == != < <= > >= && || ! and or not
Boolean logic: SWITCH
[% SWITCH myvar %]
[% CASE 'value1' %]
[% CASE ['value2', 'value3'] %] # multiple values
[% CASE myhash.keys %]          # ditto
[% CASE %]                      # default
[% END %]
Alternative contents
[% FOREACH d IN debtors %]
Dear [% d.name %],

Thank you for your recent payment of [% d.debt %].

Your account is now clear and you may continue to thrill.

The Management.
[% END %]
Include other templates
[% FOREACH d IN debtors %]
Dear [% d.name %],

[% IF d.angry -%]
[% INCLUDE angry_letter.tt2 %]
[% ELSE -%]
[% INCLUDE grateful_letter.tt2 %]
[% END -%]

The Management.
[% END %]
Perl fragment
my $debtors = [
   {
      name => 'Count Edward van Halen',
      debt => '3 riffs and a solo',
      deadline => 'the next chorus',
   },
   {
      name => ‘Baron Eric Clapton',
      debt => ‘1 badge',
      deadline => ‘tomorrow',
      final => 1,
      angry => 1,
    }
];

$tt->process('letters/overdrawn', $debtors)
    || die $tt->error(), "n";
Exercises
Part Two
•Adding a wrapper for the header and footer
•Adding simple filters to encoding entities or control case
•Using a vmethod to add row numbers
•Sorting a select list
•Formatting currency
Adding a wrapper
Content-Type: text/html; charset=ISO-8859-1

<html>
<head>
</head>
<body>
<h1>Introduction to TT</h2>

[%# Header and footer will be the same on all pages %]

<table>
<tr><th>Something</th><th>Something Else</th></tr>
[% FOREACH r IN rows %]
<tr><td>[% r.something %]</td><td>[% r.something_else %]</td></tr>
[% END %]
</table>

</body>
</html>
Move the common content to wrapper.tt

Content-Type: text/html; charset=ISO-8859-1

<html>
<head>
</head>
<body>
<h1>Introduction to TT</h2>

[% content %]

</body>
</html>
Tell TT about the wrapper
#! /usr/bin/perl

use strict; use warnings; use CGI; use Template;

# Using a wrapper.

my $tt = Template->new(
   {
     INCLUDE_PATH => '/home/duncan/tt/htdocs/tt2',
     INTERPOLATE => 1,
     WRAPPER => 'wrapper.tt2'
   }
) || die "$Template::ERRORn";

$tt->process(
   's2.tt2',
   {
      rows => [
        { something => 'apples', something_else => '1 kg' },
        { something => 'pears', something_else => '2 kg' }
      ]
   }
) || die $tt->error() . "n";
Dodgy data
my $tt = Template->new(
   {
     INCLUDE_PATH => '/home/duncan/tt/htdocs/tt2',
     INTERPOLATE => 1,
     WRAPPER => 'wrapper.tt2'
   }
) || die "$Template::ERRORn";

$tt->process(
   's2.tt2',
   {
      rows => [
        { something => 'apples', something_else => '1 kg' },
        { something => 'pears', something_else => '2 kg' },
        { something => 'turnips > pears > grapes', something_else => '2 & 3 kg' },
        { something => '<script type="text/javascript">alert("Hi");</script>', something_else => '2
& 3 kg' },
      ]
   }
) || die $tt->error() . "n";
Encode it!
<! [% component.name %] -->

[%# Using a filter to encode entities and control case. %]

<table>
<tr><th>Something</th><th>Something Else</th></tr>
[% FOREACH r IN rows %]
<tr><td>[% r.something FILTER html_entity %]</td><td>[% r.something_else FILTER html_entity
FILTER upper %]</td></tr>
[% END %]
</table>
<!-- End [% component.name %] -->
That’s better
<tr><td>apples</td><td>1 KG</td></tr> <tr><td>pears</td><td>2 KG</td></tr>

<tr><td>turnips &gt; pears &gt; grapes</td><td>2 &amp; 3 KG</td></tr>

<tr><td>&lt;script
type=&quot;text/javascript&quot;&gt;alert(&quot;Hi&quot;);&lt;/script&gt;</td><td>2 &amp; 3
KG</td></tr>
Use a vmethod to add row
                   numbers
<! [% component.name %] -->

[%# Using a vmethod to add row number %]

<table>
<tr><th>No</th><th>Something</th><th>Something Else</th></tr>
[% FOREACH r IN rows %]
<tr><td>([% loop.index %])</td>
<td>[% r.something FILTER html_entity %]</td><td>[% r.something_else FILTER html_entity
FILTER upper %]</td></tr>
[% END %]
</table>
<!-- End [% component.name %] -->
With row numbers
No       Something                  Something Else




(0)      apples                     1 KG

(1)      pears                      2 KG

(2)      turnips > pears > grapes   2 & 3 KG




(3)      <>peas                     2 & 3 KG

(4)      apricots                   2 & 3 KG
Use a vmethod to do a simple sort
         on a select list.
my $tt = Template->new(
   {
     INCLUDE_PATH => '/home/duncan/tt/htdocs/tt2',
     INTERPOLATE => 1,
     WRAPPER => 'wrapper.tt2'
   }
) || die "$Template::ERRORn";

$tt->process(
   's5.tt2',
   {
      rows => [
         { something => 'apples', something_else => '1 kg' },
         { something => 'pears', something_else => '2 kg' },
         { something => 'turnips > pears > grapes', something_else => '2 & 3 kg' },
         { something => '<>peas', something_else => '2 & 3 kg' },
         { something => 'apricots', something_else => '2 & 3 kg' },
      ],
      suppliers => [ 'Fred', 'Alan', 'Joe', 'Bert' ]
   }
) || die $tt->error() . "n";
Adding the sort vmethod
[%# Using a vmethod to do a simple sort on a select list %]

<! [% component.name %] -->

<select>
[% FOREACH s IN suppliers.sort %]
<option value="s"/>[% s %]
[% END %]
</select>

<table>
<tr><th>No</th><th>Something</th><th>Something Else</th></tr>
[% FOREACH r IN rows %]
<tr><td> ([% loop.index %])</td><td>[% r.something FILTER html_entity %]</td><td>[%
r.something_else FILTER html_entity FILTER upper %]</td></tr>
[% END %]
</table>
<!-- End [% component.name %] -->
Using the Format plugin on
               currency

No        Something                  Something Else   Cost
(0)       apples                     1 KG             £5.67
(1)       pears                      2 KG             £3.33333333333333


(2)       turnips > pears > grapes   2 & 3 KG         £4.123


(3)       <>peas                     2 & 3 KG         £2
(4)       apricots                   2 & 3 KG         £3
The script with the plugin
[% USE money=format('%.2f') -%]

<select>
[% FOREACH s IN suppliers.sort %]
<option value="s"/>[% s %]
[% END %]
</select>

<table>
<tr><th>No</th><th>Something</th><th>Something Else</th><th>Cost</ht></tr>
[% FOREACH r IN rows %]
<tr><td> ([% loop.index %])</td><td>[% r.something FILTER html_entity %]</td><td>[%
r.something_else FILTER html_entity FILTER upper %]</td><td>&pound;[% money(r.cost)
%]</td></tr>
[% END %]
</table>
<!-- End [% component.name %] -->
Further Reading
                                                 Template Toolkit [Paperback]
                                                        •Darren Chamberlain,
                                                                  •Dave Cross,
                                                              •Andy Wardley




Also documented on MetaCPAN: https://metacpan.org/

It has its own website: http://www.template-toolkit.org/

226 plugins listed on MetaCPAN

Introduction to Template::Toolkit

  • 1.
    Introduction To Template::Toolkit Introduction Basics: Part One Exercises (Hands On) Basics: Part Two Exercises (Hands On) References and Further Reading
  • 2.
    Introduction •Who am I? •Whatlevel of Perl expertise is required? •What is Template::Toolkit?
  • 3.
  • 4.
    Template for aletter Dear [% name %], It has come to our attention that your account is in arrears to the sum of [% debt %]. Please settle your account before [% deadline %] or we will be forced to revoke your Licence to Thrill. The Management.
  • 5.
    Perl script tocall it use strict; use Template; my $tt = Template->new({ INCLUDE_PATH => '/usr/local/templates', INTERPOLATE => 1, }) || die "$Template::ERRORn"; my $vars = { name => 'Count Edward van Halen', debt => '3 riffs and a solo', deadline => 'the next chorus', }; $tt->process('letters/overdrawn', $vars) || die $tt->error(), "n";
  • 6.
    Multiple letters [% FOREACHd IN debtors %] Dear [% d.name %], It has come to our attention that your account is in arrears to the sum of [% d.debt %]. Please settle your account before [% d.deadline %] or we will be forced to revoke your Licence to Thrill. The Management. [% END %]
  • 7.
    Perl fragment my $vars= { debtors => [ { name => 'Count Edward van Halen', debt => '3 riffs and a solo', deadline => 'the next chorus', }, { name => ‘Baron Eric Clapton', debt => ‘1 badge', deadline => ‘tomorrow', } ] }; $tt->process('letters/overdrawn', $vars) || die $tt->error(), "n";
  • 8.
    Multiple letters withlogic [% FOREACH d = debtors %] Dear [% d.name %], It has come to our attention that your account is in arrears to the sum of [% d.debt %]. Please settle your account before [% d.deadline %] or we will be forced to revoke your Licence to Thrill. [% IF d.final %]This is your last chance.[% END %] The Management. [% END %]
  • 9.
    Perl fragment my $debtors= [ { name => 'Count Edward van Halen', debt => '3 riffs and a solo', deadline => 'the next chorus', }, { name => ‘Baron Eric Clapton', debt => ‘1 badge', deadline => ‘tomorrow', final => 1 } ]; $tt->process('letters/overdrawn', $debtors) || die $tt->error(), "n";
  • 10.
    Boolean logic: IFELSE [% IF age < 10 %] [% ELSIF age < 18 %] [% ELSE %] [% END %] The following conditional and boolean operators may be used: == != < <= > >= && || ! and or not
  • 11.
    Boolean logic: SWITCH [%SWITCH myvar %] [% CASE 'value1' %] [% CASE ['value2', 'value3'] %] # multiple values [% CASE myhash.keys %] # ditto [% CASE %] # default [% END %]
  • 12.
    Alternative contents [% FOREACHd IN debtors %] Dear [% d.name %], Thank you for your recent payment of [% d.debt %]. Your account is now clear and you may continue to thrill. The Management. [% END %]
  • 13.
    Include other templates [%FOREACH d IN debtors %] Dear [% d.name %], [% IF d.angry -%] [% INCLUDE angry_letter.tt2 %] [% ELSE -%] [% INCLUDE grateful_letter.tt2 %] [% END -%] The Management. [% END %]
  • 14.
    Perl fragment my $debtors= [ { name => 'Count Edward van Halen', debt => '3 riffs and a solo', deadline => 'the next chorus', }, { name => ‘Baron Eric Clapton', debt => ‘1 badge', deadline => ‘tomorrow', final => 1, angry => 1, } ]; $tt->process('letters/overdrawn', $debtors) || die $tt->error(), "n";
  • 15.
  • 16.
    Part Two •Adding awrapper for the header and footer •Adding simple filters to encoding entities or control case •Using a vmethod to add row numbers •Sorting a select list •Formatting currency
  • 17.
    Adding a wrapper Content-Type:text/html; charset=ISO-8859-1 <html> <head> </head> <body> <h1>Introduction to TT</h2> [%# Header and footer will be the same on all pages %] <table> <tr><th>Something</th><th>Something Else</th></tr> [% FOREACH r IN rows %] <tr><td>[% r.something %]</td><td>[% r.something_else %]</td></tr> [% END %] </table> </body> </html>
  • 18.
    Move the commoncontent to wrapper.tt Content-Type: text/html; charset=ISO-8859-1 <html> <head> </head> <body> <h1>Introduction to TT</h2> [% content %] </body> </html>
  • 19.
    Tell TT aboutthe wrapper #! /usr/bin/perl use strict; use warnings; use CGI; use Template; # Using a wrapper. my $tt = Template->new( { INCLUDE_PATH => '/home/duncan/tt/htdocs/tt2', INTERPOLATE => 1, WRAPPER => 'wrapper.tt2' } ) || die "$Template::ERRORn"; $tt->process( 's2.tt2', { rows => [ { something => 'apples', something_else => '1 kg' }, { something => 'pears', something_else => '2 kg' } ] } ) || die $tt->error() . "n";
  • 20.
    Dodgy data my $tt= Template->new( { INCLUDE_PATH => '/home/duncan/tt/htdocs/tt2', INTERPOLATE => 1, WRAPPER => 'wrapper.tt2' } ) || die "$Template::ERRORn"; $tt->process( 's2.tt2', { rows => [ { something => 'apples', something_else => '1 kg' }, { something => 'pears', something_else => '2 kg' }, { something => 'turnips > pears > grapes', something_else => '2 & 3 kg' }, { something => '<script type="text/javascript">alert("Hi");</script>', something_else => '2 & 3 kg' }, ] } ) || die $tt->error() . "n";
  • 21.
    Encode it! <! [%component.name %] --> [%# Using a filter to encode entities and control case. %] <table> <tr><th>Something</th><th>Something Else</th></tr> [% FOREACH r IN rows %] <tr><td>[% r.something FILTER html_entity %]</td><td>[% r.something_else FILTER html_entity FILTER upper %]</td></tr> [% END %] </table> <!-- End [% component.name %] -->
  • 22.
    That’s better <tr><td>apples</td><td>1 KG</td></tr><tr><td>pears</td><td>2 KG</td></tr> <tr><td>turnips &gt; pears &gt; grapes</td><td>2 &amp; 3 KG</td></tr> <tr><td>&lt;script type=&quot;text/javascript&quot;&gt;alert(&quot;Hi&quot;);&lt;/script&gt;</td><td>2 &amp; 3 KG</td></tr>
  • 23.
    Use a vmethodto add row numbers <! [% component.name %] --> [%# Using a vmethod to add row number %] <table> <tr><th>No</th><th>Something</th><th>Something Else</th></tr> [% FOREACH r IN rows %] <tr><td>([% loop.index %])</td> <td>[% r.something FILTER html_entity %]</td><td>[% r.something_else FILTER html_entity FILTER upper %]</td></tr> [% END %] </table> <!-- End [% component.name %] -->
  • 24.
    With row numbers No Something Something Else (0) apples 1 KG (1) pears 2 KG (2) turnips > pears > grapes 2 & 3 KG (3) <>peas 2 & 3 KG (4) apricots 2 & 3 KG
  • 25.
    Use a vmethodto do a simple sort on a select list. my $tt = Template->new( { INCLUDE_PATH => '/home/duncan/tt/htdocs/tt2', INTERPOLATE => 1, WRAPPER => 'wrapper.tt2' } ) || die "$Template::ERRORn"; $tt->process( 's5.tt2', { rows => [ { something => 'apples', something_else => '1 kg' }, { something => 'pears', something_else => '2 kg' }, { something => 'turnips > pears > grapes', something_else => '2 & 3 kg' }, { something => '<>peas', something_else => '2 & 3 kg' }, { something => 'apricots', something_else => '2 & 3 kg' }, ], suppliers => [ 'Fred', 'Alan', 'Joe', 'Bert' ] } ) || die $tt->error() . "n";
  • 26.
    Adding the sortvmethod [%# Using a vmethod to do a simple sort on a select list %] <! [% component.name %] --> <select> [% FOREACH s IN suppliers.sort %] <option value="s"/>[% s %] [% END %] </select> <table> <tr><th>No</th><th>Something</th><th>Something Else</th></tr> [% FOREACH r IN rows %] <tr><td> ([% loop.index %])</td><td>[% r.something FILTER html_entity %]</td><td>[% r.something_else FILTER html_entity FILTER upper %]</td></tr> [% END %] </table> <!-- End [% component.name %] -->
  • 27.
    Using the Formatplugin on currency No Something Something Else Cost (0) apples 1 KG £5.67 (1) pears 2 KG £3.33333333333333 (2) turnips > pears > grapes 2 & 3 KG £4.123 (3) <>peas 2 & 3 KG £2 (4) apricots 2 & 3 KG £3
  • 28.
    The script withthe plugin [% USE money=format('%.2f') -%] <select> [% FOREACH s IN suppliers.sort %] <option value="s"/>[% s %] [% END %] </select> <table> <tr><th>No</th><th>Something</th><th>Something Else</th><th>Cost</ht></tr> [% FOREACH r IN rows %] <tr><td> ([% loop.index %])</td><td>[% r.something FILTER html_entity %]</td><td>[% r.something_else FILTER html_entity FILTER upper %]</td><td>&pound;[% money(r.cost) %]</td></tr> [% END %] </table> <!-- End [% component.name %] -->
  • 29.
    Further Reading Template Toolkit [Paperback] •Darren Chamberlain, •Dave Cross, •Andy Wardley Also documented on MetaCPAN: https://metacpan.org/ It has its own website: http://www.template-toolkit.org/ 226 plugins listed on MetaCPAN