internals(3)

NAME

Template::Manual::Internals - Template Toolkit internals

DESCRIPTION

This document provides an overview of the internal archi
tecture of the Template Toolkit. It is a work in progress
and is far from complete, currently providing little more
than an overview of how the major components fit together.
Nevertheless, it's a good starting point for anyone wish
ing to delve into the source code to find out how it all
works.

Outside Looking In

The Template module is simply a front end module which creates and uses a Template::Service and pipes the output
wherever you want it to go (STDOUT by default, or maybe a
file, scalar, etc). The Apache::Template module (avail
able separately from CPAN) is another front end. That
creates a Template::Service::Apache object, calls on it as
required and sends the output back to the relevant
Apache::Request object.

These front-end modules are really only there to handle
any specifics of the environment in which they're being
used. The Apache::Template front end, for example, han
dles Apache::Request specifics and configuration via the
httpd.conf. The regular Template front-end deals with
STDOUT, variable refs, etc. Otherwise it is Tem
plate::Service (or subclass) which does all the work.

The Template::Service module provides a high-quality tem plate delivery service, with bells, whistles, signed up
service level agreement and a 30-day no quibble money back
guarantee. "Have a good time, all the time", that's our
motto.

Within the lower levels of the Template Toolkit, there are
lots of messy details that we generally don't want to have
to worry about most of the time. Things like templates
not being found, or failing to parse correctly, uncaught
exceptions being thrown, missing plugin modules or depen
dencies, and so on. Template::Service hides that all away
and makes everything look simple to the outsider. It pro
vides extra features, like PRE_PROCESS, PROCESS and
POST_PROCESS, and also provides the error recovery mecha
nism via ERROR. You ask it to process a template and it
takes care of everything for you. The Template::Ser
vice::Apache module goes a little bit further, adding some
extra headers to the Apache::Request, setting a few extra
template variables, and so on.

For the most part, the job of a service is really just one
of scheduling and dispatching. It receives a request in
the form of a call to its process() method and schedules the named template specified as an argument, and possibly
several other templates (PRE_PROCESS, etc) to be processed
in order. It doesn't actually process the templates
itself, but instead makes a process() call against a Tem plate::Context object.

Template::Context is the runtime engine for the Template Toolkit - the module that hangs everything together in the
lower levels of the Template Toolkit and that one that
does most of the real work, albeit by crafty delegation to
various other friendly helper modules.

Given a template name (or perhaps a reference to a scalar
or file handle) the context process() method must load and compile, or fetch a cached copy of a previously compiled
template, corresponding to that name. It does this by
calling on a list of one or more Template::Provider
objects (the LOAD_TEMPLATES posse) who themselves might
get involved with a Template::Parser to help turn source
templates into executable Perl code (but more on that
later). Thankfully, all of this complexity is hidden away
behind a simple template() method. You call it passing a template name as an argument, and it returns a compiled
template in the form of a Template::Document object, or
otherwise raises an exeception.

A Template::Document is a thin object wrapper around a compiled template subroutine. The object implements a
process() method which performs a little bit of housekeep ing and then calls the template subroutine. The object
also defines template metadata (defined in "[% META ...
%]" directives) and has a block() method which returns a hash of any additional "[% BLOCK xxxx %]" definitions
found in the template source.

So the context fetches a compiled document via its own
template() method and then gets ready to process it. It first updates the stash (the place where template vari
ables get defined - more on that shortly) to set any tem
plate variable definitions specified as the second argu
ment by reference to hash array. Then, it calls the docu
ment process() method, passing a reference to itself, the context object, as an argument. In doing this, it pro
vides itself as an object against which template code can
make callbacks to access runtime resources and Template
Toolkit functionality.

What we're trying to say here is this: not only does the
Template::Context object receive calls from the outside, i.e. those originating in user code calling the process() method on a Template object, but it also receives calls
from the inside, i.e. those originating in template direc
tives of the form "[% PROCESS template %]".

