Commit | Line | Data |
3fea05b9 |
1 | #============================================================= -*-perl-*- |
2 | # |
3 | # Template::Manual::Views |
4 | # |
5 | # AUTHOR |
6 | # Andy Wardley <abw@wardley.org> |
7 | # |
8 | # COPYRIGHT |
9 | # Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved. |
10 | # |
11 | # This module is free software; you can redistribute it and/or |
12 | # modify it under the same terms as Perl itself. |
13 | # |
14 | #======================================================================== |
15 | |
16 | =head1 NAME |
17 | |
18 | Template::Manual::Views - Template Toolkit views (experimental) |
19 | |
20 | =head1 Overview |
21 | |
22 | A view is effectively a collection of templates and/or variable |
23 | definitions which can be passed around as a self-contained unit. This |
24 | then represents a particular interface or presentation style for other |
25 | objects or items of data. |
26 | |
27 | You can use views to implement custom "skins" for an application or |
28 | content set. You can use them to help simplify the presentation of |
29 | common objects or data types. You can even use then to automate the |
30 | presentation of complex data structures such as that generated in an |
31 | C<XML::DOM> tree or similar. You let an iterator do the walking, and the |
32 | view does the talking (or in this case, the presenting). Voila - you |
33 | have view independant, structure shy traversal using templates. |
34 | |
35 | In general, views can be used in a number of different ways to achieve |
36 | several different things. They elegantly solve some problems which |
37 | were otherwise difficult or complicated, and make easy some things |
38 | that were previously hard. |
39 | |
40 | At the moment, they're still very experimental. The directive syntax |
41 | and underlying API are likely to change quite considerably over the |
42 | next version or two. Please be very wary about building your |
43 | multi-million dollar e-commerce solutions based around this feature. |
44 | |
45 | =head1 Views as Template Collectors/Providers |
46 | |
47 | The C<VIEW> directive starts a view definition and includes a name by |
48 | which the view can be referenced. The view definition continues up to |
49 | the matching C<END> directive. |
50 | |
51 | [% VIEW myview %] |
52 | ... |
53 | [% END %] |
54 | |
55 | The first role of a view is to act as a collector and provider of templates. |
56 | The C<include()> method can be called on a view to effectively do the same |
57 | thing as the C<INCLUDE> directive. The template name is passed as the first |
58 | argument, followed by any local variable definitions for the template. |
59 | |
60 | [% myview.include('header', title='The Title') %] |
61 | |
62 | # equivalent to |
63 | [% INCLUDE header title='The Title' %] |
64 | |
65 | Views accept a number of configuration options which can be used to control |
66 | different aspects of their behaviour. The 'C<prefix>' and 'C<suffix>' options |
67 | can be specified to add a fixed prefix and/or suffix to the name of each template. |
68 | |
69 | [% VIEW myview |
70 | prefix = 'my/' |
71 | suffix = '.tt2' ; |
72 | END |
73 | %] |
74 | |
75 | Now the call |
76 | |
77 | [% myview.include('header', title='The Title') %] |
78 | |
79 | is equivalent to |
80 | |
81 | [% INCLUDE my/header.tt2 title='The Title' %] |
82 | |
83 | Views provide an C<AUTOLOAD> method which maps method names to the |
84 | C<include()> method. Thus, the following are all equivalent: |
85 | |
86 | [% myview.include('header', title='Hello World') %] |
87 | [% myview.include_header(title='Hello World') %] |
88 | [% myview.header(title='Hello World') %] |
89 | |
90 | =head1 Local BLOCK Definitions |
91 | |
92 | A C<VIEW> definition can include C<BLOCK> definitions which remain local to |
93 | the view. A request for a particular template will return a C<BLOCK>, |
94 | if defined, in preference to any other template of the same name. |
95 | |
96 | [% BLOCK foo %] |
97 | public foo block |
98 | [% END %] |
99 | |
100 | [% VIEW plain %] |
101 | [% BLOCK foo %] |
102 | plain foo block |
103 | [% END %] |
104 | [% END %] |
105 | |
106 | [% VIEW fancy %] |
107 | [% BLOCK foo %] |
108 | fancy foo block |
109 | [% END %] |
110 | [% END %] |
111 | |
112 | [% INCLUDE foo %] # public foo block |
113 | [% plain.foo %] # plain foo block |
114 | [% fancy.foo %] # fancy foo block |
115 | |
116 | In addition to C<BLOCK> definitions, a C<VIEW> can contain any other |
117 | template directives. The entire C<VIEW> definition block is processed to |
118 | initialise the view but no output is generated (this may change RSN - |
119 | and get stored as 'C<output>' item, subsequently accessible as C<[% |
120 | view.output %]>). However, directives that have side-effects, such as |
121 | those that update a variable, will have noticable consequences. |
122 | |
123 | =head1 Preserving Variable State within Views |
124 | |
125 | Views can also be used to save the values of any existing variables, |
126 | or to create new ones at the point at which the view is defined. |
127 | Unlike simple template metadata (C<META>) which can only contain static |
128 | string values, the view initialisation block can contain any template |
129 | directives and generate any kind of dynamic output and/or data items. |
130 | |
131 | [% VIEW my_web_site %] |
132 | [% view.title = title or 'My Cool Web Site' %] |
133 | [% view.author = "$abw.name, $abw.email" %] |
134 | [% view.sidebar = INCLUDE my/sidebar.tt2 %] |
135 | [% END %] |
136 | |
137 | Note that additional data items can be specified as arguments to the C<VIEW> |
138 | directive. Anything that doesn't look like a configuration parameter is |
139 | assumed to be a data item. This can be a little hazardous, of course, because |
140 | you never know when a new configuration item might get added which interferes |
141 | with your data. |
142 | |
143 | [% VIEW my_web_site |
144 | # config options |
145 | prefix = 'my/' |
146 | # misc data |
147 | title = title or 'My Cool Web Site' |
148 | author = "$abw.name, $abw.email" |
149 | sidebar = INCLUDE my/sidebar.tt2 |
150 | %] |
151 | ... |
152 | [% END %] |
153 | |
154 | Outside of the view definition you can access the view variables as, for |
155 | example: |
156 | |
157 | [% my_web_site.title %] |
158 | |
159 | One important feature is the equivalence of simple variables and templates. |
160 | You can implement the view item 'C<title>' as a simple variable, a template |
161 | defined in an external file, possibly with a prefix/suffix automatically |
162 | appended, or as a local C<BLOCK> definition within the C<[% VIEW %] ... [% END %]> |
163 | definition. If you use the syntax above then the view will Do The Right |
164 | Thing to return the appropriate output. |
165 | |
166 | At the C<END> of the C<VIEW> definition the view is "sealed" to prevent you |
167 | from accidentally updating any variable values. If you attempt to change |
168 | the value of a variable after the C<END> of the C<VIEW> definition block then |
169 | a C<view> error will be thrown. |
170 | |
171 | [% TRY; |
172 | my_web_site.title = 'New Title'; |
173 | CATCH; |
174 | error; |
175 | END |
176 | %] |
177 | |
178 | The error above will be reported as: |
179 | |
180 | view error - cannot update item in sealed view: title |
181 | |
182 | The same is true if you pass a parameter to a view variable. This is |
183 | interpreted as an attempt to update the variable and will raise the same |
184 | warning. |
185 | |
186 | [% my_web_site.title('New Title') %] # view error! |
187 | |
188 | You can set the C<silent> parameter to have the view ignore these |
189 | parameters and simply return the variable value. |
190 | |
191 | [% VIEW my_web_site |
192 | silent = 1 |
193 | title = title or 'My Cool Web Site' |
194 | # ... ; |
195 | END |
196 | %] |
197 | |
198 | [% my_web_site.title('Blah Blah') %] # My Cool Web Site |
199 | |
200 | Alternately, you can specify that a view is unsealed allowing existing |
201 | variables to be updated and new variables defined. |
202 | |
203 | [% VIEW my_web_site |
204 | sealed = 0 |
205 | title = title or 'My Cool Web Site' |
206 | # ... ; |
207 | END |
208 | %] |
209 | |
210 | [% my_web_site.title('Blah Blah') %] # Blah Blah |
211 | [% my_web_site.title %] # Blah Blah |
212 | |
213 | =head2 Inheritance, Delegation and Reuse |
214 | |
215 | Views can be inherited from previously defined views by use of the C<base> |
216 | parameter. This example shows how a base class view is defined which |
217 | applies a C<view/default/> prefix to all template names. |
218 | |
219 | [% VIEW my.view.default |
220 | prefix = 'view/default/'; |
221 | END |
222 | %] |
223 | |
224 | Thus the directive: |
225 | |
226 | [% my.view.default.header(title='Hello World') %] |
227 | |
228 | is now equivalent to: |
229 | |
230 | [% INCLUDE view/default/header title='Hello World' %] |
231 | |
232 | A second view can be defined which specifies the default view as a |
233 | base. |
234 | |
235 | [% VIEW my.view.fancy |
236 | base = my.view.default |
237 | prefix = 'view/fancy/'; |
238 | END |
239 | %] |
240 | |
241 | Now the directive: |
242 | |
243 | [% my.view.fancy.header(title='Hello World') %] |
244 | |
245 | will resolve to: |
246 | |
247 | [% INCLUDE view/fancy/header title='Hello World' %] |
248 | |
249 | or if that doesn't exist, it will be handled by the base view as: |
250 | |
251 | [% INCLUDE view/default/header title='Hello World' %] |
252 | |
253 | When a parent view is specified via the C<base> parameter, the |
254 | delegation of a view to its parent for fetching templates and accessing |
255 | user defined variables is automatic. You can also implement your own |
256 | inheritance, delegation or other reuse patterns by explicitly |
257 | delegating to other views. |
258 | |
259 | [% BLOCK foo %] |
260 | public foo block |
261 | [% END %] |
262 | |
263 | [% VIEW plain %] |
264 | [% BLOCK foo %] |
265 | <plain>[% PROCESS foo %]</plain> |
266 | [% END %] |
267 | [% END %] |
268 | |
269 | [% VIEW fancy %] |
270 | [% BLOCK foo %] |
271 | [% plain.foo | replace('plain', 'fancy') %] |
272 | [% END %] |
273 | [% END %] |
274 | |
275 | [% plain.foo %] # <plain>public foo block</plain> |
276 | [% fancy.foo %] # <fancy>public foo block</fancy> |
277 | |
278 | Note that the regular C<INCLUDE/PROCESS/WRAPPER> directives work entirely |
279 | independantly of views and will always get the original, unaltered |
280 | template name rather than any local per-view definition. |
281 | |
282 | =head2 Self-Reference |
283 | |
284 | A reference to the view object under definition is available with the |
285 | C<VIEW ... END> block by its specified name and also by the special name |
286 | 'C<view>' (similar to the C<my $self = shift;> in a Perl method or the |
287 | 'C<this>' pointer in C++, etc). The view is initially unsealed allowing |
288 | any data items to be defined and updated within the C<VIEW ... END> |
289 | block. The view is automatically sealed at the end of the definition |
290 | block, preventing any view data from being subsequently changed. |
291 | |
292 | (NOTE: sealing should be optional. As well as sealing a view to prevent |
293 | updates (C<SEALED>), it should be possible to set an option in the view to |
294 | allow external contexts to update existing variables (C<UPDATE>) or even |
295 | create totally new view variables (C<CREATE>)). |
296 | |
297 | [% VIEW fancy %] |
298 | [% fancy.title = 'My Fancy Title' %] |
299 | [% fancy.author = 'Frank Open' %] |
300 | [% fancy.col = { bg => '#ffffff', bar => '#a0a0ff' } %] |
301 | [% END %] |
302 | |
303 | or |
304 | |
305 | [% VIEW fancy %] |
306 | [% view.title = 'My Fancy Title' %] |
307 | [% view.author = 'Frank Open' %] |
308 | [% view.col = { bg => '#ffffff', bar => '#a0a0ff' } %] |
309 | [% END %] |
310 | |
311 | It makes no real difference in this case if you refer to the view by |
312 | its name, 'C<fancy>', or by the general name, 'C<view>'. Outside of the |
313 | view block, however, you should always use the given name, 'C<fancy>': |
314 | |
315 | [% fancy.title %] |
316 | [% fancy.author %] |
317 | [% fancy.col.bg %] |
318 | |
319 | The choice of given name or 'C<view>' is much more important when it |
320 | comes to C<BLOCK> definitions within a C<VIEW>. It is generally recommended |
321 | that you use 'C<view>' inside a C<VIEW> definition because this is guaranteed |
322 | to be correctly defined at any point in the future when the block gets |
323 | called. The original name of the view might have long since been changed |
324 | or reused but the self-reference via 'C<view>' should always be intact and |
325 | valid. |
326 | |
327 | Take the following VIEW as an example: |
328 | |
329 | [% VIEW foo %] |
330 | [% view.title = 'Hello World' %] |
331 | [% BLOCK header %] |
332 | Title: [% view.title %] |
333 | [% END %] |
334 | [% END %] |
335 | |
336 | Even if we rename the view, or create a new C<foo> variable, the header |
337 | block still correctly accesses the C<title> attribute of the view to |
338 | which it belongs. Whenever a view C<BLOCK> is processed, the C<view> |
339 | variable is always updated to contain the correct reference to the |
340 | view object to which it belongs. |
341 | |
342 | [% bar = foo %] |
343 | [% foo = { title => "New Foo" } %] # no problem |
344 | [% bar.header %] # => Title: Hello World |
345 | |
346 | =head2 Saving References to External Views |
347 | |
348 | When it comes to view inheritance, it's always a good idea to take a |
349 | local copy of a parent or delegate view and store it as an attribute |
350 | within the view for later use. This ensures that the correct view |
351 | reference is always available, even if the external name of a view |
352 | has been changed. |
353 | |
354 | [% VIEW plain %] |
355 | ... |
356 | [% END %] |
357 | |
358 | [% VIEW fancy %] |
359 | [% view.plain = plain %] |
360 | [% BLOCK foo %] |
361 | [% view.plain.foo | replace('plain', 'fancy') %] |
362 | [% END %] |
363 | [% END %] |
364 | |
365 | [% plain.foo %] # => <plain>public foo block</plain> |
366 | [% plain = 'blah' %] # no problem |
367 | [% fancy.foo %] # => <fancy>public foo block</fancy> |
368 | |
369 | =head2 Views as Data Presenters |
370 | |
371 | Another key role of a view is to act as a dispatcher to automatically |
372 | apply the correct template to present a particular object or data |
373 | item. This is handled via the C<print()> method. |
374 | |
375 | Here's an example: |
376 | |
377 | [% VIEW foo %] |
378 | |
379 | [% BLOCK text %] |
380 | Some text: [% item %] |
381 | [% END %] |
382 | |
383 | [% BLOCK hash %] |
384 | a hash: |
385 | [% FOREACH key = item.keys.sort -%] |
386 | [% key %] => [% item.$key %] |
387 | [% END -%] |
388 | [% END %] |
389 | |
390 | [% BLOCK list %] |
391 | a list: [% item.sort.join(', ') %] |
392 | [% END %] |
393 | |
394 | [% END %] |
395 | |
396 | We can now use the view to print text, hashes or lists. The C<print()> |
397 | method includes the right template depending on the typing of the |
398 | argument (or arguments) passed. |
399 | |
400 | [% some_text = 'I read the news today, oh boy.' %] |
401 | [% a_hash = { house => 'Lords', hall => 'Albert' } %] |
402 | [% a_list = [ 'sure', 'Nobody', 'really' ] %] |
403 | |
404 | [% view.print(some_text) %] |
405 | # Some text: I read the news today, oh boy. |
406 | |
407 | [% view.print(a_hash) %] |
408 | # a hash: |
409 | hall => Albert |
410 | house => Lords |
411 | [% view.print(a_list) %] |
412 | # a list: Nobody, really, sure |
413 | |
414 | You can also provide templates to print objects of any other class. |
415 | The class name is mapped to a template name with all non-word |
416 | character sequences such as 'C<::>' converted to a single 'C<_>'. |
417 | |
418 | [% VIEW foo %] |
419 | [% BLOCK Foo_Bar %] |
420 | a Foo::Bar object: |
421 | thingies: [% view.print(item.thingies) %] |
422 | doodahs: [% view.print(item.doodahs) %] |
423 | [% END %] |
424 | [% END %] |
425 | |
426 | [% USE fubar = Foo::Bar(...) %] |
427 | |
428 | [% foo.print(fubar) %] |
429 | |
430 | Note how we use the view object to display various items within the |
431 | objects ('C<thingies>' and 'C<doodahs>'). We don't need to worry what |
432 | kind of data these represent (text, list, hash, etc) because we can |
433 | let the view worry about it, automatically mapping the data type to |
434 | the correct template. |
435 | |
436 | Views may define their own type =E<gt> template map. |
437 | |
438 | [% VIEW foo |
439 | map = { TEXT => 'plain_text', |
440 | ARRAY => 'show_list', |
441 | HASH => 'show_hash', |
442 | My::Module => 'template_name' |
443 | default => 'any_old_data' |
444 | } |
445 | %] |
446 | [% BLOCK plain_text %] |
447 | ... |
448 | [% END %] |
449 | |
450 | ... |
451 | [% END %] |
452 | |
453 | They can also provide a C<default> map entry, specified as part of the C<map> |
454 | hash or as a parameter by itself. |
455 | |
456 | [% VIEW foo |
457 | map = { ... }, |
458 | default = 'whatever' |
459 | %] |
460 | ... |
461 | [% END %] |
462 | |
463 | or |
464 | |
465 | [% VIEW foo %] |
466 | [% view.map = { ... } |
467 | view.default = 'whatever' |
468 | %] |
469 | ... |
470 | [% END %] |
471 | |
472 | The C<print()> method provides one more piece of magic. If you pass it a |
473 | reference to an object which provides a C<present()> method, then the method |
474 | will be called passing the view as an argument. This then gives any object a |
475 | chance to determine how it should be presented via the view. |
476 | |
477 | package Foo::Bar; |
478 | ... |
479 | sub present { |
480 | my ($self, $view) = @_; |
481 | return "a Foo::Bar object:\n" |
482 | . "thingies: " . $view->print($self->{ _THINGIES }) . "\n" |
483 | . "doodahs: " . $view->print($self->{ _DOODAHS }) . "\n"; |
484 | } |
485 | |
486 | The object is free to delve deeply into its innards and mess around with |
487 | its own private data, before presenting the relevant data via the view. |
488 | In a more complex example, a C<present()> method might walk part of a tree |
489 | making calls back against the view to present different nodes within the |
490 | tree. We may not want to expose the internal structure of the tree |
491 | (because that would break encapsulation and make our presentation code |
492 | dependant on it) but we want to have some way of walking the tree and |
493 | presenting items found in a particular manner. |
494 | |
495 | This is known as I<Structure Shy Traversal>. Our view object doesn't require |
496 | prior knowledge about the internal structure of any data set to be able |
497 | to traverse it and present the data contained therein. The data items |
498 | themselves, via the C<present()> method, can implement the internal iterators |
499 | to guide the view along the right path to presentation happiness. |
500 | |
501 | The upshot is that you can use views to greatly simplify the display |
502 | of data structures like C<XML::DOM> trees. The documentation for the |
503 | C<Template::Plugin::XML::DOM> module contains an example of this. In |
504 | essence, it looks something like this: |
505 | |
506 | XML source: |
507 | |
508 | <user name="Andy Wardley"> |
509 | <project id="iCan" title="iCan, but theyCan't"/> |
510 | <project id="p45" title="iDid, but theyDidn't"/> |
511 | </user> |
512 | |
513 | TT View: |
514 | |
515 | [% VIEW fancy %] |
516 | [% BLOCK user %] |
517 | User: [% item.name %] |
518 | [% item.content(myview) %] |
519 | [% END %] |
520 | |
521 | [% BLOCK project %] |
522 | Project: [% project.id %] - [% project.name %] |
523 | [% END %] |
524 | [% END %] |
525 | |
526 | Generate view: |
527 | |
528 | [% USE dom = XML.DOM %] |
529 | [% fancy.print(dom.parse(xml_source)) %] |
530 | |
531 | Output: |
532 | |
533 | User: Andy Wardley |
534 | Project: iCan - iCan, but theyCan't |
535 | Project: p45 - iDid, but theyDidn't |
536 | |
537 | The same approach can be applied to many other areas. Here's an example from |
538 | the C<File>/C<Directory> plugins. |
539 | |
540 | [% VIEW myview %] |
541 | [% BLOCK file %] |
542 | - [% item.name %] |
543 | [% END %] |
544 | |
545 | [% BLOCK directory %] |
546 | * [% item.name %] |
547 | [% item.content(myview) FILTER indent %] |
548 | [% END %] |
549 | [% END %] |
550 | |
551 | [% USE dir = Directory(dirpath) %] |
552 | [% myview.print(dir) %] |
553 | |
554 | And here's the same approach use to convert POD documentation to any |
555 | other format via template. |
556 | |
557 | [% # load Pod plugin and parse source file into Pod Object Model |
558 | USE Pod; |
559 | pom = Pod.parse_file(my_pod_file); |
560 | |
561 | # define view to map all Pod elements to "pod/html/xxx" templates |
562 | VIEW pod2html |
563 | prefix='pod/html'; |
564 | END; |
565 | |
566 | # now print document via view (i.e. as HTML) |
567 | pod2html.print(pom) |
568 | %] |
569 | |
570 | Here we simply define a template prefix for the view which causes the |
571 | view to look for C<pod/html/head1>, C<pod/html/head2>, C<pod/html/over> |
572 | as templates to present the different sections of the parsed Pod document. |
573 | |
574 | There are some examples in the Template Toolkit test suite: F<t/pod.t> and |
575 | F<t/view.t> which may shed some more light on this. See the distribution |
576 | sub-directory F<examples/pod/html> for examples of Pod -E<gt> HTML templates. |
577 | |
578 | =cut |
579 | |
580 | # Local Variables: |
581 | # mode: perl |
582 | # perl-indent-level: 4 |
583 | # indent-tabs-mode: nil |
584 | # End: |
585 | # |
586 | # vim: expandtab shiftwidth=4: |