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       <