File: Internals.pod

package info (click to toggle)
libtemplate-perl 2.14-1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k, sarge
  • size: 5,496 kB
  • ctags: 667
  • sloc: perl: 15,349; makefile: 62; xml: 7; sh: 5
file content (556 lines) | stat: -rw-r--r-- 21,228 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
#============================================================= -*-perl-*-
#
# Template::Manual::Internals
#
# DESCRIPTION
#   This document provides an overview of the internal architecture 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 wishing to delve into the source code to
#   find out how it all works.
#
# AUTHOR
#   Andy Wardley  <abw@andywardley.com>
#
# 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::Manual::Internals - Template Toolkit internals

=head1 DESCRIPTION

This document provides an overview of the internal architecture 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 wishing to delve into the source code to find out how it all
works.

=head2 Outside Looking In

The B<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::Template
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
Template::Service (or subclass) which does all the work.

The B<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
modules 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 
Template::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
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 Template::Context object.

B<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 exception.

A B<Template::Document> is a thin object wrapper around a compiled 
template subroutine.  The object implements a process() method which
performs a little bit of housekeeping and then calls the template 
subroutine.  The object also defines template metadata (defined in 
C<[% META ... %]> directives) and has a block() method which returns
a hash of any additional C<[% 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
document 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 Template::Context
object receive calls from the I<outside>, i.e. those originating in user
code calling the process() method on a Template object, but it also 
receives calls from the I<inside>, i.e. those originating in template
directives of the form C<[% 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);
		    }
        

=head2 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.\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::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 generate 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 subroutine is called, it gets passed a reference
to a Template::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 I<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
I<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 B<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
following 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
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 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 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 I<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 previous variable state by I<decloning> the stash.

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

    [% 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 C<$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 C<$Template::Directive::PRETTY> option to
have the Perl pretty-printed 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(\*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;
    }


=head1 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 C<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 'docsrc' bundle from which all the 
Template Toolkit documentation 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 example, then it's
OK to include the POD documentation in with the module, but I<please>
write it all in one piece at the end of the file, I<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).

Aesthetics aside, if I do want to extract the documentation 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-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. 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

=head1 AUTHOR

Andy Wardley E<lt>abw@andywardley.comE<gt>

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




=head1 VERSION

Template Toolkit version 2.14, released on 04 October 2004.

=head1 COPYRIGHT

  Copyright (C) 1996-2004 Andy Wardley.  All Rights Reserved.
  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.



=cut

# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4: