Template::Manual::Internals

TriggerTek Logo
abcdefghijklmnopqrstuvwxyz_
Template::Manual::IntUserlContributed Perl DocuTemplate::Manual::Internals(3)



NAME
       Template::Manual::Internals - Template Toolkit internals

Introduction
       This section of the documentation is aimed at developers wishing to
       know more about how the Template Toolkit works on the inside in order
       to extend or adapt it to their own needs.

       If that doesn’t sound like you then you probably don’t need to read
       this.  There is no test afterwards.

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 (available 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::Tem-
       plate" front end, for example, handles "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 template 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 mod-
       ules or dependencies, and so on.	 Template::Service hides that all
       away and makes everything look simple to the outsider. It provides
       extra features, like "PRE_PROCESS", "PROCESS" and "POST_PROCESS", and
       also provides the error recovery mechanism via "ERROR".	You ask it to
       process a template and it takes care of everything for you. The "Tem-
       plate::Service::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 schedul-
       ing 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 Template::Context
       object.

       Template::Context is the runtime engine for the Template Toolkit - the
       module that hangs everything together in the lower levels of the Tem-
       plate 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 han-
       dle) 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 tem-
       plates into executable Perl code (but more on that later).

       Thankfully, all of this complexity is hidden away behind a simple tem-
       plate() 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 exception.

       A Template::Document is a thin object wrapper around a compiled tem-
       plate subroutine. The object implements a process() method which per-
       forms a little bit of housekeeping and then calls the template subrou-
       tine. 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 variables get defined - more on that
       shortly) to set any template variable definitions specified as the
       second argument 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 provides 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 Tem-
       plate::Context object receive calls from the outside, i.e. those orig-
       inating in user code calling the process() method on a Template
       object, but it also receives calls from the inside, i.e. those origi-
       nating in template directives of the form "[% PROCESS template %]".

       Before we move on to that, here’s a simple structure diagram 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->process($template, $vars);
	   +----------+	       print $out or send it to $self->OUTPUT;
		 │	   }
		 │
		 │	   package Template::Service;
		 │
		 │	   sub process($template, \%vars) {
		 │	       try {
	   +----------+		   foreach $p in @self->PRE_PROCESS
	   │ Service  │		       $self->CONTEXT->process($p, $vars);
	   +----------+
		 │		   $self->CONTEXT->process($template, $vars);
		 │
		 │		   foreach $p @self->POST_PROCESS
		 │		       $self->CONTEXT->process($p, $vars);
		 │	       }
		 │	       catch {
		 │		   $self->CONTEXT->process($self->ERROR);
		 │	       }
		 │	   }
		 │
		 V	   package Template::Context;
	   +----------+
	   │ Context  │	   sub process($template, \%vars) {
	   +----------+	       # fetch compiled template
		 │	       $template = $self->template($template)
		 │	       # update stash
		 │	       $self->STASH->update($vars);
		 │	       # process template
		 │	       $template->process($self)
		 │	   }
		 V
	   +----------+	   package Template::Document;
	   │ Document │
	   +----------+	   sub process($context) {
			       $output = &{ $self->BLOCK }($context);
			   }

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 com-
       piled template is just a regular Perl sub-routine.  Here’s a very sim-
       ple one.

	   sub my_compiled_template {
	       return "This is a compiled template.\n";
	   }

       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 implemented\n";
	   }

       If it wants to get fancy, it can raise an error as a Template::Excep-
       tion 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 generate static
       output or raise errors. They may want to inspect variable values, pro-
       cess another template, load a plugin, run a filter, and so on. When-
       ever a template subroutine is called, it gets passed a reference to a
       Template::Context object. It is through this context object that tem-
       plate code can access the features of the Template Toolkit.

       We described earlier how the Template::Service object calls on Tem-
       plate::Context to handle a process() request from the outside. We can
       make a similar request on a context 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’ })
			  . "\nsome content\n"
			  . $context->process(’footer’);
	   }

       This is then roughly equivalent to a source template something like
       this:

	   [% PROCESS header
	       title = ’Hello World’
	   %]
	   some content
	   [% PROCESS footer %]

       Template variables are stored in, and managed by a Template::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 fol-
       lowing source template:

	   <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’) . ’</title>’;
	   }

       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 ref-
       erence passed as the second argument to get().

	   [% title(’The Cat Sat on the Mat’) %]

       This translates to the stash 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 variable 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.

       Alongside the process() method lies the include() method. Just as with
       the "PROCESS" / "INCLUDE" directives, the key difference is in vari-
       able 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 pre-
       viously existing variables are still defined, but any changes made to
       variables, including setting the new variable values passed aas argu-
       ments 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 previous vari-
       able state by decloning the stash.

       The context also provides an insert() method to implement the "INSERT"
       directive, but no "wrapper()" method. This functionality can be imple-
       mented 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().

	   # TT USE directive
	   [% USE foo = Bar(10) %]

	   # equivalent Perl
	   $stash->set(’foo’, $context->plugin(’Bar’, [10]));

	   # TT FILTER block
	   [% FILTER bar(20) %]
	      blah blah blah
	   [% END %]

	   # equivalent Perl
	   my $filter = $context->filter(’bar’, [20]);
	   &$filter(’blah blah blah’);

       Pretty much everything else you might want to do in a template can be
       done in Perl code. Things like "IF", "UNLESS", "FOREACH" and so on all
       have direct counterparts in Perl.

	   # TT IF directive
	   [% IF msg %]
	      Message: [% msg %]
	   [% END %];

	   # equivalent Perl
	   if ($stash->get(’msg’)) {
	       $output .=  ’Message: ’;
	       $output .= $stash->get(’msg’);
	   }

       The best way to get a better understanding of what’s going on under-
       neath the hood is to set the $Template::Parser::DEBUG flag to a true
       value and start processing 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 $Template::Direc-
       tive::PRETTY option to have the Perl pretty-printed for human consump-
       tion.

	   use Template;
	   use Template::Parser;
	   use Template::Directive;

	   $Template::Parser::DEBUG = 1;
	   $Template::Directive::PRETTY = 1;

	   my $template = Template->new();
	   $template->process(\*DATA, { 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 context\n";
	       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 .=  "\n";
	       } };
	       if ($@) {
		   $error = $context->catch($@, \$output);
		   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-numerical 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 existing 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 adding a new module, a plugin module,
       for example, 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 documentation interspersed throughout the code. In my not-so-
       humble opinion, it makes both the code and the documentation harder to
       read (same kinda problem as embedding Perl in HTML).

       To share your changes with the rest of the world, you’ll need to pre-
       pare 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 distri-
       bution 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-Toolkit-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 distribution, 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. "Tem-
       plate-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 follow-
       ing:

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



perl v5.8.8			  2007-06-05   Template::Manual::Internals(3)