File Coverage

blib/lib/Basset/Template.pm
Criterion Covered Total %
statement 27 310 8.7
branch 2 152 1.3
condition 0 51 0.0
subroutine 7 20 35.0
pod 7 14 50.0
total 43 547 7.8


line stmt bran cond sub pod time code
1             package Basset::Template;
2              
3             #Basset::Template, copyright and (c) 2002, 2003, 2004, 2005, 2006 James A Thomason III
4             #Basset::Template is distributed under the terms of the Perl Artistic License.
5              
6             =pod
7              
8             =head1 NAME
9              
10             Basset::Template - my templating system
11              
12             =head1 AUTHOR
13              
14             Jim Thomason, jim@jimandkoka.com
15              
16             =head1 DESCRIPTION
17              
18             Yes yes, I am horribly horribly villified for even considering doing such a thing. But this is actually a pretty damn
19             powerful AND flexible templating system. It embeds perl into the template and doesn't worry about extra syntax or tokens
20             or such of its own. Theoretically, it'd be really easy to subclass the thing and actually create your own template syntax,
21             if you so desire. Personally, I don't. At least, not yet...
22              
23             Templates live in their own namespaces, so you won't need to worry about things colliding. At all. Ever. Magic!
24             The only variable that's imported into a template's namespace is '$self', the template object being processed.
25              
26             =head1 QUICK START
27              
28             Okay, so you want to write a template. It's going to need a few things. Code, values, and passed variables. Try this example:
29              
30             in: /path/to/template.tpl
31              
32             %% foreach my $age (1..5) {
33             <% $name %> is now <% $age %><% "\n" %>
34             %% };
35              
36             Then, your code can be:
37              
38             use Basset::Template;
39             my $template = Basset::Template->new(
40             'template' => '/path/to/template.tpl'
41             ) || die Basset::Template->error();
42              
43             print $template->process(
44             {
45             'name' => \'Jim'
46             }
47             ) || $template->error;
48              
49             Voila. All done. Note that %% starts a code line which goes to the end. <% %> delimits a variable to
50             be inserted. Also be aware that any white space between code blocks or variable insertion blocks will be stripped.
51             That's why we have that "\n" in a variable insertion block - it puts in a new line. We don't end up with 2 newlines
52             because the actual newline is stripped.
53              
54             The process method returns the processed template. You may pass in a hashref containing the values to be inserted.
55             Note that values should be passed by reference. In this case, we pass in 'name', which is a scalar containing 'Jim'.
56             If you don't pass a value by reference, it will be assumed that you meant to pass in a scalar reference and be alterred
57             as such. Note that this has no effect on you.
58              
59             {'name' => \'Jim'} == {'name' => 'Jim'}
60              
61             And that includes the read-only nature of the ref to the literal. Both values are accessed in your template via '$name'
62              
63             You can also skip creating an object, if you'd prefer:
64              
65             print Basset::Template->process('/path/to/template.tpl', {'name' => \'Jim'}) || die Basset::Template->error;
66              
67             Damn near everything is configurable. Read on for more information.
68              
69             =cut
70              
71             our $VERSION = '1.04';
72              
73 2     2   27498 use Cwd ();
  2         4  
  2         42  
74              
75 2     2   1367 use Basset::Object;
  2         5  
  2         145  
76             our @ISA = Basset::Object->pkg_for_type('object');
77              
78 2     2   16 use strict;
  2         4  
  2         72  
79 2     2   11 use warnings;
  2         4  
  2         6534  
