159404ab986e4cb8f63b4858577a35f58ca89880
[catagits/Catalyst-View-TT.git] / lib / Catalyst / View / TT.pm
1 package Catalyst::View::TT;
2
3 use strict;
4 use base qw/Catalyst::View/;
5 use Template;
6 use Template::Timer;
7 use NEXT;
8
9 our $VERSION = '0.22';
10
11 __PACKAGE__->mk_accessors('template');
12 __PACKAGE__->mk_accessors('include_path');
13
14 =head1 NAME
15
16 Catalyst::View::TT - Template View Class
17
18 =head1 SYNOPSIS
19
20 # use the helper to create View
21     myapp_create.pl view TT TT
22
23 # configure in lib/MyApp.pm
24
25     MyApp->config(
26         name     => 'MyApp',
27         root     => MyApp->path_to('root');,
28         'View::TT' => {
29             # any TT configurations items go here
30             INCLUDE_PATH => [
31               MyApp->path_to( 'root', 'src' ), 
32               MyApp->path_to( 'root', 'lib' ), 
33             ],
34             PRE_PROCESS        => 'config/main',
35             WRAPPER            => 'site/wrapper',
36             TEMPLATE_EXTENSION => '.tt',
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     }
52
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';
86
87     __PACKAGE__->config->{DEBUG} = 'all';
88
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.
93
94     # In MyApp or MyApp::Controller::SomeController
95     
96     sub end : Private {
97         my( $self, $c ) = @_;
98         $c->forward('MyApp::V::TT');
99     }
100
101 =head2 CONFIGURATION
102
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.
106
107     package MyApp::V::TT;
108     
109     use strict;
110     use base 'Catalyst::View::TT';
111
112     MyApp::V::TT->config({
113         INCLUDE_PATH => [
114             MyApp->path_to( 'root', 'templates', 'lib' ),
115             MyApp->path_to( 'root', 'templates', 'src' ),
116         ],
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({
130             INCLUDE_PATH => [
131                 MyApp->path_to( 'root', 'templates', 'lib' ),
132                 MyApp->path_to( 'root', 'templates', 'src' ),
133             ],
134             PRE_PROCESS  => 'config/main',
135             WRAPPER      => 'site/wrapper',
136         });
137         return $self->NEXT::new(@_);
138     }
139  
140 The final, and perhaps most direct way, is to define a class
141 item in your main application configuration, again by calling the
142 uniquitous C<config()> method.  The items in the class hash are
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     
153     MyApp->config({
154         name     => 'MyApp',
155         root     => MyApp->path_to('root'),
156         'V::TT' => {
157             INCLUDE_PATH => [
158                 MyApp->path_to( 'root', 'templates', 'lib' ),
159                 MyApp->path_to( 'root', 'templates', 'src' ),
160             ],
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
170 =head2 DYNAMIC INCLUDE_PATH
171
172 Sometimes it is desirable to modify INCLUDE_PATH for your templates at run time.
173  
174 Additional paths can be added to the start of INCLUDE_PATH via the stash as
175 follows:
176
177     $c->stash->{additional_template_paths} =
178         [$c->config->{root} . '/test_include_path'];
179
180 If you need to add paths to the end of INCLUDE_PATH, there is also an
181 include_path() accessor available:
182
183     push( @{ $c->view('TT')->include_path }, qw/path/ );
184
185 Note that if you use include_path() to add extra paths to INCLUDE_PATH, you
186 MUST check for duplicate paths. Without such checking, the above code will add
187 "path" to INCLUDE_PATH at every request, causing a memory leak.
188
189 A safer approach is to use include_path() to overwrite the array of paths
190 rather than adding to it. This eliminates both the need to perform duplicate
191 checking and the chance of a memory leak:
192
193     @{ $c->view('TT')->include_path } = qw/path another_path/;
194
195 =head2 RENDERING VIEWS
196
197 The view plugin renders the template specified in the C<template>
198 item in the stash.  
199
200     sub message : Global {
201         my ( $self, $c ) = @_;
202         $c->stash->{template} = 'message.tt2';
203         $c->forward('MyApp::V::TT');
204     }
205
206 If a class item isn't defined, then it instead uses the
207 current match, as returned by C<$c-E<gt>match>.  In the above 
208 example, this would be C<message>.
209
210 The items defined in the stash are passed to the Template Toolkit for
211 use as template variables.
212
213 sub message : Global {
214     sub default : Private {
215         my ( $self, $c ) = @_;
216         $c->stash->{template} = 'message.tt2';
217         $c->stash->{message}  = 'Hello World!';
218         $c->forward('MyApp::V::TT');
219     }
220
221 A number of other template variables are also added:
222
223     c      A reference to the context object, $c
224     base   The URL base, from $c->req->base()
225     name   The application name, from $c->config->{ name }
226
227 These can be accessed from the template in the usual way:
228
229 <message.tt2>:
230
231     The message is: [% message %]
232     The base is [% base %]
233     The name is [% name %]
234
235
236 The output generated by the template is stored in
237 C<$c-E<gt>response-E<gt>output>.
238
239 =head2 TEMPLATE PROFILING
240
241 =head2 METHODS
242
243 =over 4
244
245 =item new
246
247 The constructor for the TT view. Sets up the template provider, 
248 and reads the application config.
249
250 =cut
251
252 sub _coerce_paths {
253     my ( $paths, $dlim ) = shift;
254     return () if ( !$paths );
255     return @{$paths} if ( ref $paths eq 'ARRAY' );
256
257     # tweak delim to ignore C:/
258     unless ( defined $dlim ) {
259         $dlim = ( $^O eq 'MSWin32' ) ? ':(?!\\/)' : ':';
260     }
261     return split( /$dlim/, $paths );
262 }
263
264 sub new {
265     my ( $class, $c, $arguments ) = @_;
266     my $config = {
267         EVAL_PERL          => 0,
268         TEMPLATE_EXTENSION => '',
269         %{ $class->config },
270         %{$arguments},
271     };
272     if ( ! (ref $config->{INCLUDE_PATH} eq 'ARRAY') ) {
273         my $delim = $config->{DELIMITER};
274         my @include_path
275             = _coerce_paths( $config->{INCLUDE_PATH}, $delim );
276         if ( !@include_path ) {
277             my $root = $c->config->{root};
278             my $base = Path::Class::dir( $root, 'base' );
279             @include_path = ( "$root", "$base" );
280         }
281         $config->{INCLUDE_PATH} = \@include_path;
282     }
283
284
285
286     # if we're debugging and/or the TIMER option is set, then we install
287     # Template::Timer as a custom CONTEXT object, but only if we haven't
288     # already got a custom CONTEXT defined
289
290     if ( $config->{TIMER} ) {
291         if ( $config->{CONTEXT} ) {
292             $c->log->error(
293                 'Cannot use Template::Timer - a TT CONFIG is already defined'
294             );
295         }
296         else {
297             $config->{CONTEXT} = Template::Timer->new(%$config);
298         }
299     }
300
301     if ( $c->debug && $config->{DUMP_CONFIG} ) {
302         use Data::Dumper;
303         $c->log->debug( "TT Config: ", Dumper($config) );
304     }
305     if ( $config->{PROVIDERS} ) {
306         my @providers = ();
307         if ( ref($config->{PROVIDERS}) eq 'ARRAY') {
308             foreach my $p (@{$config->{PROVIDERS}}) {
309                 my $pname = $p->{name};
310                 eval "require Template::Provider::$pname";
311                 if(!$@) {
312                     push @providers, "Template::Provider::${pname}"->new($p->{args});
313                 }
314             }
315         }
316         delete $config->{PROVIDERS};
317         if(@providers) {
318             $config->{LOAD_TEMPLATES} = \@providers;
319         }
320     }
321
322     my $self = $class->NEXT::new(
323         $c,
324         {   template => Template->new($config) || do {
325                 my $error = Template->error();
326                 $c->log->error($error);
327                 $c->error($error);
328                 return undef;
329             },
330             %{$config},
331         },
332     );
333     $self->include_path($config->{INCLUDE_PATH});
334     $self->config($config);
335
336     return $self;
337 }
338
339 =item process
340
341 Renders the template specified in C<$c-E<gt>stash-E<gt>{template}> or
342 C<$c-E<gt>action> (the private name of the matched action. 
343 Template variables are set up from the contents of C<$c-E<gt>stash>, 
344 augmented with C<base> set to C<$c-E<gt>req-E<gt>base>, C<c> to C<$c> and 
345 C<name> to C<$c-E<gt>config-E<gt>{name}>. Alternately, the C<CATALYST_VAR>
346 configuration item can be defined to specify the name of a template
347 variable through which the context reference (C<$c>) can be accessed.
348 In this case, the C<c>, C<base> and C<name> variables are omitted.
349 Output is stored in C<$c-E<gt>response-E<gt>output>.
350
351 =cut
352
353 sub process {
354     my ( $self, $c ) = @_;
355
356     my $template = $c->stash->{template}
357       ||  $c->action . $self->config->{TEMPLATE_EXTENSION};
358
359     unless ($template) {
360         $c->log->debug('No template specified for rendering') if $c->debug;
361         return 0;
362     }
363
364     $c->log->debug(qq/Rendering template "$template"/) if $c->debug;
365
366     my $output;
367     my $vars = { $self->template_vars($c) };
368
369     unshift @{ $self->include_path },
370       @{ $c->stash->{additional_template_paths} }
371       if ref $c->stash->{additional_template_paths};
372     unless ( $self->template->process( $template, $vars, \$output ) ) {
373         my $error = $self->template->error;
374         $error = qq/Couldn't render template "$error"/;
375         $c->log->error($error);
376         $c->error($error);
377         return 0;
378     }
379     splice @{ $self->include_path }, 0,
380       scalar @{ $c->stash->{additional_template_paths} }
381       if ref $c->stash->{additional_template_paths};
382
383     unless ( $c->response->content_type ) {
384         $c->response->content_type('text/html; charset=utf-8');
385     }
386
387     $c->response->body($output);
388
389     return 1;
390 }
391
392 =item template_vars
393
394 Returns a list of keys/values to be used as the variables in the
395 template.
396
397 =cut
398
399 sub template_vars {
400     my ( $self, $c ) = @_;
401
402     my $cvar = $self->config->{CATALYST_VAR};
403
404     defined $cvar
405       ? ( $cvar => $c )
406       : (
407         c    => $c,
408         base => $c->req->base,
409         name => $c->config->{name}
410       ),
411       %{ $c->stash() }
412
413 }
414 =item config
415
416 This method allows your view subclass to pass additional settings to
417 the TT configuration hash, or to set the options as below:
418
419 =over 2
420
421 =item C<CATALYST_VAR> 
422
423 Allows you to change the name of the Catalyst context object. If set, it will also
424 remove the base and name aliases, so you will have access them through <context>.
425
426 For example:
427
428     MyApp->config({
429         name     => 'MyApp',
430         root     => MyApp->path_to('root'),
431         'V::TT' => {
432             CATALYST_VAR => 'Catalyst',
433         },
434     });
435
436 F<message.tt2>:
437
438     The base is [% Catalyst.req.base %]
439     The name is [% Catalyst.config.name %]
440
441 =item C<TIMER>
442
443 If you have configured Catalyst for debug output, and turned on the TIMER setting,
444 C<Catalyst::View::TT> will enable profiling of template processing
445 (using L<Template::Timer>). This will embed HTML comments in the
446 output from your templates, such as:
447
448     <!-- TIMER START: process mainmenu/mainmenu.ttml -->
449     <!-- TIMER START: include mainmenu/cssindex.tt -->
450     <!-- TIMER START: process mainmenu/cssindex.tt -->
451     <!-- TIMER END: process mainmenu/cssindex.tt (0.017279 seconds) -->
452     <!-- TIMER END: include mainmenu/cssindex.tt (0.017401 seconds) -->
453
454     ....
455
456     <!-- TIMER END: process mainmenu/footer.tt (0.003016 seconds) -->
457
458
459 =item C<TEMPLATE_EXTENSION>
460
461 a sufix to add when looking for templates bases on the C<match> method in L<Catalyst::Request>.
462
463 For example:
464
465   package MyApp::C::Test;
466   sub test : Local { .. } 
467
468 Would by default look for a template in <root>/test/test. If you set TEMPLATE_EXTENSION to '.tt', it will look for
469 <root>/test/test.tt.
470
471 =back
472
473 =back
474
475 =head2 HELPERS
476
477 The L<Catalyst::Helper::View::TT> and
478 L<Catalyst::Helper::View::TTSite> helper modules are provided to create
479 your view module.  There are invoked by the F<myapp_create.pl> script:
480
481     $ script/myapp_create.pl view TT TT
482
483     $ script/myapp_create.pl view TT TTSite
484
485 The L<Catalyst::Helper::View::TT> module creates a basic TT view
486 module.  The L<Catalyst::Helper::View::TTSite> module goes a little
487 further.  It also creates a default set of templates to get you
488 started.  It also configures the view module to locate the templates
489 automatically.
490
491 =head1 SEE ALSO
492
493 L<Catalyst>, L<Catalyst::Helper::View::TT>,
494 L<Catalyst::Helper::View::TTSite>, L<Template::Manual>
495
496 =head1 AUTHORS
497
498 Sebastian Riedel, C<sri@cpan.org>
499
500 Marcus Ramberg, C<mramberg@cpan.org>
501
502 Jesse Sheidlower, C<jester@panix.com>
503
504 Andy Wardley, C<abw@cpan.org>
505
506 =head1 COPYRIGHT
507
508 This program is free software, you can redistribute it and/or modify it 
509 under the same terms as Perl itself.
510
511 =cut
512
513 1;