File Coverage

blib/lib/Catalyst/View/TT.pm
Criterion Covered Total %
statement 71 102 69.6
branch 23 48 47.9
condition 4 9 44.4
subroutine 12 12 100.0
pod 4 4 100.0
total 114 175 65.1


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