File Coverage

blib/lib/Tenjin.pm
Criterion Covered Total %
statement 82 108 75.9
branch 22 46 47.8
condition 17 28 60.7
subroutine 15 17 88.2
pod 11 11 100.0
total 147 210 70.0


line stmt bran cond sub pod time code
1             package Tenjin;
2              
3             # ABSTRACT: Fast templating engine with support for embedded Perl.
4              
5 7     7   185922 use strict;
  7         18  
  7         300  
6 7     7   43 use warnings;
  7         14  
  7         212  
7 7     7   40 use Carp;
  7         18  
  7         506  
8              
9 7     7   4112 use Tenjin::Context;
  7         21  
  7         326  
10 7     7   5562 use Tenjin::Template;
  7         23  
  7         709  
11 7     7   4873 use Tenjin::Preprocessor;
  7         13  
  7         10548  
12              
13             our $VERSION = "0.070001";
14             $VERSION = eval $VERSION;
15              
16             our $USE_STRICT = 0;
17             our $ENCODING = 'UTF-8';
18             our $BYPASS_TAINT = 1; # unset if you like taint mode
19             our $TEMPLATE_CLASS = 'Tenjin::Template';
20             our $CONTEXT_CLASS = 'Tenjin::Context';
21             our $PREPROCESSOR_CLASS = 'Tenjin::Preprocessor';
22             our $TIMESTAMP_INTERVAL = 10;
23              
24             =head1 NAME
25              
26             Tenjin - Fast templating engine with support for embedded Perl.
27              
28             =head1 VERSION
29              
30             version 0.070001
31              
32             =head1 SYNOPSIS
33              
34             use Tenjin;
35              
36             $Tenjin::USE_STRICT = 1; # use strict in the embedded Perl inside
37             # your templates. Recommended, but not used
38             # by default.
39              
40             $Tenjin::ENCODING = "UTF-8"; # set the encoding of your template files
41             # to UTF-8. This is the default encoding used
42             # so there's no need to do this if your
43             # templates really are UTF-8.
44              
45             my $engine = Tenjin->new(\%options);
46             my $context = { title => 'Tenjin Example', items => [qw/AAA BBB CCC/] };
47             my $filename = 'file.html';
48             my $output = $engine->render($filename, $context);
49             print $output;
50              
51             =head1 DESCRIPTION
52              
53             Tenjin is a very fast and full-featured templating engine, implemented in
54             several programming languages, among them Perl.
55              
56             The Perl version of Tenjin supports embedded Perl code, nestable layout template,
57             inclusion of other templates inside a template, capturing parts of or the entire
58             template output, file and memory caching, template arguments and preprocessing.
59              
60             The original version of Tenjin is developed by Makoto Kuwata. This CPAN
61             version is developed by Ido Perlmuter and differs from the original in a
62             few key aspects:
63              
64             =over
65              
66             =item * Code is entirely revised, packages are separated into modules, with
67             a smaller number of packages than the original version. In particular, the
68             Tenjin::Engine module no longer exists, and is now instead just the Tenjin
69             module (i.e. this one).
70              
71             =item * Support for rendering templates from non-file sources (such as
72             a database) is added.
73              
74             =item * Ability to set the encoding of your templates is added (Tenjin will decode
75             template files according to this encoding; by default, Tenjin will decode
76              
77             =item * HTML is encoded and decoded using the L module,
78             instead of internally.
79              
80             =item * The C script is not provided, at least for now.
81              
82             =back
83              
84             To make it clear, the CPAN version of Tenjin might find itself diverting
85             a bit in the future from the original Tenjin's roadmap. Although my aim
86             is to be as compatible as possible (and this version is always updated
87             with features and changes from the original), I cannot guarantee it (but I'll
88             do my best). Please note that version 0.05 (and above) of this module is
89             NOT backwards compatible with previous versions.
90              
91             =head2 A NOTE ABOUT ENCODING
92              
93             When Tenjin opens template files, it will automatically decode their contents
94             according to the selected encoding (UTF-8 by default), so make sure your template
95             files are properly encoded. Tenjin also writes cache files of compiled template
96             structure. These will be automatically encoded according to the selected encoding.
97              
98             When it comes to UTF-8, it might interest you to know how Tenjin behaves:
99              
100             =over
101              
102             =item 1. "UTF-8" is the default encoding used. If for some reason, either before
103             running C<< Tenjin->new() >> or during, you provide an alternate spelling (such
104             as "utf8" or "UTF8"), Tenjin will convert it to UTF-8.
105              
106             =item 2. When reading files, Tenjin uses "<:encoding(UTF-8)", while when writing
107             files, Tenjin uses ">:utf8", as recommended by L.
108              
109             =back
110              
111             =head1 METHODS
112              
113             =head2 new( \%options )
114              
115             This creates a new instant of Tenjin. C<\%options> is a hash-ref
116             containing Tenjin's configuration options:
117              
118             =over
119              
120             =item * B - Array-ref of filesystem paths where templates will be searched
121              
122             =item * B - A string that will be automatically prepended to template names
123             when searching for them in the path. Empty by default.
124              
125             =item * B - The default extension to be automtically appended to template names
126             when searching for them in the path. Don't forget to include the
127             dot, such as '.html'. Empty by default.
128              
129             =item * B - If set to 1 (the default), compiled templates will be cached on the
130             filesystem (this means the template's code will be cached, not the completed rendered
131             output).
132              
133             =item * B - Enable template preprocessing (turned off by default). Only
134             use if you're actually using any preprocessed Perl code in your templates.
135              
136             =item * B - Name of a layout template that can be optionally used. If set,
137             templates will be automatically inserted into the layout template,
138             in the location where you use C<[== $_content ==]>.
139              
140             =item * B - Another way to make Tenjin use strict on embedded Perl code (turned
141             off by default).
142              
143             =item * B - Another way to set the encoding of your template files (set to "UTF-8"
144             by default).
145              
146             =back
147              
148             =cut
149              
150             sub new {
151 6     6 1 105 my ($class, $options) = @_;
152              
153 6         18 my $self = {};
154 6         25 foreach (qw[prefix postfix layout path cache preprocess templateclass strict encoding]) {
155 54         176 $self->{$_} = delete $options->{$_};
156             }
157 6 50       44 $self->{cache} = 1 unless defined $self->{cache};
158 6         17 $self->{init_opts_for_template} = $options;
159 6         20 $self->{templates} = {};
160 6 50       32 $self->{prefix} = '' unless $self->{prefix};
161 6 50       50 $self->{postfix} = '' unless $self->{postfix};
162              
163 6 50       36 $Tenjin::ENCODING = $self->{encoding}
164             if $self->{encoding};
165              
166             # if encoding is utf8, make sure it's spelled UTF-8 and not otherwise
167 6 50       71 $Tenjin::ENCODING = 'UTF-8'
168             if $Tenjin::ENCODING =~ m/^utf-?8$/i;
169              
170 6 50       31 $Tenjin::USE_STRICT = $self->{strict}
171             if defined $self->{strict};
172              
173 6         32 return bless $self, $class;
174             }
175              
176             =head2 render( $tmpl_name, [\%_context, $use_layout] )
177              
178             Renders a template whose name is identified by C<$tmpl_name>. Remember that a prefix
179             and a postfix might be added if they where set when creating the Tenjin instance.
180              
181             C<$_context> is a hash-ref containing the variables that will be available for usage inside
182             the templates. So, for example, if your C<\%_context> is C<< { message => 'Hi there' } >>, then you can use C<$message> inside your templates.
183              
184             C<$use_layout> is a flag denoting whether or not to render this template into a layout
185             template (when doing so, the template will be rendered, then the rendered output will be
186             added to the context hash-ref as '_content', and finally the layout template will be rendered with the revised context and returned.
187              
188             If C<$use_layout> is 1 (which is the default in case it is undefined),
189             then Tenjin will use the layout template that was set when creating the
190             Tenjin instance (via the 'layout' configuration option). If you want to use a different layout template (or if you haven't defined a layout
191             template when creating the Tenjin instance), then you must add the layout template's name
192             to the context as '_layout'. You can also just pass the layout template's name as C<$use_layout>, but C<< $_context->{_layout} >> has precedence.
193              
194             If C<$use_layout> is 0, then a layout template will not be used,
195             even if C<< $_context->{_layout} >> is defined.
196              
197             Note that you can nest layout templates as much as you like, but the only
198             way to do so is by setting the layout template for each template in the
199             nesting chain with C<< $_context->{_layout} >>.
200              
201             Please note that by default file templates are cached on disk (with a '.cache') extension.
202             Tenjin automatically deprecates these cache files every 10 seconds. If you
203             find this value is too low, you can override the C<$Tenjin::TIMESTAMP_INTERVAL>
204             variable with your preferred value.
205              
206             =cut
207              
208             sub render {
209 15     15 1 6764 my ($self, $template_name, $_context, $use_layout) = @_;
210              
211 15   100     86 $_context ||= {};
212 15         40 $_context->{'_engine'} = $self;
213              
214             # use a layout template by default
215 15 100       76 $use_layout = 1 unless defined $use_layout;
216              
217             # start rendering the template, and if use_layout is true
218             # then render the layout template with the original output, and
219             # keep doing so if the layout template in itself is nested
220             # inside other layout templates until there are no layouts left
221 15         23 my $output;
222 15         54 while ($template_name) {
223             # get the template
224 21         82 my $template = $self->get_template($template_name, $_context); # pass $_context only for preprocessing
225            
226             # render the template
227 21         112 $output = $template->render($_context);
228            
229             # should we nest into a layout template?
230             # check if $use_layout is 0, and if so bolt
231             # check if $_context->{_layout} is defined, and if so use it
232             # if not, and $use_layout is the name of a template, use it
233             # if $use_layout is just 1, then use $self->{layout}
234             # if no layout has been found, loop will finish
235 20 100 100     355 last if defined $use_layout && $use_layout eq '0';
236 19   100     128 $template_name = delete $_context->{_layout} || $use_layout;
237 19         41 undef $use_layout; # undef so we don't nest infinitely
238 19 100 100     126 $template_name = $self->{layout} if $template_name && $template_name eq '1';
239              
240 19         90 $_context->{_content} = $output;
241             }
242              
243             # return the output
244 14         126 return $output;
245             }
246              
247             =head2 register_template( $template_name, $template )
248              
249             Receives the name of a template and its L object
250             and stores it in memory for usage by the engine. This is useful if you
251             need to use templates that are not stored on the file system, for example
252             from a database.
253              
254             Note, however, that you need to pass a template object who's already been
255             converted and compiled into Perl code, so if you have a template with a
256             certain name and certain text, these are the steps you will need to perform:
257              
258             # create a Tenjin instance
259             my $tenjin = Tenjin->new(\%options);
260            
261             # create an empty template object
262             my $template = Tenjin::Template->new();
263            
264             # compile template content into Perl code
265             $template->convert($tmpl_content);
266             $template->compile();
267              
268             # register the template with the Tenjin instance
269             $tenjin->register_template($tmpl_name, $template);
270              
271             =cut
272              
273             sub register_template {
274 16     16 1 55 my ($self, $template_name, $template) = @_;
275              
276 16         82 $template->{timestamp} = time;
277 16         352 $self->{templates}->{$template_name} = $template;
278             }
279              
280             =head1 INTERNAL METHODS
281              
282             =head2 get_template( $template_name, $_context )
283              
284             Receives the name of a template and the context object and tries to find
285             that template in the engine's memory. If it's not there, it will try to find
286             it in the file system (the cache file might be loaded, if present). Returns
287             the template's L object.
288              
289             =cut
290              
291             sub get_template {
292 21     21 1 55 my ($self, $template_name, $_context) = @_;
293              
294             ## get cached template
295 21         92 my $template = $self->{templates}->{$template_name};
296              
297             ## check whether template file is updated or not
298 21 50 66     124 undef $template if ($template && $template->{filename} && $template->{timestamp} + $TIMESTAMP_INTERVAL <= time);
      66        
299              
300             ## load and register template
301 21 100       66 unless ($template) {
302 16         61 my $filename = $self->to_filename($template_name);
303 16         70 my $filepath = $self->find_template_file($filename);
304 16         73 $template = $self->create_template($filepath, $template_name, $_context); # $_context is passed only for preprocessor
305 16         97 $self->register_template($template_name, $template);
306             }
307              
308 21         74 return $template;
309             }
310              
311             =head2 to_filename( $template_name )
312              
313             Receives a template name and returns the proper file name to be searched
314             in the file system, which will only be different than C<$template_name>
315             if it begins with ':', in which case the prefix and postfix configuration
316             options will be appended and prepended to the template name (minus the ':'),
317             respectively.
318              
319             =cut
320              
321             sub to_filename {
322 16     16 1 39 my ($self, $template_name) = @_;
323              
324 16 50       76 if (substr($template_name, 0, 1) eq ':') {
325 0         0 return $self->{prefix} . substr($template_name, 1) . $self->{postfix};
326             }
327              
328 16         44 return $template_name;
329             }
330              
331             =head2 find_template_file( $filename )
332              
333             Receives a template filename and searches for it in the path defined in
334             the configuration options (or, if a path was not set, in the current
335             working directory). Returns the absolute path to the file.
336              
337             =cut
338              
339             sub find_template_file {
340 16     16 1 37 my ($self, $filename) = @_;
341              
342 16         36 my $path = $self->{path};
343 16 50       56 if ($path) {
344 16 50       79 my $sep = $^O eq 'MSWin32' ? '\\\\' : '/';
345 16         53 foreach my $dirname (@$path) {
346 16         45 my $filepath = $dirname . $sep . $filename;
347 16 50       486 return $filepath if -f $filepath;
348             }
349             } else {
350 0 0       0 return $filename if -f $filename;
351             }
352 0 0       0 my $s = $path ? ("['" . join("','", @$path) . "']") : '[]';
353 0         0 croak "[Tenjin] $filename not found in path (path is $s).";
354             }
355              
356             =head2 read_template_file( $template, $filename, $_context )
357              
358             Receives a template object and its absolute file path and reads that file.
359             If preprocessing is on, preprocessing will take place using the provided
360             context object.
361              
362             =cut
363              
364             sub read_template_file {
365 0     0 1 0 my ($self, $template, $filename, $_context) = @_;
366              
367 0 0       0 if ($self->{preprocess}) {
368 0 0 0     0 if (! defined($_context) || ! $_context->{_engine}) {
369 0   0     0 $_context ||= {};
370 0         0 $_context->{'_engine'} = $self;
371             }
372 0         0 my $pp = $Tenjin::PREPROCESSOR_CLASS->new();
373 0         0 $pp->convert($template->_read_file($filename));
374 0         0 return $pp->render($_context);
375             }
376              
377 0         0 return $template->_read_file($filename, 1);
378             }
379              
380             =head2 cachename( $filename )
381              
382             Receives a template filename and returns its standard cache filename (which
383             will simply be C<$filename> with '.cache' appended to it.
384              
385             =cut
386              
387             sub cachename {
388 16     16 1 33 my ($self, $filename) = @_;
389              
390 16         59 return $filename . '.cache';
391             }
392              
393             =head2 store_cachefile( $cachename, $template )
394              
395             Receives the name of a template cache file and the corrasponding template
396             object, and creates the cache file on disk.
397              
398             =cut
399              
400             sub store_cachefile {
401 0     0 1 0 my ($self, $cachename, $template) = @_;
402              
403 0         0 my $cache = $template->{script};
404 0 0       0 if (defined $template->{args}) {
405 0         0 my $args = $template->{args};
406 0         0 $cache = "\#\@ARGS " . join(',', @$args) . "\n" . $cache;
407             }
408 0         0 $template->_write_file($cachename, $cache, 1);
409             }
410              
411             =head2 load_cachefile( $cachename, $template )
412              
413             Receives the name of a template cache file and the corrasponding template
414             object, reads the cache file and stores it in the template object (as 'script').
415              
416             =cut
417              
418             sub load_cachefile {
419 16     16 1 36 my ($self, $cachename, $template) = @_;
420              
421 16         80 my $cache = $template->_read_file($cachename, 1);
422 16 50       188 if ($cache =~ s/\A\#\@ARGS (.*)\r?\n//) {
423 0         0 my $argstr = $1;
424 0         0 $argstr =~ s/\A\s+|\s+\Z//g;
425 0         0 my @args = split(',', $argstr);
426 0         0 $template->{args} = \@args;
427             }
428 16         163 $template->{script} = $cache;
429             }
430              
431             =head2 create_template( $filename, $_context )
432              
433             Receives an absolute path to a template file and the context object, reads
434             the file, processes it (which may involve loading the template's cache file
435             or creating the template's cache file), compiles it and returns the template
436             object.
437              
438             =cut
439              
440             sub create_template {
441 16     16 1 41 my ($self, $filename, $template_name, $_context) = @_;
442              
443 16         68 my $cachename = $self->cachename($filename);
444              
445 16   33     90 my $class = $self->{templateclass} || $Tenjin::TEMPLATE_CLASS;
446 16         166 my $template = $class->new(undef, $template_name, $self->{init_opts_for_template});
447              
448 16 50 33     1003 if (! $self->{cache}) {
    50          
449 0         0 $template->convert($self->read_template_file($template, $filename, $_context), $filename);
450             } elsif (! -f $cachename || (stat $cachename)[9] < (stat $filename)[9]) {
451 0         0 $template->convert($self->read_template_file($template, $filename, $_context), $filename);
452 0         0 $self->store_cachefile($cachename, $template);
453             } else {
454 16         76 $template->{filename} = $filename;
455 16         71 $self->load_cachefile($cachename, $template);
456             }
457 16         133 $template->compile();
458              
459 16         82 return $template;
460             }
461              
462             1;
463              
464             =head1 SEE ALSO
465              
466             The original Tenjin website is located at L. In there check out
467             L for detailed usage guide,
468             L for examples, and
469             L for frequently asked questions.
470              
471             Note that the Perl version of Tenjin is refered to as plTenjin on the Tenjin website,
472             and that, as opposed to this module, the website suggests using a .plhtml extension
473             for the templates instead of .html (this is entirely your choice).
474              
475             L, L, L.
476              
477             =head1 CHANGES
478              
479             Version 0.05 of this module broke backwards compatibility with previous versions.
480             In particular, the Tenjin::Engine module does not exist any more and is
481             instead integrated into this one. Templates are also rendered entirely
482             different (as per changes in the original tenjin) which provides much
483             faster rendering.
484              
485             Upon upgrading to versions 0.05 and above, you MUST perform the following changes
486             for your applications (or, if you're using Catalyst, you must also upgrade
487             L):
488              
489             =over
490              
491             =item * C as your normally would, but to get an instance
492             of Tenjin you must call C<< Tenjin->new() >> instead of the old method
493             of calling C<< Tenjin::Engine->new() >>.
494              
495             =item * Remove all your templates cache files (they are the '.cache' files
496             in your template directories), they are not compatible with the new
497             templates structure and WILL cause your application to fail if present.
498              
499             =back
500              
501             Version 0.06 (this version) restored the layout template feature which was
502             accidentaly missing in version 0.05, and the ability to call the utility
503             methods of L natively inside templates. You will want to
504             remove your templates' .cache files when upgrading to 0.6 too.
505              
506             =head1 AUTHOR
507              
508             The CPAN version of Tenjin was forked by Ido Perlmuter Eido at ido50.netE
509             from version 0.0.2 of the original plTenjin, which is developed by Makoto Kuwata
510             at L.
511              
512             Development of Tenjin is done with github at L.
513              
514             =head1 ACKNOWLEDGEMENTS
515              
516             I would like to thank the following people for their contributions:
517              
518             =over
519              
520             =item * Makoto Kuwata
521              
522             The original developer of Tenjin.
523              
524             =item * John Beppu Ebeppu at cpan.orgE
525              
526             For introducing me to Tenjin and helping me understand the way it's designed.
527              
528             =item * Pedro Melo Emelo at cpan.orgE
529              
530             For helping me understand the logic behind some of the original Tenjin aspects
531             and helping me fix bugs and create tests.
532              
533             =back
534              
535             =head1 BUGS
536              
537             Please report any bugs or feature requests to C,
538             or through the web interface at L. I will be notified, and then you'll automatically be notified of progress
539             on your bug as I make changes.
540              
541             =head1 SUPPORT
542              
543             You can find documentation for this module with the perldoc command.
544              
545             perldoc Tenjin
546              
547             You can also look for information at:
548              
549             =over 4
550              
551             =item * RT: CPAN's request tracker
552              
553             L
554              
555             =item * AnnoCPAN: Annotated CPAN documentation
556              
557             L
558              
559             =item * CPAN Ratings
560              
561             L
562              
563             =item * Search CPAN
564              
565             L
566              
567             =back
568              
569             =head1 LICENSE AND COPYRIGHT
570              
571             Tenjin is licensed under the MIT license.
572              
573             Copyright (c) 2007-2011 the aforementioned authors.
574              
575             Permission is hereby granted, free of charge, to any person obtaining
576             a copy of this software and associated documentation files (the
577             "Software"), to deal in the Software without restriction, including
578             without limitation the rights to use, copy, modify, merge, publish,
579             distribute, sublicense, and/or sell copies of the Software, and to
580             permit persons to whom the Software is furnished to do so, subject to
581             the following conditions:
582              
583             The above copyright notice and this permission notice shall be
584             included in all copies or substantial portions of the Software.
585              
586             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
587             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
588             MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
589             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
590             LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
591             OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
592             WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
593              
594             See http://dev.perl.org/licenses/ for more information.
595              
596             =cut