80              
81             =pod
82              
83             =head1 ATTRIBUTES
84              
85             B
86              
87             =over
88              
89             =item open_return_tag, close_return_tag
90              
91             In a template, the simplest thing that you're going to want to do is embed a value. Say you have $x = 7 and want to display
92             that. Your template could be:
93              
94             $x = <% $x %>
95              
96             Which, when processed, would print:
97              
98             $x = 7
99              
100             In this case, <% is your open_return_tag and %> is your close_return_tag. These should be specified in your
101             conf file, but may be alterred on a per-object basis (if you're a real masochist).
102              
103             Also note that side effects are handled correctly:
104              
105             $x is now : <% ++$x %>, and is still <% $x %>;
106              
107             evaluates to :
108             $x is now : 8, and is still 8
109              
110             And that you may do extra processing here, if you'd like. The final value is the one returned.
111              
112             <% $x++; $x = 18; $x %>
113              
114             evaluates to :
115             18
116              
117             Defaults are <% and %>
118              
119             =cut
120              
121             __PACKAGE__->add_attr('open_return_tag');
122             __PACKAGE__->add_attr('close_return_tag');
123              
124             =item open_eval_tag, close_eval_tag
125              
126             Sometimes, though, it gets a little more complicated, and you actually want to put code in your template. That's where
127             the eval tags come into play. The defaults are "%%" and "\n".
128              
129             For example:
130              
131             %% foreach my $x (1..5) {
132             <% $x %>
133             %% };
134              
135             evalutes to:
136              
137             12345
138              
139             (recall that the whitespace is stripped)
140              
141             Voila. You may insert any perl code you'd like in there, as long as it's valid. If you want to output something into the
142             template instead of using the eval tags, use the special filehandle OUT.
143              
144             %% foreach my $x (1..5) {
145             %% print OUT $x;
146             %% };
147              
148             is the same thing.
149              
150             Note that you may put comments in this way.
151              
152             %% # this is a comment.
153              
154             Comments will be stripped before the template is displayed. See also open_comment_tag and close_comment_tag
155              
156             =cut
157              
158             __PACKAGE__->add_attr('open_eval_tag');
159             __PACKAGE__->add_attr('close_eval_tag');
160              
161             =pod
162              
163             =item big_open_eval_tag, big_close_eval_tag
164              
165             By default, our code is line delimited. That's nice for not needing closing tags for one-liner things, like if statements
166             or for loops. But sometimes you need to do a lot of processing. That's a mess.
167              
168             %% my $x = some_function();
169             %% $x + 1;
170             %% if ($x > 18) {
171             %% $x = other_function;
172             %% } elsif ($x > 14) {
173             $x = 12;
174             %% };
175             %% $x = process($x);
176             %% # etc.
177              
178             So, we have our big tags, defaulting to and , which are a synonym, just with a closing tag.
179              
180            
181             my $x = some_function();
182             $x + 1;
183             if ($x > 18) {
184             $x = other_function;
185             } elsif ($x > 14) {
186             $x = 12;
187             };
188             $x = process($x);
189             # etc.
190            
191              
192             Much cleaner.
193              
194             =cut
195              
196             __PACKAGE__->add_attr('big_open_eval_tag');
197             __PACKAGE__->add_attr('big_close_eval_tag');
198              
199             =pod
200              
201             =item open_comment_tag, close_comment_tag
202              
203             You're a bad, bad developer if you're not commenting your code. And your templates are no exception. While you can
204             embed comments via the eval_tags, it's less than ideal.
205              
206             %% # this is a comment
207             #this is a comment
208              
209             So we have our comment tags, which default to <# and #>
210              
211             <# this is a comment that will be stripped out well before you see the processed template #>
212              
213             =cut
214              
215             __PACKAGE__->add_attr('open_comment_tag');
216             __PACKAGE__->add_attr('close_comment_tag');
217              
218             =pod
219              
220             =item open_include_tag, close_include_tag
221              
222             You may want to include another template inside your current template. That's accomplished with include tags,
223             which default to <& and &>
224              
225             This is my template.
226             Here is a subtemplate : <& /path/to/subtemplate.tpl &>
227              
228             There are two ways to include a subtemplate - with passed variables and without. Passing without variables is easy -
229             we just did that up above.
230              
231             <& /path/to/subtemplate.tpl &>
232              
233             Passing with variables is also easy, just give it a hashref.
234              
235             <& /path/to/subtemplate.tpl {'var1' => \$var1, 'var2' => \$var2, 'var3' => \$var3} &>
236              
237             And voila. All set. Same rules apply for passing in variables as applies for the process method. You may break the include
238             statement over multiple lines, if so desired.
239              
240             The major difference between the two is that if a subtemplate is included without variables, then it is evaluted in
241             the B package. So it has access to all variables, etc. within the template and vice-versa. If a subtemplate
242             is included with variables, then it is evaluated in it's B package. So it does not have access to any variables of
243             the supertemplate, nor does the supertemplate have access to the subtemplate's values.
244              
245             =cut
246              
247             __PACKAGE__->add_attr('open_include_tag');
248             __PACKAGE__->add_attr('close_include_tag');
249              
250             __PACKAGE__->add_attr('open_cached_include_tag');
251             __PACKAGE__->add_attr('close_cached_include_tag');
252              
253             __PACKAGE__->add_attr('cache_all_inserts');
254              
255             =pod
256              
257             =item document_root
258              
259             For included files, this is the document root. Say you're running a webserver, and you want to include a file. Your
260             webserver doc root is: /home/users/me/public_html/mysite.com/
261              
262             Now, when you include files, you don't want to have to do:
263              
264             <& /home/users/me/public_html/mysite.com/someplace/mysubtemplate.tpl &>
265              
266             because that's messy and very non-portable. So just set a document_root.
267              
268             $tpl->document_root('/home/users/me/public_html/mysite.com/');
269              
270             and voila:
271              
272             <& /someplace/mysubtemplate.tpl &>
273              
274             Note that this only affects subtemplates set with an absolute path. So even with that doc root, these includes are
275             unaffected:
276              
277             <& someplace/mysubtemplate.tpl &>
278             <& ~/someplace/mysubtemplate.tpl &>
279              
280             =cut
281              
282             __PACKAGE__->add_attr('document_root');
283              
284             =pod
285              
286             =item allows_debugging
287              
288             Boolean flag. 1/0. If 1, then the debug tags will execute, if 0, then they won't.
289              
290             =cut
291              
292             __PACKAGE__->add_attr('allows_debugging');
293              
294             =pod
295              
296             =item open_debug_tag, close_debug_tag
297              
298             Debugging information can be a very good thing. The debug tags are the equivalent of return tags, but they go
299             to STDERR. You may do additional processing, manipulation, etc., but the last value always go to STDERR.
300              
301            
302             "Now at line 15, and x is $x"
303            
304              
305            
306             my $z = $x;
307             $z *= 2;
308             "Now at line 15, and twice x is $z";
309            
310              
311             Debug tags will only be executed if allows_debugging is 1.
312              
313             =cut
314              
315             __PACKAGE__->add_attr('open_debug_tag');
316             __PACKAGE__->add_attr('close_debug_tag');
317              
318             =pod
319              
320             =item pipe_flags
321              
322             This is a trickled class attribute hashref.
323              
324             The pipe_flags allow you to deviate from standard perl and send the output of your value to a processor before displaying.
325              
326             Built in flags include 'h' to escape the output for HTML and 'u' to escape the output for URLs.
327              
328             New flags should have the flag as the key and the processor as the value.
329              
330             Some::Template::Subclass->pipe_flags->{'blogger_flag'} = 'blogger_processor';
331              
332             Will get one argument - the value to be processed.
333              
334             =cut
335              
336             __PACKAGE__->add_trickle_class_attr('pipe_flags',
337             {
338             'h' => 'escape_for_html',
339             'u' => 'escape_for_url',
340             }
341             );
342              
343             =pod
344              
345             =item template
346              
347             This is the actual template on which you are operating.
348              
349             $tpl->template('/path/to/template'); #for a file
350             $tpl->template(\$template); #for a value in memory
351              
352             my $tpl = Basset::Template->new('template' => '/path/to/template');
353              
354             Hand in a literal string for a file to open, or a scalarref if it's already in memory.
355              
356             Note that if you had in a template with an absolute path starting with /, the template will automatically
357             be assumed to be sitting off of the document root. Relative paths are unaffected.
358              
359             =cut
360              
361             __PACKAGE__->add_attr('_template');
362              
363             sub template {
364 0     0 1 0 my $self = shift;
365              
366 0         0 my $tpl = $self->_template(@_);
367 0         0 my $root = $self->document_root;
368              
369 0 0 0     0 if (defined $tpl && defined $root && $tpl =~ m!^/! && $tpl !~ m!^$root!) {
      0        
      0        
370 0         0 my $full_path_to_tpl = $root . $tpl;
371 0         0 $full_path_to_tpl =~ s!//+!/!g;
372 0         0 return $full_path_to_tpl;
373             }
374             else {
375 0         0 return $tpl;
376             };
377              
378             }
379              
380             =pod
381              
382             =item preprocessed_template
383              
384             This stores the value of the template after it's been run through the preprocessor. You probably don't
385             want to touch this unless you B know what you're doing.
386              
387             Still, it's sometimes useful to look at for debugging purposes, if your template isn't displaying properly.
388             Be warned - it's a bitch to read.
389              
390             =cut
391              
392             __PACKAGE__->add_attr('preprocessed_template');
393              
394             #This is the template we're currently operating on. Internal attribute. Don't touch it.
395              
396             __PACKAGE__->add_attr('_current_template');
397              
398             #internal method. All templates evaluate in their own distinct package. This is it.
399             #This value is set by gen_package
400              
401             __PACKAGE__->add_attr('package');
402              
403             #internal method. As a template evaluates, its output gets tacked onto a scalar. This is it.
404             #This value is set by gen_file
405              
406             __PACKAGE__->add_attr('file');
407              
408             __PACKAGE__->add_attr('_preprocessed_inserted_file_cache');
409              
410             =pod
411              
412             =item caching
413              
414             Boolean flag. 1/0.
415              
416             templates are obviously not executable code. Nothing can be done with them, they're nonsensical. So, before they can
417             be used, they must be preprocessed into something useful. That preprocessing step is reasonably fast, but it's still
418             effectively overhead. You don't care about the transformations happening, you just want it to work!
419              
420             Besides most templates are modified very rarely - it's normally the same thing being re-displayed. So constantly re-preprocessing
421             it is inefficient. So, you may turn on caching.
422              
423             If caching is on, during preprocessing the template looks in your cache_dir. If it finds a preprocessed version that is
424             current, it grabs that version and returns it. No more additional processing. If it finds a preprocessed version that is
425             out of date (i.e., the actual template was modified after the cached version was created) then it looks to the new
426             template and re-caches it. If no cached value is found, then one is cached for future use.
427              
428             =cut
429              
430             __PACKAGE__->add_attr('caching');
431              
432             =pod
433              
434             =item cache_dir
435              
436             This is your cache directory, used if caching is on.
437              
438             $template->cache_dir('/path/to/my/cache/dir/');
439              
440             =cut
441              
442             __PACKAGE__->add_attr('cache_dir');
443              
444             =pod
445              
446             =item compress_whitespace
447              
448             Boolean flag. 1/0.
449              
450             Sometimes whitespace in your template doesn't matter. an HTML file, for example. So, you can compress it. That way
451             you're sending less data to a web browser, for instance.
452              
453             compress_whitespace turns runs of spaces or tabs into a single space, and runs of newlines into a single newline.
454              
455             =cut
456              
457             __PACKAGE__->add_attr('compress_whitespace');
458              
459             sub init {
460             return shift->SUPER::init(
461             {
462 0     0 1 0 'open_return_tag' => '<%',
463             'close_return_tag' => '%>',
464             'open_eval_tag' => '%%',
465             'close_eval_tag' => "\n",
466             'big_open_eval_tag' => '',
467             'big_close_eval_tag' => "",
468             'open_comment_tag' => '<#',
469             'close_comment_tag' => '#>',
470             'open_include_tag' => '<&',
471             'close_include_tag' => '&>',
472             'open_cached_include_tag' => '<&+',
473             'close_cached_include_tag' => '+&>',
474             'open_debug_tag' => '',
475             'close_debug_tag' => '',
476             'cache_all_inserts' => 0,
477            
478             'caching' => 1,
479             'compress_whitespace' => 1,
480             'allows_debugging' => 1,
481            
482             '_full_file_path_cache' => {},
483             '_preprocessed_inserted_file_cache' => {},
484             },
485             @_
486             );
487             }
488              
489             {
490             my $package = 0;
491             my $file = 0;
492              
493              
494             #internal method, generates a new package for the template to be processed in
495              
496             sub gen_package {
497 0     0 0 0 my $self = shift;
498              
499 0   0     0 my $template = shift || $self->template;
500              
501             #if it's the template itself was handed in as a ref, then create an internal package
502 0 0       0 if (ref $template) {
503 0         0 return __PACKAGE__ . "::package::ipackage" . $package++;
504             }
505             #otherwise, it's a file, then create the special package
506             else {
507 0         0 my $full_file = $self->full_file_path($template);
508 0         0 $full_file =~ s/(\W)/'::p' . ord($1)/ge;
  0         0  
509 0         0 return __PACKAGE__ . "::package::fpackage" . $full_file;
510             };
511             };
512              
513             #internal method, generates a new scalar for the processed template to be tacked on to.
514              
515             sub gen_file {
516 0     0 0 0 my $self = shift;
517              
518 0   0     0 my $template = shift || $self->template;
519              
520             #if we've been given two values, then it's a template to be incremented
521 0 0       0 if (@_){
522 0         0 $template =~ s/[if]file/subfile/g;
523 0         0 return $template . $file++;
524             }
525             #if it's the template itself was handed in as a ref, then create an internal package
526 0 0       0 if (ref $template) {
527 0         0 return '$' . __PACKAGE__ . "::file::ifile" . $file++;
528             }
529             #otherwise, it's a file, then create the special package
530             else {
531 0         0 my $full_file = $self->full_file_path($template);
532 0         0 $full_file =~ s/(\W)/'::p' . ord($1)/ge;
  0         0  
533 0         0 return '$' . __PACKAGE__ . "::file::ffile" . $full_file;
534             };
535             };
536              
537             };
538              
539             # very very very internal method. Takes any template information inside of a return tag, and translates
540             # it into an eval tag
541              
542             sub return_to_eval {
543 0     0 0 0 my $self = shift;
544 0         0 my $val = shift;
545              
546 0         0 my $bein = $self->big_open_eval_tag;
547 0         0 my $beout = $self->big_close_eval_tag;
548              
549 0         0 my $file = $self->file;
550              
551 0         0 my $subval = $self->gen_file($file,1);
552              
553 0         0 $val =~ /^(.+?) # first of all match, well, anything.
554             ( # finally, our optional pipe flags. A pipe, followed by an arbitrary word
555             (?:
556             \|
557             \s*
558             \w+
559             (?:
560             \s*[\$%@&*\\]?\w+ # and an optional string of arguments, which may be words or variables
561             )*
562             \s*
563             )*
564             )
565             $/sx;
566            
567 0         0 my ($code, $pipes) = ($1, $2);
568            
569 0         0 my $pipe;
570            
571 0 0       0 if (defined $pipes) {
572 0         0 $pipe = $subval;
573 0         0 while ($pipes =~ /\|\s*(\w+)((?:\s*[\$%@&*\\]?\w+)*)/g) {
574 0 0       0 if (my $method = $self->pipe_flags->{$1}) {
575 0         0 my $args = '';
576 0 0       0 if (defined $2) {
577 0         0 my @params = split ' ', $2;
578 0         0 my @args = ();
579            
580 0         0 foreach my $param (@params) {
581 0 0       0 push @args, $param =~ /^\W/
582             ? $param
583             : "q{$param}";
584             }
585            
586 0         0 $args = ', ' . join(', ', @args);
587             }
588 0         0 $pipe = "\$self->$method($pipe $args)";
589             }
590             }
591             }
592              
593 0         0 $val = $bein . " $subval = do { $code }; $file .= defined ($subval) ? $pipe : ''; " . $beout;
594            
595 0         0 return $val;
596             };
597              
598             # very very very internal method. Takes any template information inside of a debug tag, and translates
599             # it into an eval tag
600              
601             sub debug_to_eval {
602 0     0 0 0 my $self = shift;
603 0         0 my $val = shift;
604              
605 0 0       0 return '' unless $self->allows_debugging;
606              
607 0         0 my $bein = $self->big_open_eval_tag;
608 0         0 my $beout = $self->big_close_eval_tag;
609              
610 0         0 $val = $bein . "{ my \@debug_val = do { $val }; print STDERR (\@debug_val ? \@debug_val : ''), \"\\n\"; };" . $beout;
611            
612 0         0 return $val;
613             };
614              
615             # internal method. The tokenizer breaks up the template into eval components and non eval components
616              
617             sub tokenize {
618 0     0 0 0 my $self = shift;
619              
620 0 0 0     0 my $template = shift || $self->template
621             or return $self->error("Cannot tokenize without template", "BT-01");
622              
623 0         0 my $rin = $self->open_return_tag;
624 0         0 my $rout = $self->close_return_tag;
625              
626 0         0 my $ein = $self->open_eval_tag;
627 0         0 my $eout = $self->close_eval_tag;
628              
629 0         0 my $bein = $self->big_open_eval_tag;
630 0         0 my $beout = $self->big_close_eval_tag;
631              
632             return
633 0 0       0 grep {defined $_ && length $_ > 0}
  0         0  
634             split(/(\Q$rin\E(?:.*?)\Q$rout\E)|(\Q$ein\E(?:.*?)\Q$eout\E)|(\Q$bein\E(?:.*?)\Q$beout\E)/s, $template);
635             };
636              
637             =pod
638              
639             =item full_file_path
640              
641             given a file (usually a template), full_file_path returns the absolute path of the file,
642             relative to the file system root
643              
644             =cut
645              
646             __PACKAGE__->add_attr('_full_file_path_cache');
647              
648             sub full_file_path {
649 0     0 1 0 my $self = shift;
650 0 0       0 my $file = shift or return $self->error("Cannot get file path w/o file", "BT-14");
651              
652 0 0       0 return $self->_full_file_path_cache->{$file} if defined $self->_full_file_path_cache->{$file};
653              
654              
655 0 0       0 if ($file =~ /^\//){
    0          
    0          
    0          
656             #do nothing, it's fine
657             }
658             elsif ($file =~ /^~/){
659 0 0       0 my $home = $ENV{HOME} or return $self->error("Cannot get home", "BT-10");
660 0 0       0 $home .= '/' unless $home =~ /\/$/;
661 0         0 $file =~ s/^~\//$home/;
662             }
663             elsif ($file =~ /^\.[^.]/){
664            
665 0 0       0 my $cwd = Cwd::getcwd() or return $self->error("Cannot getcwd", "BT-09");
666 0 0       0 $cwd .= '/' unless $cwd =~ /\/$/;
667            
668 0         0 $file =~ s/^\.\//$cwd/;
669             #return $file;
670             }
671             elsif ($file =~ /[a-zA-Z0-9_]/) {
672            
673 0 0       0 my $cwd = Cwd::getcwd() or return $self->error("Cannot getcwd", "BT-09");
674 0 0       0 $cwd .= '/' unless $cwd =~ /\/$/;
675            
676 0         0 $file = $cwd . $file;
677             #return $file;
678             }
679             else {
680 0         0 return $self->error("Cannot get full path to file '$file'", "BT-11");
681             }
682            
683 0 0       0 if ($file =~ /\.\./){
684 0         0 my @file = split(/\//, $file);
685 0         0 my @new = ();
686 0         0 foreach (@file){
687 0 0       0 if ($_ eq '..'){
688 0         0 pop @new;
689             }
690             else {
691 0         0 push @new, $_;
692             };
693             };
694 0         0 $file = join('/', @new);
695             };
696              
697 0         0 $self->_full_file_path_cache->{$file} = $file;
698              
699 0         0 return $file;
700             };
701              
702             # internal method. Names the cached file that will be written to the cache_dir.
703             # currently filename . '-,' debuglevel . '.' . package name . '.cache'
704              
705             sub cache_file {
706 0     0 0 0 my $self = shift;
707 0 0       0 my $file = shift or return $self->error("Cannot create cache_file w/o file", "BT-12");
708              
709 0 0       0 my $dir = $self->cache_dir or return $self->error("Cannot create cache_file w/o cache_dir", "BT-19");
710 0         0 $dir =~ s/\/$//;
711              
712 0         0 (my $pkg = __PACKAGE__) =~ s/::/,/g;
713              
714 0         0 my $debug = $self->allows_debugging;
715              
716 0         0 my $cache_file = $dir . $self->full_file_path("$file-,$debug.$pkg.cache"); #name our preprocessed cache file
717              
718 0         0 return $cache_file;
719              
720             };
721              
722             #internal method. handles converting a subtemplate include tag into the necessary eval tag equivalent.
723              
724             sub insert_file {
725 0     0 0 0 my $self = shift;
726 0 0       0 my $file = shift or return $self->error("Cannot insert w/o file", "BT-13");
727 0   0     0 my $cached = shift || 0;
728              
729 0         0 my $pkg = $self->pkg;
730              
731 0         0 my $bein = $self->big_open_eval_tag;
732 0         0 my $beout = $self->big_close_eval_tag;
733              
734 0         0 my $f = $self->file;
735            
736 0 0       0 if ($file =~ s/\s+>>\s*(\$\w+)//) {
737 0         0 $f = $1;
738             }
739              
740 0         0 $file =~ s/^\s+|\s+$//g;
741              
742 0         0 my $args = undef;
743              
744 0 0       0 if ($file =~ /\s/){
745 0         0 ($file, $args) = split(/\s+/, $file, 2);
746 0         0 $args =~ s/\\/\\\\/g;
747 0         0 $args =~ s/([{}])/\\$1/g;
748             };
749              
750 0         0 my $return = undef;
751 0 0       0 if ($cached) {
    0          
752              
753 0         0 my $tpl = $pkg->new(
754             'template' => $file,
755             'caching' => 0,
756             'compress_whitespace' => $self->compress_whitespace
757             );
758            
759 0         0 my $file = $tpl->template;
760 0         0 my $embedded;
761             {
762 0         0 local $/ = undef;
  0         0  
763 0         0 my $filehandle = $self->gen_handle;
764 0 0       0 open ($filehandle, '<', $file) or return $self->error("Cannot open embedded templated $file : $!", "BT-06");
765 0         0 $embedded = <$filehandle>;
766 0 0       0 close $filehandle or return $self->error("Cannot close embedded template $file : $!", "BT-07");
767             }
768            
769 0         0 $return = "$bein { $beout" . $embedded . "$bein } $beout";
770            
771             }
772             elsif ($args){
773 0         0 $return = qq[$bein { local \$@ = undef; my \$tpl = \$self->_preprocessed_inserted_file_cache->{"$file"} || $pkg->new('template' => "$file", caching => ] . $self->caching . ', compress_whitespace => ' . $self->compress_whitespace . ');';
774 0         0 $return .= qq[\$self->_preprocessed_inserted_file_cache->{"$file"} = \$tpl;];
775 0         0 $return .= qq[my \$hash = eval q{$args}; if (\$@) { $f .= '[' . \$@ . ' in subtemplate $file]' } else {$f .= \$tpl->process(\$hash) || '[' . \$tpl->errstring . ' in subtemplate $file]'; } }; $beout];
776             }
777             else {
778 0         0 $return = qq[$bein { local \$@ = undef; my \$tpl = \$self->_preprocessed_inserted_file_cache->{"$file"} || $pkg->new('template' => "$file", caching => ] . $self->caching . ', compress_whitespace => ' . $self->compress_whitespace . ');';
779 0         0 $return .= qq[\$self->_preprocessed_inserted_file_cache->{"$file"} = \$tpl;];
780 0         0 $return .= qq[$f .= eval (\$tpl->preprocess) || '[' . \$tpl->errstring . ':(' . \$@ . ') in subtemplate $file]'; }; $beout];
781             };
782              
783 0         0 return $return;
784              
785             };
786              
787             =pod
788              
789             =back
790              
791             =head1 METHODS
792              
793             =over
794              
795             =item preprocess
796              
797             preprocess is called internally, so you'll never need to worry about it. It takes the template and translates it into
798             an executable form. Only call preprocess if you really know what you're doing (for example, if you want to look at the
799             preprocessed_template without actually calling process).
800              
801             =cut
802              
803             sub preprocess {
804 0     0 1 0 my $self = shift;
805              
806 0 0 0     0 my $template = shift || $self->template
807             or return $self->error("Cannot preprocess without template", "BT-02");
808 0   0     0 my $raw = shift || 0;
809              
810 0 0       0 $self->file($self->gen_file($template)) unless $self->file;
811 0 0       0 $self->package($self->gen_package($template)) unless $self->package;
812              
813             # first things first - nuke the template itself, this will allow us to use the standardized names
814             # based upon the template file name, AND run that template more than once in the
815             # same script
816 0 0       0 if ($self->file) {
817 2     2   22 no strict 'refs';
  2         3  
  2         3663  
818 0         0 my $stringy_file = $self->file;
819 0         0 $stringy_file =~ s/^\$//;
820 0         0 ${$stringy_file} = undef;
  0         0  
821             };
822              
823             # okay, if we have a preprocessed_template AND the template that we're preprocessing
824             # is the one that we've cached (_current_template), then we can return the preprocessed_template
825             # otherwise, we need to preprocess it
826 0 0 0     0 if ($self->preprocessed_template && $self->_current_template eq $template){
827 0         0 return $self->preprocessed_template;
828             };
829              
830             # keep track of the original value that was passed, so we can hand that into _cached_template
831             # if desired
832 0         0 my $passed_template = scalar $template;
833              
834 0         0 my $cache_file = undef; #so we can cache the preprocessed template to disk, if desired
835              
836             #okay, if we're given a string reference, use that as our template. Otherwise,
837             #we're going to assume that it's a file to open
838 0 0       0 if (ref $template){
839             #for now, just de-reference it. Memory management be damned!
840             #I may pass by ref later, but for now I don't want to mess w/the original
841 0         0 $template = $$template;
842             }
843             #otherwise, we're going to assume that it's the path to a hard file on disk
844             else {
845 0         0 my $filename = $template;
846              
847 0         0 $cache_file = $self->cache_file($filename); #turn it into the full name
848              
849 0 0       0 if ($cache_file) {
850              
851 0         0 my $using_cache = 0;
852              
853             #check to see if we have a cached preprocessed file
854 0 0 0     0 if (-e $cache_file && (-M $filename >= -M $cache_file)){
855 0         0 $filename = $cache_file;
856 0         0 $using_cache = 1;
857             };
858              
859             # load up the file. We'll either be loading the template from the cache
860             # if the check up there succeeded, or we'll be loading the original template
861 0         0 my $filehandle = $self->gen_handle;
862 0 0       0 open ($filehandle, '<', $filename) or return $self->error("Cannot open template $template : $!", "BT-06");
863 0         0 local $/ = undef;
864 0         0 $template = <$filehandle>;
865 0 0       0 close $filehandle or return $self->error("Cannot close template $template : $!", "BT-07");
866              
867             #return now if we loaded this thing out of the cache
868 0         0 $self->_current_template($passed_template);
869 0 0       0 return $self->preprocessed_template($template) if $using_cache;
870             };
871             };
872              
873 0         0 my $rin = $self->open_return_tag;
874 0         0 my $rout = $self->close_return_tag;
875              
876 0         0 my $ein = $self->open_eval_tag;
877 0         0 my $eout = $self->close_eval_tag;
878              
879 0         0 my $bein = $self->big_open_eval_tag;
880 0         0 my $beout = $self->big_close_eval_tag;
881              
882 0         0 my $cin = $self->open_comment_tag;
883 0         0 my $cout = $self->close_comment_tag;
884              
885 0         0 my $ciin = $self->open_cached_include_tag;
886 0         0 my $ciout = $self->close_cached_include_tag;
887              
888 0         0 my $iin = $self->open_include_tag;
889 0         0 my $iout = $self->close_include_tag;
890              
891 0         0 my $din = $self->open_debug_tag;
892 0         0 my $dout = $self->close_debug_tag;
893              
894 0         0 my $pkg = $self->package;
895 0         0 my $file = $self->file;
896              
897             #we need the special extra case of the while loop here to handled nested cached embedded templates.
898 0         0 $template =~ s/\Q$ciin\E(.*?)\Q$ciout\E/$self->insert_file($1, 'cached')/ges while $template =~ /\Q$ciin\E(.*?)\Q$ciout\E/s;
  0         0  
899 0         0 $template =~ s/\Q$iin\E(.*?)\Q$iout\E/$self->insert_file($1, $self->cache_all_inserts)/ges while $template =~ /\Q$iin\E(.*?)\Q$iout\E/s;
  0         0  
900              
901 0 0       0 if (defined $template) {
902 0         0 $template =~ s/\Q$cin\E(.*?)\Q$cout\E//gs;
903              
904 0         0 $template =~ s/\Q$rin\E(.*?)\Q$rout\E/$self->return_to_eval($1)/gse;
  0         0  
905              
906 0         0 $template =~ s/\Q$din\E(.*?)\Q$dout\E/$self->debug_to_eval($1)/gse;
  0         0  
907              
908 0         0 $template =~ s/\Q$eout\E(\s+)\Q$ein\E/$eout$ein/g;
909 0         0 $template =~ s/\Q$beout\E(\s+)\Q$bein\E/$beout$bein/g;
910             }
911              
912              
913              
914 0 0       0 my @tokens = $self->tokenize($template) or return;
915              
916 0         0 my $stack = 0;
917 0         0 my @idx = ();
918              
919 0         0 my $block = 0;
920              
921 0         0 foreach (@tokens){
922 0 0       0 if ($_ =~ /$ein(.*?)$eout/s){
    0          
923 0         0 $_ =~ s/$ein(.*?)$eout/$1\n/gs;
924 0         0 $_ =~ s/([^;{}\s]\s*)$/$1;/; #add semicolons, if needed
925 0 0       0 $block++ if $_ =~ /{\s*$/;
926 0 0       0 $block-- if $_ =~ /^\s*}/;
927             }
928             elsif ($_ =~ /$bein(.*?)$beout/s){
929 0         0 $_ =~ s/$bein(.*?)$beout/$1\n/gs;
930 0         0 $_ =~ s/([^;{}\s]\s*)$/$1;/; #add semicolons, if needed
931 0 0       0 $block++ if $_ =~ /{\s*$/;
932 0 0       0 $block-- if $_ =~ /^\s*}/;
933             }
934             else {
935 0         0 $_ =~ s/([{}])/\\$1/g;
936 0 0 0     0 if ($block && /^\s+$/){
937 0         0 $_ = '';
938 0         0 next;
939             };
940 0 0       0 unless (/$file/){
941 0 0       0 $_ =~ s/[^\S\n]+/ /g if $self->compress_whitespace;
942 0 0       0 $_ =~ s/[^\S ]+/\n/g if $self->compress_whitespace;
943 0         0 $_ = "$file .= (q{$_});\n";
944             };
945             };
946             };
947              
948             #$template = join('', "use strict;\n", "use warnings;\n", @tokens);
949 0         0 $template = join('', @tokens);
950 0         0 $template =~ s/print OUT/$file .=/g;
951 0         0 $template .= ";\nreturn $file;\n";
952              
953 0 0 0     0 if ($self->caching && $cache_file){
954              
955 0 0       0 if (my $cache_dir = $self->cache_dir){
956             #print "C $cache_dir\n";
957 0         0 $cache_dir =~ s/\/$//;
958              
959 0 0       0 unless (-d $cache_dir){
960 0 0       0 mkdir($cache_dir, 0777) or return $self->error("Cannot make directory $cache_dir - ($!)", "BT-15");
961             };
962              
963 0         0 (my $filedir = $cache_file) =~ s/^$cache_dir//;
964              
965 0         0 my @file = split(/\//, $filedir);
966 0         0 pop @file; #thats my filename, not the path.
967             #print "F @file\n";
968              
969 0         0 foreach my $dir (@file) {
970 0 0       0 next if $dir =~ /^\s*$/;
971 0         0 $cache_dir .= "/$dir";
972             #print "CACHE DIR : $cache_dir ($dir)\n";
973 0 0       0 unless (-d $cache_dir){
974 0 0       0 mkdir($cache_dir, 0777) or return $self->error("Cannot make directory $cache_dir - ($!)", "BT-15");
975             };
976             };
977 0         0 my $cachehandle = $self->gen_handle;
978 0 0       0 if (open ($cachehandle, '>', $cache_file)) {
979 0         0 print $cachehandle $template;
980 0 0       0 close ($cachehandle) or return $self->error("Cannot close cache file ($cache_file) - ($!)", "BT-16");
981             }
982             else {
983 0         0 return $self->error("Cannot open cache file ($cache_file) - ($!)", "BT-17");
984             };
985             };
986             };
987              
988              
989 0         0 $self->_current_template($passed_template);
990 0         0 return $self->preprocessed_template($template);
991             };
992              
993             =pod
994              
995             =item process
996              
997             Ahh, the magic method that finally does what we want - turns our template into the populated thing we want to work with.
998              
999             Takes 0, 1, or 2 arguments, and may be called as a class or an object method. If called as an class method, a new template
1000             object is created internally.
1001              
1002             With 0 arguments, you must call it as an object method. The 0 argument form is equivalent to:
1003              
1004             $tpl->process($tpl->template);
1005              
1006             With 1 argument, you may either pass a template OR a hashref of values.
1007              
1008             $tpl->process($template);
1009             $tpl->process($hashref);
1010              
1011             The second form is equivalent to:
1012              
1013             $tpl->process($tpl->template, $hashref);
1014              
1015             With 2 arguments, you pass a template AND a hashref of values.
1016              
1017             $tpl->process($template, $hashref);
1018              
1019             process returns the completed, processed, done template.
1020              
1021             my $page = $tpl->process($hashref) || die $tpl->error();
1022             print $page;
1023              
1024             The hashref contains the values to be populated into the template. Assume your template is:
1025              
1026             Hello, <% $name %>
1027              
1028             Then you may process it as:
1029              
1030             $tpl->process(); # prints "Hello, "
1031             $tpl->process({'name' => \'Billy'}); #prints "Hello, Billy"
1032             $name = 'Jack';
1033             $tpl->process({'name' => \$name}); #prints "Hello, Jack"
1034             $tpl->process({'$name' => \$name}); #prints "Hello, Jack"
1035              
1036             You may pass different types of variables with the same name, if you specify their types.
1037              
1038             $tpl->process( {
1039             '$name' => \$name,
1040             '@name' => \@name,
1041             '%name' => \%name
1042             } );
1043              
1044             If no type is specified in the key, it is assumed to be a scalar.
1045              
1046             Also be warned that while you may pass in a reference to a constant, just like any other constant reference,
1047             you may not then alter its value in your template. Even if you pass in the constant itself, it internally becomes
1048             a reference and you can't change it.
1049              
1050             =cut
1051              
1052             sub process {
1053 0 0   0 1 0 my $self = ref $_[0] ? shift : shift->new();
1054              
1055 0         0 my ($template, $vars) = @_;
1056              
1057             #okay, if the template is not defined (meaning nothing was passed)
1058             #OR it's a hash reference (meaning it's actually the vars hash)
1059             #then we'll set the template to the object's template value or fail out
1060             #then we'll set the vars hash to the template. Not to worry if it's undefined,
1061             #since we'll initialize it later if need be.
1062 0 0 0     0 if (! defined $template || ref $template eq 'HASH'){
1063 0         0 $vars = $template;
1064 0 0       0 $template = $self->template
1065             or return $self->error("Cannot process without template", "BT-03");
1066             };
1067              
1068             #okay, now if we've been passed in a file (not a template reference), then
1069             #we can try to get away with using the cached version
1070              
1071 0 0       0 my $tplpath = ref $template ? 'Inline template' : $template;
1072              
1073 0 0       0 $template = $self->preprocess($template) or return;
1074              
1075 0   0     0 $vars ||= {}; #the vars will just be an empty hash if it's not defined
1076              
1077 0         0 my $pkg = $self->package;
1078 0         0 my $file = $self->file;
1079              
1080             {
1081 2     2   14 no strict 'refs';
  2         4  
  2         1583  
  0         0  
1082              
1083             #make sure there's nothing lurking around inside the template
1084 0         0 %{$pkg . "::"} = ();
  0         0  
1085              
1086             # and nuke the template itself, this will allow us to use the standardized names
1087             # based upon the template file name, AND run that template more than once in the
1088             # same script
1089 0         0 my $stringy_file = $file;
1090 0         0 $stringy_file =~ s/^\$//;
1091 0         0 ${$stringy_file} = undef;
  0         0  
1092              
1093             # finally, import our variables
1094 0         0 foreach my $key (keys %$vars){
1095             #if it's not a ref, we'll assume they wanted to pass in a scalar and make
1096             #it a reference.
1097 0 0 0     0 if (! ref $vars->{$key} || ref($vars->{$key}) !~ /^(REF|HASH|ARRAY|CODE|GLOB|SCALAR)$/) {
1098 0         0 my $val = $vars->{$key};
1099 0         0 $vars->{$key} = \$val;#\$vars->{$key};
1100             };
1101             #return $self->error("Please pass variables as references ($key)", "BT-08")
1102             # unless ref $vars->{$key};
1103              
1104             #strip off leading variable type, if provided
1105 0         0 (my $pkgkey = $key) =~ s/^[\$@%&*]//;
1106 0         0 *{$pkg . "::$pkgkey"} = $vars->{$key};
  0         0  
1107             };
1108             };
1109              
1110 0         0 local $@ = undef;
1111 0         0 my $out = undef;
1112 0         0 my $ec = undef;
1113              
1114 0         0 eval qq{
1115             package $pkg;
1116             local \$@ = undef;
1117             \$out = eval \$template;
1118             \$ec = \$@ if \$@;
1119             };
1120              
1121 0   0     0 return $out || $self->error("Evaluation error in template $tplpath: $ec", "BT-05");
1122              
1123             };
1124              
1125             =pod
1126              
1127             =item escape_for_html
1128              
1129             class method, all it does is turn &, ", ', <, and > into their respective HTML entities. This is
1130             here for simplicity of all the subclasses to display things in templates
1131              
1132             =cut
1133              
1134             =pod
1135              
1136             =begin btest(escape_for_html)
1137              
1138             $test->is(__PACKAGE__->escape_for_html('&'), '&', 'escapes &');
1139             $test->is(__PACKAGE__->escape_for_html('a&'), 'a&', 'escapes &');
1140             $test->is(__PACKAGE__->escape_for_html('&b'), '&b', 'escapes &');
1141             $test->is(__PACKAGE__->escape_for_html('a&b'), 'a&b', 'escapes &');
1142              
1143             $test->is(__PACKAGE__->escape_for_html('"'), '"', 'escapes "');
1144             $test->is(__PACKAGE__->escape_for_html('a"'), 'a"', 'escapes "');
1145             $test->is(__PACKAGE__->escape_for_html('"b'), '"b', 'escapes "');
1146             $test->is(__PACKAGE__->escape_for_html('a"b'), 'a"b', 'escapes "');
1147              
1148             $test->is(__PACKAGE__->escape_for_html("'"), ''', "escapes '");
1149             $test->is(__PACKAGE__->escape_for_html("a'"), 'a'', "escapes '");
1150             $test->is(__PACKAGE__->escape_for_html("'b"), ''b', "escapes '");
1151             $test->is(__PACKAGE__->escape_for_html("a'b"), 'a'b', "escapes '");
1152              
1153             $test->is(__PACKAGE__->escape_for_html('<'), '<', 'escapes <');
1154             $test->is(__PACKAGE__->escape_for_html('a<'), 'a<', 'escapes <');
1155             $test->is(__PACKAGE__->escape_for_html('
1156             $test->is(__PACKAGE__->escape_for_html('a
1157              
1158             $test->is(__PACKAGE__->escape_for_html('>'), '>', 'escapes >');
1159             $test->is(__PACKAGE__->escape_for_html('a>'), 'a>', 'escapes >');
1160             $test->is(__PACKAGE__->escape_for_html('>b'), '>b', 'escapes >');
1161             $test->is(__PACKAGE__->escape_for_html('a>b'), 'a>b', 'escapes >');
1162              
1163             $test->is(__PACKAGE__->escape_for_html('&>'), '&>', 'escapes &>');
1164             $test->is(__PACKAGE__->escape_for_html('<">'), '<">', 'escapes <">');
1165             $test->is(__PACKAGE__->escape_for_html("&&'"), '&&'', "escapes &&'");
1166             $test->is(__PACKAGE__->escape_for_html('<&'), '<&', 'escapes <&');
1167             $test->is(__PACKAGE__->escape_for_html(q('"'')), ''"''', q(escapes '"''));
1168              
1169             $test->is(__PACKAGE__->escape_for_html(), undef, 'escaped nothing returns undef');
1170             $test->is(__PACKAGE__->escape_for_html(undef), undef, 'escaped undef returns nothing');
1171              
1172             =end btest(escape_for_html)
1173              
1174             =cut
1175              
1176             sub escape_for_html {
1177 27     27 1 714 my $self = shift;
1178 27         46 my $string = shift;
1179              
1180 27 100       73 if (defined $string) {
1181 25         51 $string =~ s/&/&/g;
1182 25         49 $string =~ s/"/"/g;
1183 25         56 $string =~ s/'/'/g;
1184 25         51 $string =~ s/
1185 25         49 $string =~ s/>/>/g;
1186             };
1187              
1188 27         235 return $string;
1189             };
1190              
1191              
1192             =pod
1193              
1194             =item escape_for_url
1195              
1196             URL escapes the key/value pair passed. This is here for simplicity of all the subclasses to display things in templates.
1197              
1198             my $escaped = $class->escape_for_url('foo', 'this&that'); #$escape is foo=this%26that
1199              
1200             Also, you may pass an arrayref of values
1201              
1202             my $escaped = $class->escape_for_url('foo', ['this&that', 'me', '***'); #$escape is foo=this%26that&foo=me&foo=%2A%2A%2A
1203              
1204             =cut
1205              
1206             sub escape_for_url {
1207 0     0 1   my $class = shift;
1208 0           my $key = shift;
1209 0           my $value = shift;
1210            
1211 0           $key =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
  0            
1212            
1213 0 0 0       if (defined $value && ref $value eq 'ARRAY'){
    0          
1214 0           my @q = undef;
1215 0           foreach my $v (@$value){
1216 0 0         $v = '' unless defined $v;
1217 0           $v =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
  0            
1218 0           push @q, "$key=$v";
1219             };
1220 0           return join("&", @q);
1221             }
1222             elsif (defined $value){
1223 0           $value =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
  0            
1224 0           return "$key=$value";
1225             }
1226             else {
1227 0           return $key;
1228             };
1229             };
1230              
1231             1;
1232              
1233             __END__