Commit | Line | Data |
8077080c |
1 | package Catalyst::View::TT; |
2 | |
3 | use strict; |
7d8aa5ec |
4 | use base qw/Catalyst::View/; |
8077080c |
5 | use Template; |
6 | use Template::Timer; |
7 | use NEXT; |
8 | |
07571b2f |
9 | our $VERSION = '0.20'; |
8077080c |
10 | |
11 | __PACKAGE__->mk_accessors('template'); |
e2bbd784 |
12 | __PACKAGE__->mk_accessors('include_path'); |
8077080c |
13 | |
14 | =head1 NAME |
15 | |
16 | Catalyst::View::TT - Template View Class |
17 | |
18 | =head1 SYNOPSIS |
19 | |
8cd017a8 |
20 | # use the helper to create View |
722fede4 |
21 | myapp_create.pl view TT TT |
8077080c |
22 | |
8cd017a8 |
23 | # configure in lib/MyApp.pm |
24 | |
8cd017a8 |
25 | MyApp->config({ |
26 | name => 'MyApp', |
7d8aa5ec |
27 | root => MyApp->path_to('root');, |
7d607671 |
28 | 'V::TT' => { |
8cd017a8 |
29 | # any TT configurations items go here |
30 | INCLUDE_PATH => [ |
93f25382 |
31 | MyApp->path_to( 'root', 'src' ), |
32 | MyApp->path_to( 'root', 'lib' ), |
8cd017a8 |
33 | ], |
07571b2f |
34 | PRE_PROCESS => 'config/main', |
35 | WRAPPER => 'site/wrapper', |
36 | TEMPLATE_EXTENSION => '.tt', |
8cd017a8 |
37 | |
38 | # two optional config items |
39 | CATALYST_VAR => 'Catalyst', |
40 | TIMER => 1, |
41 | }, |
42 | }); |
43 | |
44 | # render view from lib/MyApp.pm or lib/MyApp::C::SomeController.pm |
45 | |
46 | sub message : Global { |
94b3529a |
47 | my ( $self, $c ) = @_; |
48 | $c->stash->{template} = 'message.tt2'; |
49 | $c->stash->{message} = 'Hello World!'; |
8cd017a8 |
50 | $c->forward('MyApp::V::TT'); |
51 | } |
8077080c |
52 | |
8cd017a8 |
53 | # access variables from template |
54 | |
55 | The message is: [% message %]. |
56 | |
57 | # example when CATALYST_VAR is set to 'Catalyst' |
58 | Context is [% Catalyst %] |
59 | The base is [% Catalyst.req.base %] |
60 | The name is [% Catalyst.config.name %] |
61 | |
62 | # example when CATALYST_VAR isn't set |
63 | Context is [% c %] |
64 | The base is [% base %] |
65 | The name is [% name %] |
66 | |
67 | =head1 DESCRIPTION |
68 | |
69 | This is the Catalyst view class for the L<Template Toolkit|Template>. |
70 | Your application should defined a view class which is a subclass of |
71 | this module. The easiest way to achieve this is using the |
72 | F<myapp_create.pl> script (where F<myapp> should be replaced with |
73 | whatever your application is called). This script is created as part |
74 | of the Catalyst setup. |
75 | |
76 | $ script/myapp_create.pl view TT TT |
77 | |
78 | This creates a MyApp::V::TT.pm module in the F<lib> directory (again, |
79 | replacing C<MyApp> with the name of your application) which looks |
80 | something like this: |
81 | |
82 | package FooBar::V::TT; |
83 | |
84 | use strict; |
85 | use base 'Catalyst::View::TT'; |
8077080c |
86 | |
87 | __PACKAGE__->config->{DEBUG} = 'all'; |
88 | |
8cd017a8 |
89 | Now you can modify your action handlers in the main application and/or |
90 | controllers to forward to your view class. You might choose to do this |
91 | in the end() method, for example, to automatically forward all actions |
92 | to the TT view class. |
722fede4 |
93 | |
8cd017a8 |
94 | # In MyApp or MyApp::Controller::SomeController |
8077080c |
95 | |
8cd017a8 |
96 | sub end : Private { |
94b3529a |
97 | my( $self, $c ) = @_; |
8cd017a8 |
98 | $c->forward('MyApp::V::TT'); |
99 | } |
8077080c |
100 | |
8cd017a8 |
101 | =head2 CONFIGURATION |
4687ac0d |
102 | |
8cd017a8 |
103 | There are a three different ways to configure your view class. The |
104 | first way is to call the C<config()> method in the view subclass. This |
105 | happens when the module is first loaded. |
8077080c |
106 | |
8cd017a8 |
107 | package MyApp::V::TT; |
108 | |
109 | use strict; |
110 | use base 'Catalyst::View::TT'; |
722fede4 |
111 | |
8cd017a8 |
112 | MyApp::V::TT->config({ |
7d8aa5ec |
113 | INCLUDE_PATH => [ |
114 | MyApp->path_to( 'root', 'templates', 'lib' ), |
115 | MyApp->path_to( 'root', 'templates', 'src' ), |
116 | ], |
8cd017a8 |
117 | PRE_PROCESS => 'config/main', |
118 | WRAPPER => 'site/wrapper', |
119 | }); |
120 | |
121 | The second way is to define a C<new()> method in your view subclass. |
122 | This performs the configuration when the view object is created, |
123 | shortly after being loaded. Remember to delegate to the base class |
124 | C<new()> method (via C<$self-E<gt>NEXT::new()> in the example below) after |
125 | performing any configuration. |
126 | |
127 | sub new { |
128 | my $self = shift; |
129 | $self->config({ |
7d8aa5ec |
130 | INCLUDE_PATH => [ |
131 | MyApp->path_to( 'root', 'templates', 'lib' ), |
132 | MyApp->path_to( 'root', 'templates', 'src' ), |
133 | ], |
8cd017a8 |
134 | PRE_PROCESS => 'config/main', |
135 | WRAPPER => 'site/wrapper', |
136 | }); |
137 | return $self->NEXT::new(@_); |
138 | } |
139 | |
94b3529a |
140 | The final, and perhaps most direct way, is to define a class |
8cd017a8 |
141 | item in your main application configuration, again by calling the |
94b3529a |
142 | uniquitous C<config()> method. The items in the class hash are |
8cd017a8 |
143 | added to those already defined by the above two methods. This happens |
144 | in the base class new() method (which is one reason why you must |
145 | remember to call it via C<NEXT> if you redefine the C<new()> method in a |
146 | subclass). |
147 | |
148 | package MyApp; |
149 | |
150 | use strict; |
151 | use Catalyst; |
152 | |
8cd017a8 |
153 | MyApp->config({ |
154 | name => 'MyApp', |
7d8aa5ec |
155 | root => MyApp->path_to('root'), |
7d607671 |
156 | 'V::TT' => { |
7d8aa5ec |
157 | INCLUDE_PATH => [ |
158 | MyApp->path_to( 'root', 'templates', 'lib' ), |
159 | MyApp->path_to( 'root', 'templates', 'src' ), |
160 | ], |
8cd017a8 |
161 | PRE_PROCESS => 'config/main', |
162 | WRAPPER => 'site/wrapper', |
163 | }, |
164 | }); |
165 | |
166 | Note that any configuration items defined by one of the earlier |
167 | methods will be overwritten by items of the same name provided by the |
168 | latter methods. |
169 | |
e2bbd784 |
170 | =head2 DYNAMIC INCLUDE_PATH |
171 | |
172 | It is sometimes needed to dynamically add additional paths to the |
173 | INCLUDE_PATH variable of the template object. This can be done by setting |
174 | 'additional_include_paths' on stash to a referrence to an array with |
175 | additional paths: |
176 | |
177 | $c->stash->{additional_template_paths} = [$c->config->{root} . '/test_include_path']; |
178 | |
8cd017a8 |
179 | =head2 RENDERING VIEWS |
180 | |
181 | The view plugin renders the template specified in the C<template> |
182 | item in the stash. |
183 | |
184 | sub message : Global { |
94b3529a |
185 | my ( $self, $c ) = @_; |
186 | $c->stash->{template} = 'message.tt2'; |
8cd017a8 |
187 | $c->forward('MyApp::V::TT'); |
188 | } |
722fede4 |
189 | |
94b3529a |
190 | If a class item isn't defined, then it instead uses the |
8cd017a8 |
191 | current match, as returned by C<$c-E<gt>match>. In the above |
192 | example, this would be C<message>. |
193 | |
194 | The items defined in the stash are passed to the Template Toolkit for |
195 | use as template variables. |
196 | |
197 | sub message : Global { |
198 | sub default : Private { |
94b3529a |
199 | my ( $self, $c ) = @_; |
200 | $c->stash->{template} = 'message.tt2'; |
201 | $c->stash->{message} = 'Hello World!'; |
8cd017a8 |
202 | $c->forward('MyApp::V::TT'); |
203 | } |
7b592fc7 |
204 | |
8cd017a8 |
205 | A number of other template variables are also added: |
8077080c |
206 | |
8cd017a8 |
207 | c A reference to the context object, $c |
208 | base The URL base, from $c->req->base() |
209 | name The application name, from $c->config->{ name } |
210 | |
211 | These can be accessed from the template in the usual way: |
212 | |
213 | <message.tt2>: |
214 | |
215 | The message is: [% message %] |
216 | The base is [% base %] |
217 | The name is [% name %] |
218 | |
8cd017a8 |
219 | |
220 | The output generated by the template is stored in |
221 | C<$c-E<gt>response-E<gt>output>. |
222 | |
223 | =head2 TEMPLATE PROFILING |
224 | |
caa61517 |
225 | =head2 METHODS |
8077080c |
226 | |
2774dc77 |
227 | =over 4 |
228 | |
229 | =item new |
230 | |
231 | The constructor for the TT view. Sets up the template provider, |
232 | and reads the application config. |
233 | |
8077080c |
234 | =cut |
235 | |
83056300 |
236 | sub _coerce_paths { |
e2bbd784 |
237 | my ( $paths, $dlim ) = shift; |
bec996dc |
238 | return () if ( !$paths ); |
239 | return @{$paths} if ( ref $paths eq 'ARRAY' ); |
9300af5b |
240 | |
bec996dc |
241 | # tweak delim to ignore C:/ |
242 | unless ( defined $dlim ) { |
243 | $dlim = ( $^O eq 'MSWin32' ) ? ':(?!\\/)' : ':'; |
244 | } |
245 | return split( /$dlim/, $paths ); |
246 | } |
9300af5b |
247 | |
e2bbd784 |
248 | sub new { |
249 | my ( $class, $c, $arguments ) = @_; |
8d116b20 |
250 | my $config = { |
251 | EVAL_PERL => 0, |
252 | TEMPLATE_EXTENSION => '', |
253 | %{ $class->config }, |
254 | %{$arguments}, |
255 | }; |
256 | if ( ! (ref $config->{INCLUDE_PATH} eq 'ARRAY') ) { |
257 | my $delim = $config->{DELIMITER}; |
bec996dc |
258 | my @include_path |
8d116b20 |
259 | = _coerce_paths( $config->{INCLUDE_PATH}, $delim ); |
bec996dc |
260 | if ( !@include_path ) { |
3a4ffa2a |
261 | my $root = $c->config->{root}; |
bec996dc |
262 | my $base = Path::Class::dir( $root, 'base' ); |
3a4ffa2a |
263 | @include_path = ( "$root", "$base" ); |
264 | } |
8d116b20 |
265 | $config->{INCLUDE_PATH} = \@include_path; |
e2bbd784 |
266 | } |
8d116b20 |
267 | |
268 | |
62728755 |
269 | |
8cd017a8 |
270 | # if we're debugging and/or the TIMER option is set, then we install |
271 | # Template::Timer as a custom CONTEXT object, but only if we haven't |
272 | # already got a custom CONTEXT defined |
273 | |
dccfa0ff |
274 | if ( $config->{TIMER} ) { |
94b3529a |
275 | if ( $config->{CONTEXT} ) { |
8cd017a8 |
276 | $c->log->error( |
bec996dc |
277 | 'Cannot use Template::Timer - a TT CONFIG is already defined' |
278 | ); |
8cd017a8 |
279 | } |
280 | else { |
94b3529a |
281 | $config->{CONTEXT} = Template::Timer->new(%$config); |
8cd017a8 |
282 | } |
283 | } |
284 | |
94b3529a |
285 | if ( $c->debug && $config->{DUMP_CONFIG} ) { |
8cd017a8 |
286 | use Data::Dumper; |
94b3529a |
287 | $c->log->debug( "TT Config: ", Dumper($config) ); |
62728755 |
288 | } |
289 | |
d7e9a1b2 |
290 | if ( $config->{PROVIDERS} ) { |
291 | my @providers = (); |
292 | if ( ref($config->{PROVIDERS}) eq 'ARRAY') { |
293 | foreach my $p (@{$config->{PROVIDERS}}) { |
294 | my $pname = $p->{name}; |
295 | eval "require Template::Provider::$pname"; |
296 | if(!$@) { |
297 | push @providers, "Template::Provider::${pname}"->new($p->{args}); |
298 | } |
299 | } |
300 | } |
301 | delete $config->{PROVIDERS}; |
302 | if(@providers) { |
303 | $config->{LOAD_TEMPLATES} = \@providers; |
304 | } |
305 | } |
306 | |
e3a53d5b |
307 | my $self = $class->NEXT::new( |
8cd017a8 |
308 | $c, |
bec996dc |
309 | { template => Template->new($config) || do { |
8cd017a8 |
310 | my $error = Template->error(); |
311 | $c->log->error($error); |
312 | $c->error($error); |
313 | return undef; |
bec996dc |
314 | }, |
315 | %{$config}, |
48d69085 |
316 | }, |
8cd017a8 |
317 | ); |
8d116b20 |
318 | $self->include_path($config->{INCLUDE_PATH}); |
e3a53d5b |
319 | $self->config($config); |
320 | |
321 | return $self; |
8077080c |
322 | } |
323 | |
2774dc77 |
324 | =item process |
8077080c |
325 | |
722fede4 |
326 | Renders the template specified in C<$c-E<gt>stash-E<gt>{template}> or |
327 | C<$c-E<gt>request-E<gt>match>. Template variables are set up from the |
328 | contents of C<$c-E<gt>stash>, augmented with C<base> set to |
329 | C<$c-E<gt>req-E<gt>base>, C<c> to C<$c> and C<name> to |
8cd017a8 |
330 | C<$c-E<gt>config-E<gt>{name}>. Alternately, the C<CATALYST_VAR> |
331 | configuration item can be defined to specify the name of a template |
332 | variable through which the context reference (C<$c>) can be accessed. |
333 | In this case, the C<c>, C<base> and C<name> variables are omitted. |
334 | Output is stored in C<$c-E<gt>response-E<gt>output>. |
8077080c |
335 | |
336 | =cut |
337 | |
338 | sub process { |
339 | my ( $self, $c ) = @_; |
23042c3c |
340 | |
7d8aa5ec |
341 | my $template = $c->stash->{template} |
d7e9a1b2 |
342 | || ( $c->request->match || $c->request->action ) |
343 | . $self->config->{TEMPLATE_EXTENSION}; |
23042c3c |
344 | |
345 | unless ($template) { |
8077080c |
346 | $c->log->debug('No template specified for rendering') if $c->debug; |
347 | return 0; |
348 | } |
23042c3c |
349 | |
350 | $c->log->debug(qq/Rendering template "$template"/) if $c->debug; |
23042c3c |
351 | |
8cd017a8 |
352 | my $output; |
d7e9a1b2 |
353 | my $cvar = $self->config->{CATALYST_VAR}; |
354 | my $vars = { |
355 | defined $cvar |
356 | ? ( $cvar => $c ) |
357 | : ( c => $c, |
358 | base => $c->req->base, |
359 | name => $c->config->{name} |
360 | ), |
361 | %{ $c->stash() } |
362 | }; |
bec996dc |
363 | unshift @{ $self->include_path }, |
d7e9a1b2 |
364 | @{ $c->stash->{additional_template_paths} } |
365 | if ref $c->stash->{additional_template_paths}; |
8cd017a8 |
366 | unless ( $self->template->process( $template, $vars, \$output ) ) { |
8077080c |
367 | my $error = $self->template->error; |
368 | $error = qq/Couldn't render template "$error"/; |
369 | $c->log->error($error); |
becb7ac2 |
370 | $c->error($error); |
23042c3c |
371 | return 0; |
372 | } |
bec996dc |
373 | splice @{ $self->include_path }, 0, |
d7e9a1b2 |
374 | scalar @{ $c->stash->{additional_template_paths} } |
375 | if ref $c->stash->{additional_template_paths}; |
bec996dc |
376 | |
23042c3c |
377 | unless ( $c->response->content_type ) { |
378 | $c->response->content_type('text/html; charset=utf-8'); |
8077080c |
379 | } |
23042c3c |
380 | |
381 | $c->response->body($output); |
382 | |
8077080c |
383 | return 1; |
384 | } |
385 | |
2774dc77 |
386 | =item config |
8077080c |
387 | |
8cd017a8 |
388 | This method allows your view subclass to pass additional settings to |
4729c102 |
389 | the TT configuration hash, or to set the options as below: |
390 | |
391 | =over 2 |
392 | |
393 | =item C<CATALYST_VAR> |
394 | |
395 | Allows you to change the name of the Catalyst context object. If set, it will also |
396 | remove the base and name aliases, so you will have access them through <context>. |
397 | |
398 | For example: |
399 | |
400 | MyApp->config({ |
401 | name => 'MyApp', |
7d8aa5ec |
402 | root => MyApp->path_to('root'), |
4729c102 |
403 | 'V::TT' => { |
404 | CATALYST_VAR => 'Catalyst', |
405 | }, |
406 | }); |
407 | |
408 | F<message.tt2>: |
409 | |
410 | The base is [% Catalyst.req.base %] |
411 | The name is [% Catalyst.config.name %] |
412 | |
413 | =item C<TIMER> |
414 | |
415 | If you have configured Catalyst for debug output, and turned on the TIMER setting, |
416 | C<Catalyst::View::TT> will enable profiling of template processing |
417 | (using L<Template::Timer>). This will embed HTML comments in the |
418 | output from your templates, such as: |
419 | |
420 | <!-- TIMER START: process mainmenu/mainmenu.ttml --> |
421 | <!-- TIMER START: include mainmenu/cssindex.tt --> |
422 | <!-- TIMER START: process mainmenu/cssindex.tt --> |
423 | <!-- TIMER END: process mainmenu/cssindex.tt (0.017279 seconds) --> |
424 | <!-- TIMER END: include mainmenu/cssindex.tt (0.017401 seconds) --> |
425 | |
426 | .... |
427 | |
428 | <!-- TIMER END: process mainmenu/footer.tt (0.003016 seconds) --> |
429 | |
430 | |
be7c1afa |
431 | =item C<TEMPLATE_EXTENSION> |
4729c102 |
432 | |
747f2b6d |
433 | a sufix to add when looking for templates bases on the C<match> method in L<Catalyst::Request>. |
4729c102 |
434 | |
435 | For example: |
436 | |
437 | package MyApp::C::Test; |
438 | sub test : Local { .. } |
439 | |
440 | Would by default look for a template in <root>/test/test. If you set TEMPLATE_EXTENSION to '.tt', it will look for |
441 | <root>/test/test.tt. |
442 | |
443 | =back |
8077080c |
444 | |
2774dc77 |
445 | =back |
8077080c |
446 | |
8cd017a8 |
447 | =head2 HELPERS |
448 | |
449 | The L<Catalyst::Helper::View::TT> and |
450 | L<Catalyst::Helper::View::TTSite> helper modules are provided to create |
451 | your view module. There are invoked by the F<myapp_create.pl> script: |
452 | |
453 | $ script/myapp_create.pl view TT TT |
454 | |
455 | $ script/myapp_create.pl view TT TTSite |
456 | |
457 | The L<Catalyst::Helper::View::TT> module creates a basic TT view |
458 | module. The L<Catalyst::Helper::View::TTSite> module goes a little |
459 | further. It also creates a default set of templates to get you |
460 | started. It also configures the view module to locate the templates |
461 | automatically. |
462 | |
8077080c |
463 | =head1 SEE ALSO |
464 | |
8cd017a8 |
465 | L<Catalyst>, L<Catalyst::Helper::View::TT>, |
466 | L<Catalyst::Helper::View::TTSite>, L<Template::Manual> |
8077080c |
467 | |
c0eb0527 |
468 | =head1 AUTHORS |
8077080c |
469 | |
470 | Sebastian Riedel, C<sri@cpan.org> |
c0eb0527 |
471 | |
d938377b |
472 | Marcus Ramberg, C<mramberg@cpan.org> |
c0eb0527 |
473 | |
722fede4 |
474 | Jesse Sheidlower, C<jester@panix.com> |
c0eb0527 |
475 | |
8cd017a8 |
476 | Andy Wardley, C<abw@cpan.org> |
8077080c |
477 | |
478 | =head1 COPYRIGHT |
479 | |
2774dc77 |
480 | This program is free software, you can redistribute it and/or modify it |
481 | under the same terms as Perl itself. |
8077080c |
482 | |
483 | =cut |
484 | |
485 | 1; |