Commit | Line | Data |
e374d8da |
1 | package Template::Simple; |
2 | |
3 | use warnings; |
4 | use strict; |
5 | |
6 | use Carp ; |
7 | use File::Slurp ; |
8 | |
9 | use Data::Dumper ; |
10 | |
11 | our $VERSION = '0.01'; |
12 | |
13 | my %opt_defaults = ( |
14 | |
15 | pre_delim => qr/\[%/, |
16 | post_delim => qr/%\]/, |
17 | greedy_chunk => 0, |
18 | # upper_case => 0, |
19 | # lower_case => 0, |
20 | include_paths => [ qw( templates ) ], |
21 | ) ; |
22 | |
23 | sub new { |
24 | |
25 | my( $class, %opts ) = @_ ; |
26 | |
27 | my $self = bless {}, $class ; |
28 | |
29 | # get all the options or defaults into the object |
30 | |
31 | while( my( $name, $default ) = each %opt_defaults ) { |
32 | |
33 | $self->{$name} = defined( $opts{$name} ) ? |
34 | $opts{$name} : $default ; |
35 | } |
36 | |
37 | # make up the regexes to parse the markup from templates |
38 | |
39 | # this matches scalar markups and grabs the name |
40 | |
41 | $self->{scalar_re} = qr{ |
42 | $self->{pre_delim} |
43 | \s* # optional leading whitespace |
44 | (\w+?) # grab scalar name |
45 | \s* # optional trailing whitespace |
46 | $self->{post_delim} |
47 | }xi ; # case insensitive |
48 | |
49 | #print "RE <$self->{scalar_re}>\n" ; |
50 | |
51 | # this grabs the body of a chunk in either greedy or non-greedy modes |
52 | |
53 | my $chunk_body = $self->{greedy_chunk} ? qr/.+/s : qr/.+?/s ; |
54 | |
55 | # this matches a marked chunk and grabs its name and text body |
56 | |
57 | $self->{chunk_re} = qr{ |
58 | $self->{pre_delim} |
59 | \s* # optional leading whitespace |
60 | START # required START token |
61 | \s+ # required whitespace |
62 | (\w+?) # grab the chunk name |
63 | \s* # optional trailing whitespace |
64 | $self->{post_delim} |
65 | ($chunk_body) # grab the chunk body |
66 | $self->{pre_delim} |
67 | \s* # optional leading whitespace |
68 | END # required END token |
69 | \s+ # required whitespace |
70 | \1 # match the grabbed chunk name |
71 | \s* # optional trailing whitespace |
72 | $self->{post_delim} |
73 | }xi ; # case insensitive |
74 | |
75 | #print "RE <$self->{chunk_re}>\n" ; |
76 | |
77 | # this matches a include markup and grabs its template name |
78 | |
79 | $self->{include_re} = qr{ |
80 | $self->{pre_delim} |
81 | \s* # optional leading whitespace |
82 | INCLUDE # required INCLUDE token |
83 | \s+ # required whitespace |
84 | (\w+?) # grab the included template name |
85 | \s* # optional trailing whitespace |
86 | $self->{post_delim} |
87 | }xi ; # case insensitive |
88 | |
89 | # load in any templates |
90 | |
91 | $self->add_templates( $opts{templates} ) ; |
92 | |
93 | return $self ; |
94 | } |
95 | |
96 | |
97 | |
98 | sub expand { |
99 | |
100 | my( $self, $template, $data ) = @_ ; |
101 | |
102 | # make a copy if a scalar ref is passed as the template text is |
103 | # modified in place |
104 | |
105 | my $tmpl_ref = ref $template eq 'SCALAR' ? $template : \$template ; |
106 | |
107 | my $expanded = $self->_expand_includes( $tmpl_ref ) ; |
108 | |
109 | #print "INC EXP <$expanded>\n" ; |
110 | |
111 | $expanded = eval { |
112 | $self->_expand_chunk( $expanded, $data ) ; |
113 | } ; |
114 | |
115 | croak "Template::Simple $@" if $@ ; |
116 | |
117 | return $expanded ; |
118 | } |
119 | |
120 | sub _expand_includes { |
121 | |
122 | my( $self, $tmpl_ref ) = @_ ; |
123 | |
124 | # make a copy of the initial template so we can expand it. |
125 | |
126 | my $expanded = ${$tmpl_ref} ; |
127 | |
128 | # loop until we can expand no more include markups |
129 | |
130 | 1 while $expanded =~ |
131 | s{$self->{include_re}} |
132 | { ${ $self->_get_template($1) } |
133 | }e ; |
134 | |
135 | return \$expanded ; |
136 | } |
137 | |
138 | my %expanders = ( |
139 | |
140 | HASH => \&_expand_hash, |
141 | ARRAY => \&_expand_array, |
142 | CODE => \&_expand_code, |
143 | # if no ref then data is a scalar so replace the template with just the data |
144 | '' => sub { \$_[2] }, |
145 | ) ; |
146 | |
147 | sub _expand_chunk { |
148 | |
149 | my( $self, $tmpl_ref, $data ) = @_ ; |
150 | |
151 | #print "T ref [$tmpl_ref] [$$tmpl_ref]\n" ; |
152 | #print "CHUNK TMPL\n<$$tmpl_ref>\n" ; |
153 | |
154 | #print Dumper $data ; |
155 | |
156 | return \'' unless defined $data ; |
157 | |
158 | # now expand this chunk based on the type of data |
159 | |
160 | my $expander = $expanders{ref $data} ; |
161 | |
162 | #print "EXP $expander\nREF ", ref $data, "\n" ; |
163 | |
164 | die "unknown template data type '$data'\n" unless defined $expander ; |
165 | |
166 | return $self->$expander( $tmpl_ref, $data ) ; |
167 | } |
168 | |
169 | sub _expand_hash { |
170 | |
171 | my( $self, $tmpl_ref, $href ) = @_ ; |
172 | |
173 | return $tmpl_ref unless keys %{$href} ; |
174 | |
175 | # print "T ref [$tmpl_ref] [$$tmpl_ref]\n" ; |
176 | # print "HASH TMPL\n$$tmpl_ref\n" ; |
177 | |
178 | # we need a local copy of the template to expand |
179 | |
180 | my $expanded = ${$tmpl_ref} ; |
181 | |
182 | # recursively expand all top level chunks in this chunk |
183 | |
184 | $expanded =~ s{$self->{chunk_re}} |
185 | { |
186 | # print "CHUNK $1\nBODY\n----\n<$2>\n\n------\n" ; |
187 | ${$self->_expand_chunk( \$2, $href->{$1} ) }}gex ; |
188 | |
189 | # now expand scalars |
190 | |
191 | #print "HASH TMPL\n<$expanded>\n" ; |
192 | #print Dumper $href ; |
193 | |
194 | $expanded =~ s{$self->{scalar_re}} |
195 | { |
196 | #print "SCALAR $1 VAL $href->{$1}\n" ; |
197 | defined $href->{$1} ? $href->{$1} : '' }ge ; |
198 | |
199 | #print "HASH2 TMPL\n$$expanded\n" ; |
200 | |
201 | return \$expanded ; |
202 | } |
203 | |
204 | sub _expand_array { |
205 | |
206 | my( $self, $tmpl_ref, $aref ) = @_ ; |
207 | |
208 | # expand this $tmpl_ref for each element of the aref and join them |
209 | |
210 | my $expanded ; |
211 | |
212 | #print Dumper $aref ; |
213 | |
214 | $expanded .= ${$self->_expand_chunk( $tmpl_ref, $_ )} for @{$aref} ; |
215 | |
216 | return \$expanded ; |
217 | } |
218 | |
219 | sub _expand_code { |
220 | |
221 | my( $self, $tmpl_ref, $cref ) = @_ ; |
222 | |
223 | my $expanded = $cref->( $tmpl_ref ) ; |
224 | |
225 | croak <<CROAK if ref $expanded ne 'SCALAR' ; |
226 | code expansion didn't return a scalar or scalar reference |
227 | CROAK |
228 | |
229 | return $expanded ; |
230 | } |
231 | |
232 | sub add_templates { |
233 | |
234 | my( $self, $tmpls ) = @_ ; |
235 | |
236 | #print Dumper $tmpls ; |
237 | return unless defined $tmpls ; |
238 | |
239 | ref $tmpls eq 'HASH' or croak "templates argument is not a hash ref" ; |
240 | |
241 | @{ $self->{templates}}{ keys %{$tmpls} } = |
242 | map ref $_ eq 'SCALAR' ? \"${$_}" : \"$_", values %{$tmpls} ; |
243 | |
244 | #print Dumper $self->{templates} ; |
245 | |
246 | return ; |
247 | } |
248 | |
249 | sub delete_templates { |
250 | |
251 | my( $self, @names ) = @_ ; |
252 | |
253 | @names = keys %{$self->{templates}} unless @names ; |
254 | |
255 | delete @{$self->{templates}}{ @names } ; |
256 | |
257 | delete @{$self->{template_paths}}{ @names } ; |
258 | |
259 | return ; |
260 | } |
261 | |
262 | sub _get_template { |
263 | |
264 | my( $self, $tmpl_name ) = @_ ; |
265 | |
266 | #print "INC $tmpl_name\n" ; |
267 | |
268 | my $tmpls = $self->{templates} ; |
269 | |
270 | # get the template from the cache and send it back if it was found there |
271 | |
272 | my $template = $tmpls->{ $tmpl_name } ; |
273 | return $template if $template ; |
274 | |
275 | # not found, so find, slurp in and cache the template |
276 | |
277 | $template = $self->_find_template( $tmpl_name ) ; |
278 | $tmpls->{ $tmpl_name } = $template ; |
279 | |
280 | return $template ; |
281 | } |
282 | |
283 | sub _find_template { |
284 | |
285 | my( $self, $tmpl_name ) = @_ ; |
286 | |
287 | foreach my $dir ( @{$self->{include_paths}} ) { |
288 | |
289 | my $tmpl_path = "$dir/$tmpl_name.tmpl" ; |
290 | |
291 | print "PATH: $tmpl_path\n" ; |
292 | next unless -r $tmpl_path ; |
293 | |
294 | # cache the path to this template |
295 | |
296 | $self->{template_paths}{$tmpl_name} = $tmpl_path ; |
297 | |
298 | # slurp in the template file and return it as a scalar ref |
299 | |
300 | return scalar read_file( $tmpl_path, scalar_ref => 1 ) ; |
301 | } |
302 | |
303 | croak <<CROAK ; |
304 | can't find template '$tmpl_name' in '@{$self->{include_paths}}' |
305 | CROAK |
306 | |
307 | } |
308 | |
309 | 1; # End of Template::Simple |
310 | |
311 | __END__ |
312 | |
313 | =head1 NAME |
314 | |
315 | Template::Simple - A simple and fast template module |
316 | |
317 | =head1 VERSION |
318 | |
319 | Version 0.01 |
320 | |
321 | =head1 SYNOPSIS |
322 | |
323 | use Template::Simple; |
324 | |
325 | my $tmpl = Template::Simple->new(); |
326 | |
327 | my $template = <<TMPL ; |
328 | [%INCLUDE header%] |
329 | [%START row%] |
330 | [%first%] - [%second%] |
331 | [%END row%] |
332 | [%INCLUDE footer%] |
333 | TMPL |
334 | |
335 | my $data = { |
336 | header_data => { |
337 | date => 'Jan 1, 2008', |
338 | author => 'Me, myself and I', |
339 | }, |
340 | row => [ |
341 | { |
342 | first => 'row 1 value 1', |
343 | second => 'row 1 value 2', |
344 | }, |
345 | { |
346 | first => 'row 2 value 1', |
347 | second => 'row 2 value 2', |
348 | }, |
349 | ], |
350 | footer_data => { |
351 | modified => 'Aug 31, 2006', |
352 | }, |
353 | } ; |
354 | |
355 | my $expanded = $tmpl->expand( $template, $data ) ; |
356 | |
357 | =head1 DESCRIPTION |
358 | |
359 | Template::Simple has these goals: |
360 | |
361 | =over 4 |
362 | |
363 | =item * Support most common template operations |
364 | |
365 | It can recursively include other templates, replace tokens (scalars), |
366 | recursively expand nested chunks of text and expand lists. By using |
367 | simple idioms you can get conditional expansions. |
368 | |
369 | =item * Complete isolation of template from program code |
370 | |
371 | This is very important as template design can be done by different |
372 | people than the program logic. It is rare that one person is well |
373 | skilled in both template design and also programming. |
374 | |
375 | =item * Very simple template markup (only 4 markups) |
376 | |
377 | The only markups are C<INCLUDE>, C<START>, C<END> and C<token>. See |
378 | MARKUP for more. |
379 | |
380 | =item * Easy to follow expansion rules |
381 | |
382 | Expansion of templates and chunks is driven from a data tree. The type |
383 | of the data element used in an expansion controls how the expansion |
384 | happens. The data element can be a scalar or scalar reference or an |
385 | array, hash or code reference. |
386 | |
387 | =item * Efficient template expansion |
388 | |
389 | Expansion is very simple and uses Perl's regular expressions |
390 | efficiently. Because the markup is so simple less processing is needed |
391 | than many other templaters. Precompiling templates is not supported |
392 | yet but that optimization is on the TODO list. |
393 | |
394 | =item * Easy user extensions |
395 | |
396 | User code can be called during an expansion so you can do custom |
397 | expansions and plugins. Closures can be used so the code can have its |
398 | own private data for use in expanding its template chunk. |
399 | |
400 | =back |
401 | |
402 | =head2 new() |
403 | |
404 | You create a Template::Simple by calling the class method new: |
405 | |
406 | my $tmpl = Template::Simple->new() ; |
407 | |
408 | All the arguments to C<new()> are key/value options that change how |
409 | the object will do expansions. |
410 | |
411 | =over 4 |
412 | |
413 | =item pre_delim |
414 | |
415 | This option sets the string or regex that is the starting delimiter |
416 | for all markups. You can use a plain string or a qr// but you need to |
417 | escape (with \Q or \) any regex metachars if you want them to be plain |
418 | chars. The default is qr/\[%/. |
419 | |
420 | my $tmpl = Template::Simple->new( |
421 | pre_delim => '<%', |
422 | ); |
423 | |
424 | my $expanded = $tmpl->expand( '<%FOO%]', 'bar' ) ; |
425 | |
426 | =item post_delim |
427 | |
428 | This option sets the string or regex that is the ending delimiter |
429 | for all markups. You can use a plain string or a qr// but you need to |
430 | escape (with \Q or \) any regex metachars if you want them to be plain |
431 | chars. The default is qr/%]/. |
432 | |
433 | my $tmpl = Template::Simple->new( |
434 | post_delim => '%>', |
435 | ); |
436 | |
437 | my $expanded = $tmpl->expand( '[%FOO%>', 'bar' ) ; |
438 | |
439 | =item greedy_chunk |
440 | |
441 | This boolean option will cause the regex that grabs a chunk of text |
442 | between the C<START/END> markups to become greedy (.+). The default is |
443 | a not-greedy grab of the chunk text. (UNTESTED) |
444 | |
445 | =item templates |
446 | |
447 | This option lets you load templates directly into the cache of the |
448 | Template::Simple object. This cache will be searched by the C<INCLUDE> |
449 | markup which will be replaced by the template if found. The option |
450 | value is a hash reference which has template names (the name in the |
451 | C<INCLUDE> markup) for keys and their template text as their |
452 | values. You can delete or clear templates from the object cache with |
453 | the C<delete_template> method. |
454 | |
455 | |
456 | my $tmpl = Template::Simple->new( |
457 | templates => { |
458 | |
459 | foo => <<FOO, |
460 | [%baz%] is a [%quux%] |
461 | FOO |
462 | bar => <<BAR, |
463 | [%user%] is not a [%fool%] |
464 | BAR |
465 | }, |
466 | ); |
467 | |
468 | my $template = <<TMPL ; |
469 | [%INCLUDE foo %] |
470 | TMPL |
471 | |
472 | my $expanded = $tmpl->expand( |
473 | $template, |
474 | { |
475 | baz => 'blue', |
476 | quux => 'color, |
477 | } |
478 | ) ; |
479 | |
480 | =item include_paths |
481 | |
482 | Template::Simple can also load C<INCLUDE> templates from files. This |
483 | option lets you set the directory paths to search for those |
484 | files. Note that the template name in the C<INCLUDE> markup has the |
485 | .tmpl suffix appended to it when searched for in one of these |
486 | paths. The loaded file is cached inside the Template::Simple object |
487 | along with any loaded by the C<templates> option. |
488 | |
489 | =back |
490 | |
491 | =head1 METHODS |
492 | |
493 | =head2 expand( $template, $data ) |
494 | |
495 | =head2 add_templates |
496 | |
497 | This method adds templates to the object cache. It takes a list of template names and texts just like the C<templates> constructor option. |
498 | |
499 | $tmpl->add_templates( |
500 | { |
501 | foo => \$foo_template, |
502 | bar => '[%include bar%]', |
503 | } |
504 | ) ; |
505 | |
506 | =head2 delete_templates |
507 | |
508 | This method takes a list of template names and will delete them from |
509 | the template cache in the object. If you pass in an empty list then |
510 | all the templates will be deleted. This can be used when you know a |
511 | template file has been updated and you want to get it loaded back into |
512 | the cache. Note that you can delete templates that were loaded |
513 | directly (via the C<templates> constructor option or the |
514 | C<add_templates> method) or loaded from a file. |
515 | |
516 | # this deletes only the foo and bar templates from the object cache |
517 | |
518 | $tmpl->delete_templates( qw( foo bar ) ; |
519 | |
520 | # this deletes all of templates from the object cache |
521 | |
522 | $tmpl->delete_templates() ; |
523 | |
524 | =head2 get_dependencies |
525 | |
526 | This method expand the only C<INCLUDE> markups of a template and it |
527 | returns a list of the file paths that were found and loaded. It is |
528 | meant to be used to build up a dependency list of included templates |
529 | for a main template. Typically this can be called from a script (see |
530 | TODO) that will do this for a set of main templates and will generate |
531 | Makefile dependencies for them. Then you can regenerate expanded |
532 | templates only when any of their included templates have changed. It |
533 | takes a single argument of a template. |
534 | |
535 | UNKNOWN: will this require a clearing of the cache or will it do the |
536 | right thing on its own? or will it use the file path cache? |
537 | |
538 | my @dependencies = |
539 | $tmpl->get_dependencies( '[%INCLUDE top_level%]' ); |
540 | |
541 | =head1 MARKUP |
542 | |
543 | All the markups in Template::Simple use the same delimiters which are |
544 | C<[%> and C<%]>. You can change the delimiters with the C<pre_delim> |
545 | and C<post_delim> options in the C<new()> constructor. |
546 | |
547 | =head2 Tokens |
548 | |
549 | A token is a single markup with a C<\w+> Perl word inside. The token |
550 | can have optional whitespace before and after it. A token is replaced |
551 | by a value looked up in a hash with the token as the key. The hash |
552 | lookup keeps the same case as parsed from the token markup. |
553 | |
554 | [% foo %] [%BAR%] |
555 | |
556 | Those will be replaced by C<$href->{foo}> and C<$href->{BAR}> assuming |
557 | C<$href> is the current data for this expansion. Tokens are only |
558 | parsed out during hash data expansion so see Hash Data for more. |
559 | |
560 | =head2 Chunks |
561 | |
562 | Chunks are regions of text in a template that are marked off with a |
563 | start and end markers with the same name. A chunk start marker is |
564 | C<[%START name%]> and the end marker for that chunk is C<[%END |
565 | name%]>. C<name> is a C<\w+> Perl word which is the name of this |
566 | chunk. The whitespace between C<START/END> and C<name> is required and |
567 | there is optional whitespace before C<START/END> and after the |
568 | C<name>. C<START/END> are case insensitive but the C<name>'s case is |
569 | kept. C<name> must match in the C<START/END> pair and it used as a key |
570 | in a hash data expansion. Chunks are the primary way to markup |
571 | templates for structures (sets of tokens), nesting (hashes of hashes), |
572 | repeats (array references) and callbacks to user code. Chunks are only |
573 | parsed out during hash data expansion so see Hash Data for more. |
574 | |
575 | The body of text between the C<START/END> markups is grabbed with a |
576 | C<.+?> regular expression with the /s option enabled so it will match |
577 | all characters. By default it will be a non-greedy grab but you can |
578 | change that in the constructor by enabling the C<greedy_chunk> option. |
579 | |
580 | [%Start FOO%] |
581 | [% START bar %] |
582 | [% field %] |
583 | [% end bar %] |
584 | [%End FOO%] |
585 | |
586 | =head2 Includes |
587 | |
588 | =head1 EXPANSION RULES |
589 | |
590 | Template::Simple has a short list of expansion rules and they are easy |
591 | to understand. There are two types of expansions, include expansion |
592 | and chunk expansion. In the C<expand> method, the template is an |
593 | unnamed top level chunk of text and it first gets its C<INCLUDE> |
594 | markups expanded. The text then undergoes a chunk expansion and a |
595 | scalar reference to that expanded template is returned to the caller. |
596 | |
597 | =head2 Include Expansion |
598 | |
599 | Include expansion is performed one time on a top level template. When |
600 | it is done the template is ready for chunk expansion. Any markup of |
601 | the form C<[%INCLUDE name]%> will be replaced by the text found in the |
602 | template C<name>. The template name is looked up in the object's |
603 | template cache and if it is found there its text is used as the |
604 | replacement. |
605 | |
606 | If a template is not found in the cache, it will be searched for in |
607 | the list of directories in the C<include_paths> option. The file name |
608 | will be a directory in that list appended with the template name and |
609 | the C<.tmpl> suffix. The first template file found will be read in and |
610 | stored in the cache. Its path is also saved and those will be returned |
611 | in the C<get_dependencies> method. See the C<add_templates> and |
612 | C<delete_templates> methods and the C<include_paths> option. |
613 | |
614 | Expanded include text can contain more C<INCLUDE> markups and they |
615 | will also be expanded. The include expansion phase ends where there |
616 | are no more C<INCLUDE> found. |
617 | |
618 | =head2 Chunk Expansion |
619 | |
620 | A chunk is the text found between C<START> and C<END> markups and it |
621 | gets its named from the C<START> markup. The top level template is |
622 | considered an unamed chunk and also gets chunk expanded. |
623 | |
624 | The data for a chunk determines how it will be expanded. The data can |
625 | be a scalar or scalar reference or an array, hash or code |
626 | reference. Since chunks can contain nested chunks, expansion will |
627 | recurse down the data tree as it expands the chunks. Each of these |
628 | expansions are explained below. Also see the IDIOMS and BEST PRACTICES |
629 | section for examples and used of these expansions. |
630 | |
631 | =head2 Scalar Data Expansion |
632 | |
633 | If the current data for a chunk is a scalar or scalar reference, the |
634 | chunk's text in the templated is replaced by the scalar's value. This |
635 | can be used to overwrite one default section of text with from the |
636 | data tree. |
637 | |
638 | =head2 Code Data Expansion |
639 | |
640 | If the current data for a chunk is a code reference (also called |
641 | anonymous sub) then the code reference is called and it is passed a |
642 | scalar reference to the that chunk's text. The code must return a |
643 | scalar or a scalar reference and its value replaces the chunk's text |
644 | in the template. If the code returns any other type of data it is a |
645 | fatal error. Code expansion is how you can do custom expansions and |
646 | plugins. A key idiom is to use closures as the data in code expansions |
647 | and keep the required outside data in the closure. |
648 | |
649 | =head2 Array Data Expansion |
650 | |
651 | If the current data for a chunk is an array reference do a full chunk |
652 | expansion for each value in the array. It will replace the original |
653 | chunk text with the joined list of expanded chunks. This is how you do |
654 | repeated sections in Template::Simple and why there is no need for any |
655 | loop markups. Note that this means that expanding a chunk with $data |
656 | and [ $data ] will do the exact same thing. A value of an empty array |
657 | C<[]> will cause the chunk to be replaced by the empty string. |
658 | |
659 | =head2 Hash Data Expansion |
660 | |
661 | If the current data for a chunk is a hash reference then two phases of |
662 | expansion happen, nested chunk expansion and token expansion. First |
663 | nested chunks are parsed of of this chunk along with their names. Each |
664 | parsed out chunk is expanded based on the value in the current hash |
665 | with the nested chunk's name as the key. |
666 | |
667 | If a value is not found (undefined), then the nested chunk is replaced |
668 | by the empty string. Otherwise the nested chunk is expanded according |
669 | to the type of its data (see chunk expansion) and it is replaced by |
670 | the expanded text. |
671 | |
672 | Chunk name and token lookup in the hash data is case sensitive (see |
673 | the TODO for cased lookups). |
674 | |
675 | Note that to keep a plain text chunk or to just have the all of its |
676 | markups (chunks and tokens) be deleted just pass in an empty hash |
677 | reference C<{}> as the data for the chunk. It will be expanded but all |
678 | markups will be replaced by the empty string. |
679 | |
680 | =head2 Token Expansion |
681 | |
682 | The second phase is token expansion. Markups of the form [%token%] are |
683 | replaced by the value of the hash element with the token as the |
684 | key. If a token's value is not defined it is replaced by the empty |
685 | string. This means if a token key is missing in the hash or its value |
686 | is undefined or its value is the empty string, the [%token%] markup |
687 | will be deleted in the expansion. |
688 | |
689 | =head1 IDIOMS and BEST PRACTICES |
690 | |
691 | With all template systems there are better ways to do things and |
692 | Template::Simple is no different. This section will show some ways to |
693 | handle typical template needs while using only the 4 markups in this |
694 | module. |
695 | |
696 | =head2 Conditionals |
697 | |
698 | This conditional idiom can be when building a fresh data tree or |
699 | modifying an existing one. |
700 | |
701 | $href->{$chunk_name} = $keep_chunk ? {} : '' ; |
702 | |
703 | If you are building a fresh data tree you can use this idiom to do a |
704 | conditional chunk: |
705 | |
706 | $href->{$chunk_name} = {} if $keep_chunk ; |
707 | |
708 | To handle an if/else conditional use two chunks, with the else chunk's |
709 | name prefixed with NOT_ (or use any name munging you want). Then you |
710 | set the data for either the true chunk (just the plain name) or the |
711 | false trunk with the NOT_ name. You can use a different name for the |
712 | else chunk if you want but keeping the names of the if/else chunks |
713 | related is a good idea. Here are two ways to set the if/else data. The |
714 | first one uses the same data for both the if and else chunks and the |
715 | second one uses different data so the it uses the full if/else code |
716 | for that. |
717 | |
718 | $href->{ ($boolean ? '' : 'NOT_') . $chunk_name} = $data |
719 | |
720 | if ( $boolean ) { |
721 | $href->{ $chunk_name} = $true_data ; |
722 | else { |
723 | $href->{ "NOT_$chunk_name" } = $false_data ; |
724 | } |
725 | |
726 | NOTE TO ALPHA USERS: i am also thinking that a non-existing key or |
727 | undefined hash value should leave the chunk as is. then you would need |
728 | to explicitly replace a chunk with the empty string if you wanted it |
729 | deleted. It does affect the list of styles idiom. Any thoughts on |
730 | this change of behavior? Since this hasn't been released it is the |
731 | time to decide this. |
732 | |
733 | =head2 Chunked Includes |
734 | |
735 | One of the benefits of using include templates is the ability to share |
736 | and reuse existing work. But if an included template has a top level |
737 | named chunk, then that name would also be the same everywhere where |
738 | this template is included. If a template included another template in |
739 | multiple places, its data tree would use the same name for each and |
740 | not allow unique data to be expanded for each include. A better way is |
741 | to have the current template wrap an include markup in a named chunk |
742 | markup. Then the data tree could use unique names for each included |
743 | template. Here is how it would look: |
744 | |
745 | [%START foo_prime%][%INCLUDE foo%][%START foo_prime%] |
746 | random noise |
747 | [%START foo_second%][%INCLUDE foo%][%START foo_second%] |
748 | |
749 | See the TODO section for some ideas on how to make this even more high level. |
750 | |
751 | =head2 Repeated Sections |
752 | |
753 | If you looked at the markup of Template::Simple you have noticed that |
754 | there is no loop or repeat construct. That is because there is no need |
755 | for one. Any chunk can be expanded in a loop just by having its |
756 | expansion data be an anonymous array. The expander will loop over each |
757 | element of the array and do a fresh expansion of the chunk with this |
758 | data. A join (on '') of the list of expansions replaces the original |
759 | chunk and you have a repeated chunk. |
760 | |
761 | =head2 A List of Mixed Styles |
762 | |
763 | One formating style is to have a list of sections each which can have |
764 | its own style or content. Template::Simple can do this very easily |
765 | with just a 2 level nested chunk and an array of data for |
766 | expansion. The outer chunk includes (or contains) each of the desired |
767 | styles in any order. It looks like this: |
768 | |
769 | [%START para_styles%] |
770 | [%START main_style%] |
771 | [%INCLUDE para_style_main%] |
772 | [%END main_style%] |
773 | [%START sub_style%] |
774 | [%INCLUDE para_style_sub%] |
775 | [%END sub_style%] |
776 | [%START footer_style%] |
777 | [%INCLUDE para_style_footer%] |
778 | [%END footer_style%] |
779 | [%END para_styles%] |
780 | |
781 | The other part to make this work is in the data tree. The data for |
782 | para_styles should be a list of hashes. Each hash contains the data |
783 | for one pargraph style which is keyed by the style's chunk name. Since |
784 | the other styles's chunk names are not hash they are deleted. Only the |
785 | style which has its name as a key in the hash is expanded. The data |
786 | tree would look something like this: |
787 | |
788 | [ |
789 | { |
790 | main_style => $main_data, |
791 | }, |
792 | { |
793 | sub_style => $sub_data, |
794 | }, |
795 | { |
796 | sub_style => $other_sub_data, |
797 | }, |
798 | { |
799 | footer_style => $footer_data, |
800 | }, |
801 | ] |
802 | |
803 | =head1 TESTS |
804 | |
805 | The test scripts use a common test driver module in t/common.pl. It is |
806 | passed a list of hashes, each of which has the data for one test. A |
807 | test can create a ne Template::Simple object or use the one from the |
808 | previous test. The template source, the data tree and the expected |
809 | results are also important keys. See the test scripts for examples of |
810 | how to write tests using this common driver. |
811 | |
812 | =over 4 |
813 | |
814 | =item name |
815 | |
816 | This is the name of the test and is used by Test::More |
817 | |
818 | =item opts |
819 | |
820 | This is a hash ref of the options passed to the Template::Simple |
821 | constructor. The object is not built if the C<keep_obj> key is set. |
822 | |
823 | =item keep_obj |
824 | |
825 | If set, this will make this test keep the Template::Simple object from |
826 | the previous test and not build a new one. |
827 | |
828 | =item template |
829 | |
830 | This is the template to expand for this test. |
831 | |
832 | =item data |
833 | |
834 | This is the data tree for the expansion of the template. |
835 | |
836 | =item expected |
837 | |
838 | This is the text that is expected after the expansion. |
839 | |
840 | =item skip |
841 | |
842 | If set, this test is skipped. |
843 | |
844 | =back |
845 | |
846 | =head1 TODO |
847 | |
848 | Even though this template system is simple, that doesn't mean it can't |
849 | be extended in many ways. Here are some features and designs that |
850 | would be good extensions which add useful functionality without adding |
851 | too much complexity. |
852 | |
853 | =head2 Compiled Templates |
854 | |
855 | A commonly performed optimization in template modules is to precompile |
856 | (really preparse) templates into a internal form that will expand |
857 | faster. Precompiling is slower than expansion from the original |
858 | template which means you won't want to do it for each expansion. This |
859 | means it has a downside that you lose out when you want to expand |
860 | using templates which change often. Template::Simple makes it very |
861 | easy to precompile as it already has the regexes to parse out the |
862 | markup. So instead of calling subs to do actual expansion, a |
863 | precompiler would call subs to generate a compiled expansion tree. |
864 | The expansion tree can then be run or processes with expansion data |
865 | passed to it. You can think of a precompiled template as having all |
866 | the nested chunks be replaced by nested code that does the same |
867 | expansion. It can still do the dynamic expansion of the data but it |
868 | saves the time of parsing the template souice. There are three |
869 | possible internal formats for the precompiled template: |
870 | |
871 | =over 4 |
872 | |
873 | =item Source code |
874 | |
875 | This precompiler will generate source code that can be stored and/or |
876 | eval'ed. The eval'ed top level sub can then be called and passed the |
877 | expansion data. |
878 | |
879 | =item Closure call tree |
880 | |
881 | The internal format can be a nested set of closures. Each closure would contain |
882 | private data such as fixed text parts of the original template, lists |
883 | of other closures to run, etc. It is trivial to write a basic closure |
884 | generator which will make build this tree a simple task. |
885 | |
886 | =item Code ref call tree |
887 | |
888 | This format is a Perl data tree where the nodes have a code reference |
889 | and its args (which can be nested instances of the same |
890 | nodes). Instead of executing this directly, you will need a small |
891 | interpreter to execute all the code refs as it runs through the tree. |
892 | |
893 | This would make for a challenging project to any intermediate Perl |
894 | hacker. It just involves knowing recursion, data trees and code refs. |
895 | Contact me if you are interested in doing this. |
896 | |
897 | =back |
898 | |
899 | =head2 Cased Hash Lookups |
900 | |
901 | One possible option is to allow hash expansions to always use upper or |
902 | lower cased keys in their lookups. |
903 | |
904 | =head2 Expand tokens before includes and chunks |
905 | |
906 | Currently tokens are expanded after includes and chunks. If tokens |
907 | were expanded in a pass before the others, the include and chunk names |
908 | could be dynamically set. This would make it harder to precompile |
909 | templates as too much would be dynamic, i.e. you won't know what the |
910 | fixed text to parse out is since anything can be included at expand |
911 | time. But the extra flexibility of changing the include and chunk |
912 | names would be interesting. It could be done easily and enabled by an |
913 | option. |
914 | |
915 | =head2 Plugins |
916 | |
917 | There are two different potential areas in Template::Simple that could |
918 | use plugins. The first is with the expansion of chunkas and |
919 | dispatching based on the data type. This dispatch table can easily be |
920 | replaced by loaded modules which offer a different way to |
921 | expand. These include the precompiled expanders mentioned above. The |
922 | other area is with code references as the data type. By defining a |
923 | closure (or a closure making) API you can create different code refs |
924 | for the expansion data. The range of plugins is endless some of the |
925 | major template modules have noticed. One idea is to make a closure |
926 | which contains a different Template::Simple object than the current |
927 | one. This will allow expansion of a nested chunk with different rules |
928 | than the current chunk being expanded. |
929 | |
930 | =head2 Data Escaping |
931 | |
932 | Some templaters have options to properly escape data for some types of |
933 | text files such as html. this can be done with some variant of the |
934 | _expand_hash routine which also does the scalar expansion (which is |
935 | where data is expanded). The expanding scalars code could be factored |
936 | out into a set of subs one of which is used based on any escaping |
937 | needs. |
938 | |
939 | =head2 Data Tree is an Object |
940 | |
941 | This is a concept I don't like but it was requested so it goes into |
942 | the TODO file. Currently C<expand> can only be passed a regular |
943 | (unblessed) ref (or a scalar) for its data tree. Passing in an object |
944 | would break encapsulation and force the object layout to be a hash |
945 | tree that matches the layout of the template. I doubt that most |
946 | objects will want to be organized to match a template. I have two |
947 | ideas, one is that you add a method to that object that builds up a |
948 | proper (unblessed) data tree to pass to C<expand>. The other is by |
949 | subclassing C<Template::Simple> and overriding C<expand> with a sub |
950 | that does take an object hash and it can unbless it or build a proper |
951 | data tree and then call C<expand> in SUPER::. A quick solution is to |
952 | use C<reftype> (from Scalar::Utils) instead of C<ref> to allow object |
953 | hashes to be passed in. |
954 | |
955 | =head2 Includes and Closure Synergy |
956 | |
957 | By pairing up an include template along with code that can generate |
958 | the appropriate data tree for its expansion, you can create a higher |
959 | level template framework (the synergy). Additional code can be |
960 | associated with them that will handle input processing and |
961 | verification for the templates (e.g. web forms) that need it. A key to |
962 | this will be making all the closures for the data tree. This can be |
963 | greatly simplified by using a closure maker sub that can create all |
964 | the required closures. |
965 | |
966 | =head2 Metafields and UI Generation |
967 | |
968 | Taking the synergy up to a much higher level is the concept of meta |
969 | knowledge of fields which can generate templates, output processing |
970 | (data tree generation), input processing, DB backing and more. If you |
971 | want to discuss such grandiose wacky application schemes in a long |
972 | rambling mind bending conversation, please contact me. |
973 | |
974 | =head2 More Examples and Idioms |
975 | |
976 | As I convert several scripts over to this module (they all used the |
977 | hack version), I will add them to an examples section or possibly put |
978 | them in another (pod only) module. Similarly the Idioms section needs |
979 | expansion and could be also put into a pod module. One goal requested |
980 | by an early alpha tester is to keep the primary docs as simple as the |
981 | markup itself. This means moving all the extra stuff (and plenty of |
982 | that) into other pod modules. All the pod modules would be in the same |
983 | cpan tarball so you get all the docs and examples when you install |
984 | this. |
985 | |
986 | =head1 AUTHOR |
987 | |
988 | Uri Guttman, C<< <uri at sysarch.com> >> |
989 | |
990 | =head1 BUGS |
991 | |
992 | Please report any bugs or feature requests to |
993 | C<bug-template-simple at rt.cpan.org>, or through the web interface at |
994 | L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Template-Simple>. |
995 | I will be notified, and then you'll automatically be notified of progress on |
996 | your bug as I make changes. |
997 | |
998 | =head1 SUPPORT |
999 | |
1000 | You can find documentation for this module with the perldoc command. |
1001 | |
1002 | perldoc Template::Simple |
1003 | |
1004 | You can also look for information at: |
1005 | |
1006 | =over 4 |
1007 | |
1008 | =item * RT: CPAN's request tracker |
1009 | |
1010 | L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Template-Simple> |
1011 | |
1012 | =item * Search CPAN |
1013 | |
1014 | L<http://search.cpan.org/dist/Template-Simple> |
1015 | |
1016 | =back |
1017 | |
1018 | =head1 ACKNOWLEDGEMENTS |
1019 | |
1020 | I wish to thank Turbo10 for their support in developing this module. |
1021 | |
1022 | =head1 COPYRIGHT & LICENSE |
1023 | |
1024 | Copyright 2006 Uri Guttman, all rights reserved. |
1025 | |
1026 | This program is free software; you can redistribute it and/or modify it |
1027 | under the same terms as Perl itself. |
1028 | |
1029 | =cut |
1030 | |
1031 | |
1032 | find templates and tests |
1033 | |
1034 | deep nesting tests |
1035 | |
1036 | greedy tests |
1037 | |
1038 | methods pod |
1039 | |
1040 | delete_templates test |
1041 | |
1042 | pod cleanup |
1043 | |
1044 | fine edit |
1045 | |
1046 | more tests |
1047 | |
1048 | slurp dependency in makefile.pl |
1049 | |