Commit | Line | Data |
8077080c |
1 | package Catalyst::View::TT; |
2 | |
3 | use strict; |
caa61517 |
4 | use base qw/Catalyst::Base/; |
8077080c |
5 | use Template; |
6 | use Template::Timer; |
7 | use NEXT; |
8 | |
9300af5b |
9 | our $VERSION = '0.13'; |
8077080c |
10 | |
11 | __PACKAGE__->mk_accessors('template'); |
8077080c |
12 | |
13 | =head1 NAME |
14 | |
15 | Catalyst::View::TT - Template View Class |
16 | |
17 | =head1 SYNOPSIS |
18 | |
8cd017a8 |
19 | # use the helper to create View |
722fede4 |
20 | myapp_create.pl view TT TT |
8077080c |
21 | |
8cd017a8 |
22 | # configure in lib/MyApp.pm |
23 | |
24 | our $ROOT = '/home/dent/catalyst/MyApp'; |
25 | |
26 | MyApp->config({ |
27 | name => 'MyApp', |
28 | root => $ROOT, |
29 | 'MyApp::V::TT' => { |
30 | # any TT configurations items go here |
31 | INCLUDE_PATH => [ |
32 | "$ROOT/templates/src", |
33 | "$ROOT/templates/lib" |
34 | ], |
35 | PRE_PROCESS => 'config/main', |
36 | WRAPPER => 'site/wrapper', |
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 { |
47 | my ($self, $c) = @_; |
48 | $c->stash->{ template } = 'message.tt2'; |
49 | $c->stash->{ message } = 'Hello World!'; |
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 { |
97 | my($self, $c) = @_; |
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 | our $ROOT = '/home/dent/catalyst/MyApp'; |
113 | |
114 | MyApp::V::TT->config({ |
115 | INCLUDE_PATH => ["$ROOT/templates/src", "$ROOT/templates/lib"], |
116 | PRE_PROCESS => 'config/main', |
117 | WRAPPER => 'site/wrapper', |
118 | }); |
119 | |
120 | The second way is to define a C<new()> method in your view subclass. |
121 | This performs the configuration when the view object is created, |
122 | shortly after being loaded. Remember to delegate to the base class |
123 | C<new()> method (via C<$self-E<gt>NEXT::new()> in the example below) after |
124 | performing any configuration. |
125 | |
126 | sub new { |
127 | my $self = shift; |
128 | $self->config({ |
129 | INCLUDE_PATH => ["$ROOT/templates/src", "$ROOT/templates/lib"], |
130 | PRE_PROCESS => 'config/main', |
131 | WRAPPER => 'site/wrapper', |
132 | }); |
133 | return $self->NEXT::new(@_); |
134 | } |
135 | |
136 | The final, and perhaps most direct way, is to define a C<template> |
137 | item in your main application configuration, again by calling the |
138 | uniquitous C<config()> method. The items in the C<template> hash are |
139 | added to those already defined by the above two methods. This happens |
140 | in the base class new() method (which is one reason why you must |
141 | remember to call it via C<NEXT> if you redefine the C<new()> method in a |
142 | subclass). |
143 | |
144 | package MyApp; |
145 | |
146 | use strict; |
147 | use Catalyst; |
148 | |
149 | our $ROOT = '/home/dent/catalyst/MyApp'; |
150 | |
151 | MyApp->config({ |
152 | name => 'MyApp', |
153 | root => $ROOT, |
154 | 'MyApp::V::TT' => { |
155 | INCLUDE_PATH => ["$ROOT/templates/src", "$ROOT/templates/lib"], |
156 | PRE_PROCESS => 'config/main', |
157 | WRAPPER => 'site/wrapper', |
158 | }, |
159 | }); |
160 | |
161 | Note that any configuration items defined by one of the earlier |
162 | methods will be overwritten by items of the same name provided by the |
163 | latter methods. |
164 | |
165 | =head2 RENDERING VIEWS |
166 | |
167 | The view plugin renders the template specified in the C<template> |
168 | item in the stash. |
169 | |
170 | sub message : Global { |
171 | my ($self, $c) = @_; |
172 | $c->stash->{ template } = 'message.tt2'; |
173 | $c->forward('MyApp::V::TT'); |
174 | } |
722fede4 |
175 | |
8cd017a8 |
176 | If a C<template> item isn't defined, then it instead uses the |
177 | current match, as returned by C<$c-E<gt>match>. In the above |
178 | example, this would be C<message>. |
179 | |
180 | The items defined in the stash are passed to the Template Toolkit for |
181 | use as template variables. |
182 | |
183 | sub message : Global { |
184 | sub default : Private { |
185 | my ($self, $c) = @_; |
186 | $c->stash->{ template } = 'message.tt2'; |
187 | $c->stash->{ message } = 'Hello World!'; |
188 | $c->forward('MyApp::V::TT'); |
189 | } |
7b592fc7 |
190 | |
8cd017a8 |
191 | A number of other template variables are also added: |
8077080c |
192 | |
8cd017a8 |
193 | c A reference to the context object, $c |
194 | base The URL base, from $c->req->base() |
195 | name The application name, from $c->config->{ name } |
196 | |
197 | These can be accessed from the template in the usual way: |
198 | |
199 | <message.tt2>: |
200 | |
201 | The message is: [% message %] |
202 | The base is [% base %] |
203 | The name is [% name %] |
204 | |
205 | If you prefer, you can set the C<CATALYST_VAR> configuration item to |
206 | define the name of a template variable through which the context can |
207 | be referenced. |
208 | |
209 | MyApp->config({ |
210 | name => 'MyApp', |
211 | root => $ROOT, |
212 | 'MyApp::V::TT' => { |
213 | CATALYST_VAR => 'Catalyst', |
214 | }, |
215 | }); |
216 | |
217 | F<message.tt2>: |
218 | |
219 | The base is [% Catalyst.req.base %] |
220 | The name is [% Catalyst.config.name %] |
221 | |
222 | The output generated by the template is stored in |
223 | C<$c-E<gt>response-E<gt>output>. |
224 | |
225 | =head2 TEMPLATE PROFILING |
226 | |
227 | If you have configured Catalyst for debug output, |
228 | C<Catalyst::View::TT> will enable profiling of template processing |
229 | (using L<Template::Timer>). This will embed HTML comments in the |
230 | output from your templates, such as: |
4687ac0d |
231 | |
232 | <!-- TIMER START: process mainmenu/mainmenu.ttml --> |
233 | <!-- TIMER START: include mainmenu/cssindex.tt --> |
234 | <!-- TIMER START: process mainmenu/cssindex.tt --> |
235 | <!-- TIMER END: process mainmenu/cssindex.tt (0.017279 seconds) --> |
236 | <!-- TIMER END: include mainmenu/cssindex.tt (0.017401 seconds) --> |
237 | |
238 | .... |
239 | |
240 | <!-- TIMER END: process mainmenu/footer.tt (0.003016 seconds) --> |
241 | |
8cd017a8 |
242 | You can suppress template profiling by setting the C<TIMER> configuration |
243 | item to a false value. |
4687ac0d |
244 | |
8cd017a8 |
245 | MyApp->config({ |
246 | 'MyApp::V::TT' => { |
247 | TIMER => 0, |
248 | }, |
249 | }); |
4687ac0d |
250 | |
caa61517 |
251 | =head2 METHODS |
8077080c |
252 | |
2774dc77 |
253 | =over 4 |
254 | |
255 | =item new |
256 | |
257 | The constructor for the TT view. Sets up the template provider, |
258 | and reads the application config. |
259 | |
8077080c |
260 | =cut |
261 | |
262 | sub new { |
9300af5b |
263 | my ( $class, $c, $arguments ) = @_; |
264 | |
265 | my $root = $c->config->{root}; |
266 | |
caa61517 |
267 | my %config = ( |
268 | EVAL_PERL => 0, |
269 | INCLUDE_PATH => [ $root, "$root/base" ], |
9300af5b |
270 | %{ $class->config }, |
8cd017a8 |
271 | %{$arguments} |
caa61517 |
272 | ); |
62728755 |
273 | |
8cd017a8 |
274 | # if we're debugging and/or the TIMER option is set, then we install |
275 | # Template::Timer as a custom CONTEXT object, but only if we haven't |
276 | # already got a custom CONTEXT defined |
277 | |
278 | if ( $config{TIMER} || ( $c->debug() && !exists $config{TIMER} ) ) { |
279 | if ( $config{CONTEXT} ) { |
280 | $c->log->error( |
281 | 'Cannot use Template::Timer - a TT CONFIG is already defined'); |
282 | } |
283 | else { |
284 | $config{CONTEXT} = Template::Timer->new( \%config ); |
285 | } |
286 | } |
287 | |
288 | if ( $c->debug && $config{DUMP_CONFIG} ) { |
289 | use Data::Dumper; |
290 | $c->log->debug( "TT Config: ", Dumper( \%config ) ); |
62728755 |
291 | } |
292 | |
8cd017a8 |
293 | return $class->NEXT::new( |
294 | $c, |
295 | { |
296 | template => Template->new( \%config ) || do { |
297 | my $error = Template->error(); |
298 | $c->log->error($error); |
299 | $c->error($error); |
300 | return undef; |
301 | } |
302 | } |
303 | ); |
8077080c |
304 | } |
305 | |
2774dc77 |
306 | =item process |
8077080c |
307 | |
722fede4 |
308 | Renders the template specified in C<$c-E<gt>stash-E<gt>{template}> or |
309 | C<$c-E<gt>request-E<gt>match>. Template variables are set up from the |
310 | contents of C<$c-E<gt>stash>, augmented with C<base> set to |
311 | C<$c-E<gt>req-E<gt>base>, C<c> to C<$c> and C<name> to |
8cd017a8 |
312 | C<$c-E<gt>config-E<gt>{name}>. Alternately, the C<CATALYST_VAR> |
313 | configuration item can be defined to specify the name of a template |
314 | variable through which the context reference (C<$c>) can be accessed. |
315 | In this case, the C<c>, C<base> and C<name> variables are omitted. |
316 | Output is stored in C<$c-E<gt>response-E<gt>output>. |
8077080c |
317 | |
318 | =cut |
319 | |
320 | sub process { |
321 | my ( $self, $c ) = @_; |
23042c3c |
322 | |
323 | my $template = $c->stash->{template} || $c->request->match; |
324 | |
325 | unless ($template) { |
8077080c |
326 | $c->log->debug('No template specified for rendering') if $c->debug; |
327 | return 0; |
328 | } |
23042c3c |
329 | |
330 | $c->log->debug(qq/Rendering template "$template"/) if $c->debug; |
23042c3c |
331 | |
8cd017a8 |
332 | my $output; |
333 | my $cvar = $self->config->{CATALYST_VAR}; |
334 | my $vars = { |
335 | defined $cvar |
336 | ? ( $cvar => $c ) |
337 | : ( |
338 | c => $c, |
339 | base => $c->req->base, |
340 | name => $c->config->{name} |
341 | ), |
342 | %{ $c->stash() } |
343 | }; |
344 | |
345 | unless ( $self->template->process( $template, $vars, \$output ) ) { |
8077080c |
346 | my $error = $self->template->error; |
347 | $error = qq/Couldn't render template "$error"/; |
348 | $c->log->error($error); |
becb7ac2 |
349 | $c->error($error); |
23042c3c |
350 | return 0; |
351 | } |
8cd017a8 |
352 | |
23042c3c |
353 | unless ( $c->response->content_type ) { |
354 | $c->response->content_type('text/html; charset=utf-8'); |
8077080c |
355 | } |
23042c3c |
356 | |
357 | $c->response->body($output); |
358 | |
8077080c |
359 | return 1; |
360 | } |
361 | |
2774dc77 |
362 | =item config |
8077080c |
363 | |
8cd017a8 |
364 | This method allows your view subclass to pass additional settings to |
365 | the TT configuration hash, or to set the C<CATALYST_VAR> and C<TIMER> |
366 | options. |
8077080c |
367 | |
2774dc77 |
368 | =back |
8077080c |
369 | |
8cd017a8 |
370 | =head2 HELPERS |
371 | |
372 | The L<Catalyst::Helper::View::TT> and |
373 | L<Catalyst::Helper::View::TTSite> helper modules are provided to create |
374 | your view module. There are invoked by the F<myapp_create.pl> script: |
375 | |
376 | $ script/myapp_create.pl view TT TT |
377 | |
378 | $ script/myapp_create.pl view TT TTSite |
379 | |
380 | The L<Catalyst::Helper::View::TT> module creates a basic TT view |
381 | module. The L<Catalyst::Helper::View::TTSite> module goes a little |
382 | further. It also creates a default set of templates to get you |
383 | started. It also configures the view module to locate the templates |
384 | automatically. |
385 | |
8077080c |
386 | =head1 SEE ALSO |
387 | |
8cd017a8 |
388 | L<Catalyst>, L<Catalyst::Helper::View::TT>, |
389 | L<Catalyst::Helper::View::TTSite>, L<Template::Manual> |
8077080c |
390 | |
391 | =head1 AUTHOR |
392 | |
393 | Sebastian Riedel, C<sri@cpan.org> |
d938377b |
394 | Marcus Ramberg, C<mramberg@cpan.org> |
722fede4 |
395 | Jesse Sheidlower, C<jester@panix.com> |
8cd017a8 |
396 | Andy Wardley, C<abw@cpan.org> |
8077080c |
397 | |
398 | =head1 COPYRIGHT |
399 | |
2774dc77 |
400 | This program is free software, you can redistribute it and/or modify it |
401 | under the same terms as Perl itself. |
8077080c |
402 | |
403 | =cut |
404 | |
405 | 1; |