File Coverage

blib/lib/Tenjin.pm
Criterion Covered Total %
statement 86 108 79.6
branch 25 46 54.3
condition 16 28 57.1
subroutine 16 17 94.1
pod 11 11 100.0
total 154 210 73.3


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