Before we move on to that, here's a simple structure dia
gram showing the outer layers of the Template Toolkit
heading inwards, with pseudo code annotations showing a
typical invocation sequence.
,--------.
| Caller | use Template;
`--------' my $tt = Template->new( ... );
| $tt->process($template, vars);
Outside
- - - - | - - - - - - - - - - - - - - - - - - - - - -
- - - - - T T
| package Template;
Inside
V
+----------+ sub process($template, vars) {
| Template | $out = $self->SERVICE->pro
cess($template, $vars);
+----------+ print $out or send it to
$self->OUTPUT;
| }
| package Template::Service;
| sub process($template, vars) {
| try {
+----------+ foreach $p in @self->PRE_PRO
CESS
| Service | $self->CONTEXT->pro
cess($p, $vars);
+----------+
| $self->CONTEXT->process($tem
plate, $vars);
| foreach $p @self->POST_PROCESS
| $self->CONTEXT->pro
cess($p, $vars);
| }
| catch {
| $self->CONTEXT->pro
cess($self->ERROR);
| }
| }
V package Template::Context;
+----------+
| Context | sub process($template, vars) {
+----------+ # fetch compiled template
| $template = $self->template($tem
plate)
| # update stash
| $self->STASH->update($vars);
| # process template
| $template->process($self)
| }
V
+----------+ package Template::Document;
| Document
+----------+ sub process($context) {
$output = &{ $self->BLOCK }($con
text);
}
Inside Looking Out
To understand more about what's going on in these lower
levels, we need to look at what a compiled template looks
like. In fact, a compiled template is just a regular Perl
sub-routine. Here's a very simple one.

sub my_compiled_template {
return "This is a compiled template.0;
}
You're unlikely to see a compiled template this simple
unless you wrote it yourself but it is entirely valid.
All a template subroutine is obliged to do is return some
output (which may be an empty of course). If it can't for
some reason, then it should raise an error via die().

sub my_todo_template {
die "This template not yet implemented0;
}
If it wants to get fancy, it can raise an error as a Tem
plate::Exception object. An exception object is really
just a convenient wrapper for the 'type' and 'info'
fields.

sub my_solilique_template {
die Template::Exception->new('yorrick', 'Fellow of
infinite jest');
}
Templates generally need to do a lot more than just gener
ate static output or raise errors. They may want to
inspect variable values, process another template, load a
plugin, run a filter, and so on. Whenever a template sub
routine is called, it gets passed a reference to a Tem
plate::Context object. It is through this context object
that template code can access the features of the Template
Toolkit.
We described earlier how the Template::Service object
calls on Template::Context to handle a process() request from the outside. We can make a similar request on a con text to process a template, but from within the code of
another template. This is a call from the inside.

sub my_process_template {
my $context = shift;
my $output = $context->process('header', { title
=> 'Hello World' })
. "0ome content0
. $context->process('footer');
}
This is then roughly equivalent to a source template some
thing like this:

[% PROCESS header
title = 'Hello World'
%]
some content
[% PROCESS footer %]
Template variables are stored in, and managed by a Tem
plate::Stash object. This is a blessed hash array in which template variables are defined. The object wrapper
provides get() and set() method which implement all the magical.variable.features of the Template Toolkit.
Each context object has its own stash, a reference to
which can be returned by the appropriately named stash() method. So to print the value of some template variable,
or for example, to represent the following source tem
plate:

<title>[% title %]</title>
we might have a subroutine definition something like this:

sub {
my $context = shift;
my $stash = $context->stash();
return '<title>' . $stash->get('title') . '</ti
tle>';
}
The stash get() method hides the details of the underlying
variable types, automatically calling code references,
checking return values, and performing other such tricks.
If 'title' happens to be bound to a subroutine then we can
specify additional parameters as a list reference passed
as the second argument to get().

[% title('The Cat Sat on the Mat') %]
This translates to the stash get() call:

$stash->get('title', ['The Cat Sat on the Mat']);
Dotted compound variables can be requested by passing a
single list reference to the get() method in place of the
variable name. Each pair of elements in the list should
correspond to the variable name and reference to a list of
arguments for each dot-delimited element of the variable.

[% foo(1, 2).bar(3, 4).baz(5) %]
is thus equivalent to

$stash->get([ foo => [1,2], bar => [3,4], baz => [5]
]);
If there aren't any arguments for an element, you can
specify an empty, zero or null argument list.

[% foo.bar %]
$stash->get([ 'foo', 0, 'bar', 0 ]);
The set() method works in a similar way. It takes a vari
able name and a variable value which should be assigned to
it.

[% x = 10 %]
$stash->set('x', 10);
[% x.y = 10 %]
$stash->set([ 'x', 0, 'y', 0 ], 10);
So the stash gives us access to template variables and the
context provides the higher level functionality. Along
side the process() method lies the include() method. Just as with the PROCESS / INCLUDE directives, the key differ
ence is in variable localisation. Before processing a
template, the process() method simply updates the stash to set any new variable definitions, overwriting any existing
values. In contrast, the include() method creates a copy of the existing stash, in a process known as cloning the stash, and then uses that as a temporary variable store.
Any previously existing variables are still defined, but
any changes made to variables, including setting the new
variable values passed aas arguments will affect only the
local copy of the stash (although note that it's only a
shallow copy, so it's not foolproof). When the template
has been processed, the include() method restores the pre vious variable state by decloning the stash.
The context also provides an insert() method to implement the INSERT directive, but no wrapper() method. This func tionality can be implemented by rewriting the Perl code
and calling include().

[% WRAPPER foo -%]
blah blah [% x %]
[%- END %]
$context->include('foo', {
content => 'blah blah ' . $stash->get('x'),
});
Other than the template processing methods process(), include() and insert(), the context defines methods for fetching plugin objects, plugin(), and filters, filter().

[% USE foo = Bar(10) %]
$stash->set('foo', $context->plugin('Bar', [10]));
[% FILTER bar(20) %]
blah blah blah
[% END %]
my $filter = $stash->filter('bar', [20]);
&$filter('blah blah blah');
Pretty much everything else you might want to do in a tem
plate can be done in Perl code. Things like IF, UNLESS,
FOREACH and so on all have direct counterparts in Perl.

[% IF msg %]
Message: [% msg %]
[% END %];
if ($stash->get('msg')) {
$output .= 'Message: ';
$output .= $stash->get('msg');
}
The best way to get a better understanding of what's going
on underneath the hood is to set the $Tem
plate::Parser::DEBUG flag to a true value and start pro
cessing templates. This will cause the parser to print
the generated Perl code for each template it compiles to
STDERR. You'll probably also want to set the $Tem
plate::Directive::PRETTY option to have the Perl prettyprinted for human consumption.

use Template;
use Template::Parser;
use Template::Directive;
$Template::Parser::DEBUG = 1;
$Template::Directive::PRETTY = 1;
my $template = Template->new();
$template->process(ATA, { cat => 'dog', mat => 'log'
});
__DATA__
The [% cat %] sat on the [% mat %]
The output sent to STDOUT remains as you would expect:

The dog sat on the log
The output sent to STDERR would look something like this:

compiled main template document block:
sub {
my $context = shift || die "template sub called
without context0;
my $stash = $context->stash;
my $output = '';
my $error;
eval { BLOCK: {
$output .= "The ";
$output .= $stash->get('cat');
$output .= " sat on the ";
$output .= $stash->get('mat');
$output .= "0;
} };
if ($@) {
$error = $context->catch($@, utput);
die $error unless $error->type eq 'return';
}
return $output;
}

HACKING ON THE TEMPLATE TOOLKIT

Please feel free to hack on the Template Toolkit. If you
find a bug that needs fixing, if you have an idea for
something that's missing, or you feel inclined to tackle
something on the TODO list, then by all means go ahead and
do it!

If you're contemplating something non-trivial then you'll
probably want to bring it up on the mailing list first to
get an idea about the current state of play, find out if
anyone's already working on it, and so on.

When you start to hack on the Template Toolkit, please
make sure you start from the latest developer release.
Stable releases are uploaded to CPAN and have all-numeri
cal version numbers, e.g. 2.04, 2.05. Developer releases
are available from the Template Toolkit web site and have
a character suffix on the version, e.g. 2.04a, 2.04b, etc.

Once you've made your changes, please remember to update
the test suite by adding extra tests to one of the exist
ing test scripts in the 't' sub-directory, or by adding a
new test script of your own. And of course, run "make
test" to ensure that all the tests pass with your new
code.

Don't forget that any files you do add will need to be
added to the MANIFEST. Running 'make manifest' will do
this for you, but you need to make sure you haven't got
any other temporary files lying around that might also get
added to it.

Documentation is often something that gets overlooked but
it's just as important as the code. If you're updating
existing documentation then you should download the 'doc
src' bundle from which all the Template Toolkit documenta
tion is built and make your changes in there. It's also
available from the Template Toolkit web site. See the
README distributed in the archive for further information.

If you're adding a new module, a plugin module, for exam
ple, then it's OK to include the POD documentation in with
the module, but please write it all in one piece at the
end of the file, after the code (just look at any other
Template::* module for an example). It's a religious
issue, I know, but I have a strong distaste for POD docu
mentation interspersed throughout the code. In my not-sohumble opinion, it makes both the code and the documenta
tion harder to read (same kinda problem as embedding Perl
in HTML).

Aesthetics aside, if I do want to extract the documenta
tion into the docsrc bundle then it's easy for me to do it
if it's all written in one chunk and extremely tedious if
not. So for practical reasons alone, please keep Perl and
POD sections separate. Comment blocks within the code are
of course welcome.

To share your changes with the rest of the world, you'll
need to prepare a patch file. To do this you should have
2 directories side-by-side, one which is the original,
unmodified distribution directory for the latest developer
release, and the other is a copy of that same directory
which includes your changes.

The following example shows a typical hacking session.
First we unpack the latest developer release.
$ tar zxf Template-Toolkit-2.05c.tar.gz
At this point, it's a good idea to rename the directory to
give some indicate of what it contains.

$ mv Template-Toolkit-2.05c Template-Toolkit-2.05c
abw-xyz-hack
Then go hack!

$ cd Template-Toolkit-2.05c-abw-xyz-hack

[ hacking ]
$ cd ..
When you're all done and ready to prepare a patch, unpack
the distribution archive again so that you've got the
original to diff against your new code.

$ tar zxf Template-Toolkit-2.05c.tar.gz
You should now have an original distribution directory and
a modified version of that same directory, side-by-side.

$ ls
Template-Toolkit-2.05c Template-Toolkit-2.05c-abw
xyz-hack
Now run diff and save the output into an appropriately
named patch file.

$ diff -Naur Template-Toolkit-2.05c Template-Toolk
it-2.05c-abw-xyz-hack > patch-TT205c-abw-xyz-hack
You can then post the generated patch file to the mailing
list, describing what it does, why it does it, how it does
it and any other relevant information.
If you want to apply someone else's patch then you should
start with the same original distribution source on which
the patch is based. From within the root of the distribu
tion, run 'patch' feeding in the patch file as standard
input. The 'p1' option is required to strip the first
element of the path name (e.g.
Template-Toolkit-2.05c/README becomes README which is then
the correct path).

$ tar zxf Template-Toolkit-2.05c.tar.gz
$ cd Template-Toolkit-2.05c
$ patch -p1 < ../patch-TT205c-abw-xyz-hack
The output generated by 'patch' should be something like
the following:

patching file README
patching file lib/Template.pm
patching file lib/Template/Provider.pm
patching file t/provider.t

AUTHOR

Andy Wardley <abw@andywardley.com>

<http://www.andywardley.com/|http://www.andywardley.com/>

VERSION

Template Toolkit version 2.08, released on 30 July 2002.

COPYRIGHT

Copyright (C) 1996-2002 Andy Wardley. All Rights Re
served.
Copyright (C) 1998-2002 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.
Copyright © 2010-2025 Platon Technologies, s.r.o.           Index | Man stránky | tLDP | Dokumenty | Utilitky | O projekte
Design by styleshout