8e7fa0e2b7bb180b8e2ebb9b1233253a51181cb2
[catagits/Catalyst-View-TT.git] / lib / Catalyst / View / TT.pm
1 package Catalyst::View::TT;
2
3 use strict;
4 use warnings;
5
6 use base qw/Catalyst::View/;
7 use Data::Dump 'dump';
8 use Template;
9 use Template::Timer;
10 use MRO::Compat;
11
12 our $VERSION = '0.31';
13
14 __PACKAGE__->mk_accessors('template');
15 __PACKAGE__->mk_accessors('include_path');
16
17 *paths = \&include_path;
18
19 =head1 NAME
20
21 Catalyst::View::TT - Template View Class
22
23 =head1 SYNOPSIS
24
25 # use the helper to create your View
26
27     myapp_create.pl view TT TT
28
29 # configure in lib/MyApp.pm (Could be set from configfile instead)
30
31     MyApp->config(
32         name     => 'MyApp',
33         root     => MyApp->path_to('root'),
34         'View::TT' => {
35             # any TT configurations items go here
36             INCLUDE_PATH => [
37               MyApp->path_to( 'root', 'src' ),
38               MyApp->path_to( 'root', 'lib' ),
39             ],
40             TEMPLATE_EXTENSION => '.tt',
41             CATALYST_VAR => 'c',
42             TIMER        => 0,
43             # Not set by default
44             PRE_PROCESS        => 'config/main',
45             WRAPPER            => 'site/wrapper',
46         },
47     );
48
49 # render view from lib/MyApp.pm or lib/MyApp::Controller::SomeController.pm
50
51     sub message : Global {
52         my ( $self, $c ) = @_;
53         $c->stash->{template} = 'message.tt2';
54         $c->stash->{message}  = 'Hello World!';
55         $c->forward( $c->view('TT') );
56     }
57
58 # access variables from template
59
60     The message is: [% message %].
61
62     # example when CATALYST_VAR is set to 'Catalyst'
63     Context is [% Catalyst %]
64     The base is [% Catalyst.req.base %]
65     The name is [% Catalyst.config.name %]
66
67     # example when CATALYST_VAR isn't set
68     Context is [% c %]
69     The base is [% base %]
70     The name is [% name %]
71
72 =cut
73
74 sub _coerce_paths {
75     my ( $paths, $dlim ) = shift;
76     return () if ( !$paths );
77     return @{$paths} if ( ref $paths eq 'ARRAY' );
78
79     # tweak delim to ignore C:/
80     unless ( defined $dlim ) {
81         $dlim = ( $^O eq 'MSWin32' ) ? ':(?!\\/)' : ':';
82     }
83     return split( /$dlim/, $paths );
84 }
85
86 sub new {
87     my ( $class, $c, $arguments ) = @_;
88     my $config = {
89         EVAL_PERL          => 0,
90         TEMPLATE_EXTENSION => '',
91         %{ $class->config },
92         %{$arguments},
93     };
94     if ( ! (ref $config->{INCLUDE_PATH} eq 'ARRAY') ) {
95         my $delim = $config->{DELIMITER};
96         my @include_path
97             = _coerce_paths( $config->{INCLUDE_PATH}, $delim );
98         if ( !@include_path ) {
99             my $root = $c->config->{root};
100             my $base = Path::Class::dir( $root, 'base' );
101             @include_path = ( "$root", "$base" );
102         }
103         $config->{INCLUDE_PATH} = \@include_path;
104     }
105
106     # if we're debugging and/or the TIMER option is set, then we install
107     # Template::Timer as a custom CONTEXT object, but only if we haven't
108     # already got a custom CONTEXT defined
109
110     if ( $config->{TIMER} ) {
111         if ( $config->{CONTEXT} ) {
112             $c->log->error(
113                 'Cannot use Template::Timer - a TT CONTEXT is already defined'
114             );
115         }
116         else {
117             $config->{CONTEXT} = Template::Timer->new(%$config);
118         }
119     }
120
121     if ( $c->debug && $config->{DUMP_CONFIG} ) {
122         $c->log->debug( "TT Config: ", dump($config) );
123     }
124
125     my $self = $class->next::method(
126         $c, { %$config },
127     );
128
129     # Set base include paths. Local'd in render if needed
130     $self->include_path($config->{INCLUDE_PATH});
131
132     $self->config($config);
133
134     # Creation of template outside of call to new so that we can pass [ $self ]
135     # as INCLUDE_PATH config item, which then gets ->paths() called to get list
136     # of include paths to search for templates.
137
138     # Use a weakend copy of self so we dont have loops preventing GC from working
139     my $copy = $self;
140     Scalar::Util::weaken($copy);
141     $config->{INCLUDE_PATH} = [ sub { $copy->paths } ];
142
143     if ( $config->{PROVIDERS} ) {
144         my @providers = ();
145         if ( ref($config->{PROVIDERS}) eq 'ARRAY') {
146             foreach my $p (@{$config->{PROVIDERS}}) {
147                 my $pname = $p->{name};
148                 my $prov = 'Template::Provider';
149                 if($pname eq '_file_')
150                 {
151                     $p->{args} = { %$config };
152                 }
153                 else
154                 {
155                     if($pname =~ s/^\+//) {
156                         $prov = $pname;
157                     }
158                     else
159                     {
160                         $prov .= "::$pname";
161                     }
162                     # We copy the args people want from the config
163                     # to the args
164                     $p->{args} ||= {};
165                     if ($p->{copy_config}) {
166                         map  { $p->{args}->{$_} = $config->{$_}  }
167                                    grep { exists $config->{$_} }
168                                    @{ $p->{copy_config} };
169                     }
170                 }
171                 eval "require $prov";
172                 if(!$@) {
173                     push @providers, "$prov"->new($p->{args});
174                 }
175                 else
176                 {
177                     $c->log->warn("Can't load $prov, ($@)");
178                 }
179             }
180         }
181         delete $config->{PROVIDERS};
182         if(@providers) {
183             $config->{LOAD_TEMPLATES} = \@providers;
184         }
185     }
186
187     $self->{template} =
188         Template->new($config) || do {
189             my $error = Template->error();
190             $c->log->error($error);
191             $c->error($error);
192             return undef;
193         };
194
195
196     return $self;
197 }
198
199 sub process {
200     my ( $self, $c ) = @_;
201
202     my $template = $c->stash->{template}
203       ||  $c->action . $self->config->{TEMPLATE_EXTENSION};
204
205     unless (defined $template) {
206         $c->log->debug('No template specified for rendering') if $c->debug;
207         return 0;
208     }
209
210     my $output = eval { $self->render($c, $template) };
211     if (my $err = $@) {
212         my $error = qq/Couldn't render template "$template"/;
213         $c->log->error($error);
214         $c->error($error);
215         return 0;
216     }
217
218     unless ( $c->response->content_type ) {
219         $c->response->content_type('text/html; charset=utf-8');
220     }
221
222     $c->response->body($output);
223
224     return 1;
225 }
226
227 sub render {
228     my ($self, $c, $template, $args) = @_;
229
230     $c->log->debug(qq/Rendering template "$template"/) if $c && $c->debug;
231
232     my $output;
233     my $vars = {
234         (ref $args eq 'HASH' ? %$args : %{ $c->stash() }),
235         $self->template_vars($c)
236     };
237
238     local $self->{include_path} =
239         [ @{ $vars->{additional_template_paths} }, @{ $self->{include_path} } ]
240         if ref $vars->{additional_template_paths};
241
242     $self->template->process( $template, $vars, \$output )
243         or die $self->template->error;
244     return $output;
245 }
246
247 sub template_vars {
248     my ( $self, $c ) = @_;
249
250     return  () unless $c;
251     my $cvar = $self->config->{CATALYST_VAR};
252
253     defined $cvar
254       ? ( $cvar => $c )
255       : (
256         c    => $c,
257         base => $c->req->base,
258         name => $c->config->{name}
259       )
260 }
261
262
263 1;
264
265 __END__
266
267 =head1 DESCRIPTION
268
269 This is the Catalyst view class for the L<Template Toolkit|Template>.
270 Your application should defined a view class which is a subclass of
271 this module.  The easiest way to achieve this is using the
272 F<myapp_create.pl> script (where F<myapp> should be replaced with
273 whatever your application is called).  This script is created as part
274 of the Catalyst setup.
275
276     $ script/myapp_create.pl view TT TT
277
278 This creates a MyApp::View::TT.pm module in the F<lib> directory (again,
279 replacing C<MyApp> with the name of your application) which looks
280 something like this:
281
282     package FooBar::View::TT;
283
284     use strict;
285     use warnings;
286
287     use base 'Catalyst::View::TT';
288
289     __PACKAGE__->config->{DEBUG} = 'all';
290
291 Now you can modify your action handlers in the main application and/or
292 controllers to forward to your view class.  You might choose to do this
293 in the end() method, for example, to automatically forward all actions
294 to the TT view class.
295
296     # In MyApp or MyApp::Controller::SomeController
297
298     sub end : Private {
299         my( $self, $c ) = @_;
300         $c->forward( $c->view('TT') );
301     }
302
303 But if you are using the standard auto-generated end action, you don't even need
304 to do this!
305
306     # in MyApp::Controller::Root
307     sub end : ActionClass('RenderView') {} # no need to change this line
308
309     # in MyApp.pm
310     __PACKAGE__->config(
311         ...
312         default_view => 'TT',
313     );
314
315 This will Just Work.  And it has the advantages that:
316
317 =over 4
318
319 =item *
320
321 If you want to use a different view for a given request, just set 
322 << $c->stash->{current_view} >>.  (See L<Catalyst>'s C<< $c->view >> method
323 for details.
324
325 =item *
326
327 << $c->res->redirect >> is handled by default.  If you just forward to 
328 C<View::TT> in your C<end> routine, you could break this by sending additional
329 content.
330
331 =back
332
333 See L<Catalyst::Action::RenderView> for more details.
334
335 =head2 CONFIGURATION
336
337 There are a three different ways to configure your view class.  The
338 first way is to call the C<config()> method in the view subclass.  This
339 happens when the module is first loaded.
340
341     package MyApp::View::TT;
342
343     use strict;
344     use base 'Catalyst::View::TT';
345
346     MyApp::View::TT->config({
347         INCLUDE_PATH => [
348             MyApp->path_to( 'root', 'templates', 'lib' ),
349             MyApp->path_to( 'root', 'templates', 'src' ),
350         ],
351         PRE_PROCESS  => 'config/main',
352         WRAPPER      => 'site/wrapper',
353     });
354
355 The second way is to define a C<new()> method in your view subclass.
356 This performs the configuration when the view object is created,
357 shortly after being loaded.  Remember to delegate to the base class
358 C<new()> method (via C<$self-E<gt>next::method()> in the example below) after
359 performing any configuration.
360
361     sub new {
362         my $self = shift;
363         $self->config({
364             INCLUDE_PATH => [
365                 MyApp->path_to( 'root', 'templates', 'lib' ),
366                 MyApp->path_to( 'root', 'templates', 'src' ),
367             ],
368             PRE_PROCESS  => 'config/main',
369             WRAPPER      => 'site/wrapper',
370         });
371         return $self->next::method(@_);
372     }
373
374 The final, and perhaps most direct way, is to define a class
375 item in your main application configuration, again by calling the
376 ubiquitous C<config()> method.  The items in the class hash are
377 added to those already defined by the above two methods.  This happens
378 in the base class new() method (which is one reason why you must
379 remember to call it via C<MRO::Compat> if you redefine the C<new()>
380 method in a subclass).
381
382     package MyApp;
383
384     use strict;
385     use Catalyst;
386
387     MyApp->config({
388         name     => 'MyApp',
389         root     => MyApp->path_to('root'),
390         'View::TT' => {
391             INCLUDE_PATH => [
392                 MyApp->path_to( 'root', 'templates', 'lib' ),
393                 MyApp->path_to( 'root', 'templates', 'src' ),
394             ],
395             PRE_PROCESS  => 'config/main',
396             WRAPPER      => 'site/wrapper',
397         },
398     });
399
400 Note that any configuration items defined by one of the earlier
401 methods will be overwritten by items of the same name provided by the
402 latter methods.
403
404 =head2 DYNAMIC INCLUDE_PATH
405
406 Sometimes it is desirable to modify INCLUDE_PATH for your templates at run time.
407
408 Additional paths can be added to the start of INCLUDE_PATH via the stash as
409 follows:
410
411     $c->stash->{additional_template_paths} =
412         [$c->config->{root} . '/test_include_path'];
413
414 If you need to add paths to the end of INCLUDE_PATH, there is also an
415 include_path() accessor available:
416
417     push( @{ $c->view('TT')->include_path }, qw/path/ );
418
419 Note that if you use include_path() to add extra paths to INCLUDE_PATH, you
420 MUST check for duplicate paths. Without such checking, the above code will add
421 "path" to INCLUDE_PATH at every request, causing a memory leak.
422
423 A safer approach is to use include_path() to overwrite the array of paths
424 rather than adding to it. This eliminates both the need to perform duplicate
425 checking and the chance of a memory leak:
426
427     @{ $c->view('TT')->include_path } = qw/path another_path/;
428
429 If you are calling C<render> directly then you can specify dynamic paths by
430 having a C<additional_template_paths> key with a value of additonal directories
431 to search. See L<CAPTURING TEMPLATE OUTPUT> for an example showing this.
432
433 =head2 RENDERING VIEWS
434
435 The view plugin renders the template specified in the C<template>
436 item in the stash.
437
438     sub message : Global {
439         my ( $self, $c ) = @_;
440         $c->stash->{template} = 'message.tt2';
441         $c->forward( $c->view('TT') );
442     }
443
444 If a stash item isn't defined, then it instead uses the
445 stringification of the action dispatched to (as defined by $c->action)
446 in the above example, this would be C<message>, but because the default
447 is to append '.tt', it would load C<root/message.tt>.
448
449 The items defined in the stash are passed to the Template Toolkit for
450 use as template variables.
451
452     sub default : Private {
453         my ( $self, $c ) = @_;
454         $c->stash->{template} = 'message.tt2';
455         $c->stash->{message}  = 'Hello World!';
456         $c->forward( $c->view('TT') );
457     }
458
459 A number of other template variables are also added:
460
461     c      A reference to the context object, $c
462     base   The URL base, from $c->req->base()
463     name   The application name, from $c->config->{ name }
464
465 These can be accessed from the template in the usual way:
466
467 <message.tt2>:
468
469     The message is: [% message %]
470     The base is [% base %]
471     The name is [% name %]
472
473
474 The output generated by the template is stored in C<< $c->response->body >>.
475
476 =head2 CAPTURING TEMPLATE OUTPUT
477
478 If you wish to use the output of a template for some other purpose than
479 displaying in the response, e.g. for sending an email, this is possible using
480 L<Catalyst::Plugin::Email> and the L<render> method:
481
482   sub send_email : Local {
483     my ($self, $c) = @_;
484
485     $c->email(
486       header => [
487         To      => 'me@localhost',
488         Subject => 'A TT Email',
489       ],
490       body => $c->view('TT')->render($c, 'email.tt', {
491         additional_template_paths => [ $c->config->{root} . '/email_templates'],
492         email_tmpl_param1 => 'foo'
493         }
494       ),
495     );
496   # Redirect or display a message
497   }
498
499 =head2 TEMPLATE PROFILING
500
501 See L<C<TIMER>> property of the L<config> method.
502
503 =head2 METHODS
504
505 =head2 new
506
507 The constructor for the TT view. Sets up the template provider,
508 and reads the application config.
509
510 =head2 process($c)
511
512 Renders the template specified in C<< $c->stash->{template} >> or
513 C<< $c->action >> (the private name of the matched action).  Calls L<render> to
514 perform actual rendering. Output is stored in C<< $c->response->body >>.
515
516 It is possible to forward to the process method of a TT view from inside
517 Catalyst like this:
518
519     $c->forward('View::TT');
520
521 N.B. This is usually done automatically by L<Catalyst::Action::RenderView>.
522
523 =head2 render($c, $template, \%args)
524
525 Renders the given template and returns output. Throws a L<Template::Exception>
526 object upon error.
527
528 The template variables are set to C<%$args> if $args is a hashref, or
529 $C<< $c->stash >> otherwise. In either case the variables are augmented with
530 C<base> set to C< << $c->req->base >>, C<c> to C<$c> and C<name> to
531 C<< $c->config->{name} >>. Alternately, the C<CATALYST_VAR> configuration item
532 can be defined to specify the name of a template variable through which the
533 context reference (C<$c>) can be accessed. In this case, the C<c>, C<base> and
534 C<name> variables are omitted.
535
536 C<$template> can be anything that Template::process understands how to
537 process, including the name of a template file or a reference to a test string.
538 See L<Template::process|Template/process> for a full list of supported formats.
539
540 To use the render method outside of your Catalyst app, just pass a undef context.
541 This can be useful for tests, for instance.
542
543 It is possible to forward to the render method of a TT view from inside Catalyst
544 to render page fragments like this:
545
546     my $fragment = $c->forward("View::TT", "render", $template_name, $c->stash->{fragment_data});
547
548 =head2 template_vars
549
550 Returns a list of keys/values to be used as the catalyst variables in the
551 template.
552
553 =head2 config
554
555 This method allows your view subclass to pass additional settings to
556 the TT configuration hash, or to set the options as below:
557
558 =head2 paths
559
560 The list of paths TT will look for templates in.
561
562 =head2 C<CATALYST_VAR>
563
564 Allows you to change the name of the Catalyst context object. If set, it will also
565 remove the base and name aliases, so you will have access them through <context>.
566
567 For example:
568
569     MyApp->config({
570         name     => 'MyApp',
571         root     => MyApp->path_to('root'),
572         'View::TT' => {
573             CATALYST_VAR => 'Catalyst',
574         },
575     });
576
577 F<message.tt2>:
578
579     The base is [% Catalyst.req.base %]
580     The name is [% Catalyst.config.name %]
581
582 =head2 C<TIMER>
583
584 If you have configured Catalyst for debug output, and turned on the TIMER setting,
585 C<Catalyst::View::TT> will enable profiling of template processing
586 (using L<Template::Timer>). This will embed HTML comments in the
587 output from your templates, such as:
588
589     <!-- TIMER START: process mainmenu/mainmenu.ttml -->
590     <!-- TIMER START: include mainmenu/cssindex.tt -->
591     <!-- TIMER START: process mainmenu/cssindex.tt -->
592     <!-- TIMER END: process mainmenu/cssindex.tt (0.017279 seconds) -->
593     <!-- TIMER END: include mainmenu/cssindex.tt (0.017401 seconds) -->
594
595     ....
596
597     <!-- TIMER END: process mainmenu/footer.tt (0.003016 seconds) -->
598
599
600 =head2 C<TEMPLATE_EXTENSION>
601
602 a sufix to add when looking for templates bases on the C<match> method in L<Catalyst::Request>.
603
604 For example:
605
606   package MyApp::Controller::Test;
607   sub test : Local { .. }
608
609 Would by default look for a template in <root>/test/test. If you set TEMPLATE_EXTENSION to '.tt', it will look for
610 <root>/test/test.tt.
611
612 =head2 C<PROVIDERS>
613
614 Allows you to specify the template providers that TT will use.
615
616     MyApp->config({
617         name     => 'MyApp',
618         root     => MyApp->path_to('root'),
619         'View::TT' => {
620             PROVIDERS => [
621                 {
622                     name    => 'DBI',
623                     args    => {
624                         DBI_DSN => 'dbi:DB2:books',
625                         DBI_USER=> 'foo'
626                     }
627                 }, {
628                     name    => '_file_',
629                     args    => {}
630                 }
631             ]
632         },
633     });
634
635 The 'name' key should correspond to the class name of the provider you
636 want to use.  The _file_ name is a special case that represents the default
637 TT file-based provider.  By default the name is will be prefixed with
638 'Template::Provider::'.  You can fully qualify the name by using a unary
639 plus:
640
641     name => '+MyApp::Provider::Foo'
642
643 You can also specify the 'copy_config' key as an arrayref, to copy those keys
644 from the general config, into the config for the provider:
645
646     DEFAULT_ENCODING    => 'utf-8',
647     PROVIDERS => [
648         {
649             name    => 'Encoding',
650             copy_config => [qw(DEFAULT_ENCODING INCLUDE_PATH)]
651         }
652     ]
653
654 This can prove useful when you want to use the additional_template_paths hack
655 in your own provider, or if you need to use Template::Provider::Encoding
656
657 =head2 HELPERS
658
659 The L<Catalyst::Helper::View::TT> and
660 L<Catalyst::Helper::View::TTSite> helper modules are provided to create
661 your view module.  There are invoked by the F<myapp_create.pl> script:
662
663     $ script/myapp_create.pl view TT TT
664
665     $ script/myapp_create.pl view TT TTSite
666
667 The L<Catalyst::Helper::View::TT> module creates a basic TT view
668 module.  The L<Catalyst::Helper::View::TTSite> module goes a little
669 further.  It also creates a default set of templates to get you
670 started.  It also configures the view module to locate the templates
671 automatically.
672
673 =head1 NOTES
674
675 If you are using the L<CGI> module inside your templates, you will
676 experience that the Catalyst server appears to hang while rendering
677 the web page. This is due to the debug mode of L<CGI> (which is
678 waiting for input in the terminal window). Turning off the
679 debug mode using the "-no_debug" option solves the
680 problem, eg.:
681
682     [% USE CGI('-no_debug') %]
683
684 =head1 SEE ALSO
685
686 L<Catalyst>, L<Catalyst::Helper::View::TT>,
687 L<Catalyst::Helper::View::TTSite>, L<Template::Manual>
688
689 =head1 AUTHORS
690
691 Sebastian Riedel, C<sri@cpan.org>
692
693 Marcus Ramberg, C<mramberg@cpan.org>
694
695 Jesse Sheidlower, C<jester@panix.com>
696
697 Andy Wardley, C<abw@cpan.org>
698
699 =head1 COPYRIGHT
700
701 This program is free software. You can redistribute it and/or modify it
702 under the same terms as Perl itself.
703
704 =cut