token_re, include cleanup, git cleanup
[urisagit/Template-Simple.git] / extras / cookbook.pl
1 #!/usr/bin/perl
2
3 use strict ;
4 use warnings ;
5 use lib '../lib' ;
6
7 use File::Slurp ;
8
9 =head1 Template::Simple Cookbook
10
11 This cookbook contains examples of idioms and best practices when
12 using the Template::Simple module. It will illustrate all the the
13 features of this module and show various combinations of code to get
14 the job done. Send any requests for more examples to
15 E<lt>template@stemsystems.comE<gt>. Read the source code to see the
16 working examples or you can run this script to see all the
17 output. Each example has its title printed and rendered templates are
18 printed with a KEY: prefix and delimited with [].
19
20 By combining these techniques you can create and built complex
21 template applications that can do almost any task other templaters can
22 do. You will be able to share more code logic and templates as they
23 are totally isolated and independent from each other.
24
25 =head2 Use Scalar References
26
27 When passing text either as templates or in data tree elements, it is
28 generally faster to use scalar references than plain scalars. T::S can
29 accept text either way so you choose the style you like best. Most of
30 the examples here will use scalar references. Note that passing a
31 scalar reference to the new() constructor as the template will force
32 that to be a template and not a template name so no lookup will
33 occur. T::S always treats all text values as read only and never
34 modifies incoming data.
35
36 =cut
37
38 use Template::Simple ;
39 my $ts = Template::Simple->new() ;
40 my $template ;
41
42
43 =head1 Token Expansion
44
45 The simplest use of templates is replacing single tokens with
46 values. This is vry similar to interpolation of scalar variables in a
47 double quoted string. The difference is that the template text can
48 come from outside the program whereas double quoted strings must be in
49 the code (eval STRING doesn't count).
50
51 To replace tokens all you need is a template with token markups
52 (e.g. C<[% foo %]>) and a hash with those tokens as keys and the
53 values with which to replace them. Remember the top level template is
54 treated as an unnamed chunk so you can pass a hash reference to
55 render.
56
57 =cut
58
59 print "\n******************\nToken Expansion\n******************\n\n" ;
60
61 $template = <<TMPL ;
62 This [% token %] will be replaced as will [%foo%] and [% bar %]
63 TMPL
64
65 my $token_expansion = $ts->render( $template,
66         {
67         token   => 'markup',
68         foo     => 'this',
69         bar     => 'so will this',
70         }
71 ) ;
72
73 print "TOKEN EXPANSION: [${$token_expansion}]\n" ;
74
75 =head1 Token Deletion
76
77 Sometimes you want to delete a token and not replace it with text. All
78 you need to do is use the null string for its data. Altenatively if
79 you are rendering a chunk with a hash (see below for more examples)
80 you can just not have any data for the token and it will also be
81 deleted. Both styles are shown in this example.
82
83 =cut
84
85 print "\n******************\nToken Deletion\n******************\n\n" ;
86
87 $template = <<TMPL ;
88 This [% token %]will be deleted as will [%foo%]
89 TMPL
90
91 my $token_deletion = $ts->render( $template,
92         {
93         token   => '',
94         }
95 ) ;
96
97 print "TOKEN DELETION: [${$token_deletion}]\n" ;
98
99
100 =head1 Named Templates
101
102 You can pass a template directly to the C<render> method or pass in
103 its name. A named template will be searched for in the object cache
104 and then in the C<template_paths> directories. Templates can be loaded
105 into the cache with in the new() call or added later with the
106 C<add_templates> method.
107
108 =cut
109
110 print "\n******************\nNamed Templates\n******************\n\n" ;
111
112 $ts = Template::Simple->new(
113         templates       => {
114                 foo     => <<FOO,
115 We have some foo text here with [% data %]
116 FOO
117         }
118 ) ;
119
120 my $foo_template = $ts->render( 'foo', { data => 'lots of foo' } ) ;
121
122 $ts->add_templates( { bar => <<BAR } ) ;
123 We have some bar text here with [% data %]
124 BAR
125
126 my $bar_template = $ts->render( 'bar', { data => 'some bar' } ) ;
127
128 print "FOO TEMPLATE: [${$foo_template}]\n" ;
129 print "BAR TEMPLATE: [${$bar_template}]\n" ;
130
131 =head1 Include Expansion
132
133 You can build up templates by including other templates. This allows a
134 template to be reused and shared by other templates. What makes this
135 even better, is that by passing different data to the included
136 templates in different renderings, you can get different results. If
137 the logic was embedded in the template you can't change the rendering
138 as easily. You include a template by using the C<[%include name%]>
139 markup. The name is used to locate a template by name and its text
140 replaces the markup. This example shows a single include in the top
141 level template.
142
143 =cut
144
145 print "\n******************\nInclude Expansion\n******************\n\n" ;
146
147 $ts = Template::Simple->new(
148         templates       => {
149                 top     => <<TOP,
150 This top level template includes this <<[% include other %]>>text
151 TOP
152                 other   => <<OTHER,
153 This is the included text
154 OTHER
155 } ) ;
156
157 my $include_template = $ts->render( 'top', {} ) ;
158
159 print "INCLUDE TEMPLATE: [${$include_template}]\n" ;
160
161 =head1 Template Paths
162
163 You can search for templates in files with the C<search_dirs> option
164 to the constructor. If a named template is not found in the object
165 cache it will be searched for in the directories listed in the
166 C<search_dirs> option. If it is found there, it will be loaded into
167 the object cache so future uses of it by name will be faster. The
168 default value of C<search_dirs> option is C<templates>. Templates must
169 have a suffix of C<.tmpl>. This example makes a directory called
170 'templates' and a template file named C<foo.tmpl>. The second example
171 makes a directory called C<cookbook> and puts a template in there and
172 sets. Note that the option value must be an array reference.
173
174 =cut
175
176 print "\n******************\nSearch Dirs\n******************\n\n" ;
177
178 my $tmpl_dir = 'templates' ;
179 mkdir $tmpl_dir ;
180 write_file( "$tmpl_dir/foo.tmpl", <<FOO ) ;
181 This template was loaded from the dir [%dir%]
182 FOO
183
184 $ts = Template::Simple->new() ;
185 my $foo_file_template = $ts->render( 'foo', { dir => 'templates' } ) ;
186
187 print "FOO FILE TEMPLATE: [${$foo_file_template}]\n" ;
188
189 unlink "$tmpl_dir/foo.tmpl" ;
190 rmdir $tmpl_dir ;
191
192 ######
193
194 my $cook_dir = 'cookbook' ;
195 mkdir $cook_dir ;
196 write_file( "$cook_dir/bar.tmpl", <<BAR ) ;
197 This template was loaded from the $cook_dir [%dir%]
198 BAR
199
200 $ts = Template::Simple->new( search_dirs => [$cook_dir] ) ;
201 my $bar_file_template = $ts->render( 'bar', { dir => 'directory' } ) ;
202
203 print "BAR FILE TEMPLATE: [${$bar_file_template}]\n" ;
204
205 unlink "$cook_dir/bar.tmpl" ;
206 rmdir $cook_dir ;
207
208 =head1 Named Chunk Expansion
209
210 The core markup in T::S is called a chunk. It is delimited by paired
211 C<start> and C<end> markups and the text in between them is the
212 chunk. Any chunk can have multiple chunks inside it and they are named
213 for the name in the C<start> and C<end> markups. That name is used to
214 match the chunk with the data passed to render. This example uses the
215 top level template (which is always an unnamed chunk) which contains a
216 nested chunk which has a name. The data passed in is a hash reference
217 which has a key with the chunk name and its value is another hash
218 reference. So the nested chunk match up to the nested hashes.
219
220 =cut
221
222 print "\n******************\nNested Chunk Expansion\n******************\n\n" ;
223
224 $ts = Template::Simple->new(
225         templates       => {
226                 top     => <<TOP,
227 This top level template includes this <<[% include nested %]>> chunk
228 TOP
229                 nested  => <<NESTED,
230 [%START nested %]This included template just has a [% token %] and another [% one %][%END nested %]
231 NESTED
232         }
233 ) ;
234
235 my $nested_template = $ts->render( 'top',
236         {
237         nested => {
238                 token   => 'nested value',
239                 one     => 'value from the data',
240         }
241 } ) ;
242
243 print "NESTED TEMPLATE: [${$nested_template}]\n" ;
244
245 =head2 Boolean Chunk
246
247 The simplest template decision is when you want to show some text or
248 nothing.  This is done with an empty hash reference or a null string
249 value in the data tree. The empty hash reference will cause the text
250 to be kept as is with all markups removed (replaced by the null
251 string). A null string (or a reference to one) will cause the text
252 chunk to be deleted.
253
254 =cut
255
256 print "\n******************\nBoolean Text\n******************\n\n" ;
257
258 $template = \<<TMPL ;
259 [% START boolean %]This is text to be left or deleted[% END boolean %]
260 TMPL
261
262 my $boolean_kept = $ts->render( $template, { boolean => {} } ) ;
263 my $deleted = $ts->render( $template, { default => \'' } ) ;
264
265 print "KEPT: [${$boolean_kept}]\n" ;
266 print "DELETED: [${$deleted}]\n" ;
267
268 =head2 Default vs. Overwrite Text
269
270 The next step up from boolean text is overwriting a default text with
271 another when rendering. This is done with an empty hash reference or a
272 scalar value for the chunk in the data tree. The empty hash reference
273 will cause the default text to be kept as is with all markups removed
274 (replaced by the null string). A scalar value (or a scalar reference)
275 will cause the complete text chunk to be replaced by that value.
276
277 =cut
278
279 print "\n******************\nDefault vs. Overwrite Text\n******************\n\n" ;
280
281 $template = \<<TMPL ;
282 [% START default %]This is text to be left or replaced[% END default %]
283 TMPL
284
285 my $default_kept = $ts->render( $template, { default => {} } ) ;
286 my $overwrite = $ts->render( $template, { default => \<<OVER } ) ;
287 This text will overwrite the default text
288 OVER
289
290 print "DEFAULT: [${$default_kept}]\n" ;
291 print "OVERWRITE: [${$overwrite}]\n" ;
292
293 =head2 Conditional Text
294
295 Instead of having the overwrite text in the data tree, it is useful to
296 have it in the template itself. This is a conditional where one text
297 or the other is rendered. This is done by wrapping each text in its
298 own chunk with unique names. The data tree can show either one by
299 passing an empty hash reference for that data and a null string for
300 the other one. Also you can just not have a value for the text not to
301 be rendered and that will also delete it. Both styles are shown here.
302
303 =cut
304
305 print "\n******************\nConditional Text\n******************\n\n" ;
306
307 $template = \<<TMPL ;
308 [% START yes_text %]This text shown when yes[% END yes_text %]
309 [% START no_text %]This text shown when no[% END no_text %]
310 TMPL
311
312 my $yes_shown = $ts->render( $template, { yes_text => {} } ) ;
313 my $no_shown = $ts->render( $template, {
314         yes_text => '',
315         no_text => {}
316 } ) ;
317
318 print "YES: [${$yes_shown}]\n" ;
319 print "NO: [${$no_shown}]\n" ;
320
321 =head1 List Chunk Expansion
322
323 T::S has no list markup because of the unique way it handles data
324 during rendering. When an array reference is matched to a chunk, the
325 array is iterated and the chunk is then rendered with each element of
326 the array. This list of rendered texts is concatenated and replaces
327 the original chunk in the template. The data and the logic that
328 creates the data controls when a template chunk is repeated. This
329 example shows the top level (unnamed) template being rendered with an
330 array of hashes. Each hash renders the chunk one time. Note that the
331 different results you get based on the different hashes in the array.
332
333 =cut
334
335 print "\n******************\nList Chunk Expansion\n******************\n\n" ;
336
337 $ts = Template::Simple->new(
338         templates => {
339                 top_array => <<TOP_ARRAY,
340
341 This is the [%count%] chunk.
342 [%start maybe%]This line may be shown[%end maybe%]
343 This is the end of the chunk line
344 TOP_ARRAY
345 } ) ;
346
347 my $top_array = $ts->render( 'top_array', [
348         {
349                 count   => 'first',
350                 maybe   => {},
351         },
352         {
353                 count   => 'second',
354         },
355         {
356                 count   => 'third',
357                 maybe   => {},
358         },
359 ] ) ;
360
361 print "TOP_ARRAY: [${$top_array}]\n" ;
362
363
364 =head1 Separated List Expansion
365
366 A majorly used variation of data lists are list with a separator but
367 not one after the last element. This can be done easily with T::S and
368 here are two techniques. The first one uses a token for the separator
369 in the chunk and passes in a hash with the delimiter string set in all
370 but the last element. This requires the code logic to know and set the
371 delimiter. The other solution lets the template set the delimiter by
372 enclosing it in a chunk of its own and passing an empty hash ref for
373 the places to keep it and nothing for the last element. Both examples
374 use the same sub to do this work for you and all you need to do is
375 pass it the token for the main value and the seperator key and
376 optionally its value. You can easily make a variation that puts the
377 separator before the element and delete it from the first element.  If
378 your chunk has more tokens or nested chunks, this sub could be
379 generalized to modify a list of hashes instead of generating one.
380
381 =cut
382
383 print "\n******************\nSeparated List Expansion\n******************\n\n" ;
384
385
386 sub make_separated_data {
387         my( $token, $data, $delim_key, $delim ) = @_ ;
388
389 # make the delim set from the template (in a chunk) if not passed in
390 # an empty hash ref keeps the chunk text as is.
391
392         $delim ||= {} ;
393
394         my @list = map +{ $token => $_, $delim_key => $delim, }, @{$data} ;
395
396 # remove the separator from the last element
397
398         delete $list[-1]{$delim_key} ;
399
400         return \@list ;
401 }
402
403 my @data = qw( one two three four ) ;
404
405 $ts = Template::Simple->new(
406         templates       => {
407                 sep_tmpl        => <<SEP_TMPL,
408 Number [%count%][%sep%]
409 SEP_TMPL
410                 sep_data        => <<SEP_DATA,
411 Number [%count%][%start sep%],[%end sep%]
412 SEP_DATA
413 } ) ;
414
415 my $sep_tmpl = $ts->render( 'sep_tmpl',
416         make_separated_data( 'count', \@data, 'sep', '--' ) ) ;
417
418 my $sep_data = $ts->render( 'sep_data',
419         make_separated_data( 'count', \@data, 'sep', {} ) ) ;
420
421 print "SEP_DATA: [${$sep_data}]\n" ;
422 print "SEP_DATA: [${$sep_data}]\n" ;
423
424 exit ;