releasing V:TT 0.21
[catagits/Catalyst-View-TT.git] / lib / Catalyst / View / TT.pm
CommitLineData
8077080c 1package Catalyst::View::TT;
2
3use strict;
7d8aa5ec 4use base qw/Catalyst::View/;
8077080c 5use Template;
6use Template::Timer;
7use NEXT;
8
c3c62106 9our $VERSION = '0.21';
8077080c 10
11__PACKAGE__->mk_accessors('template');
e2bbd784 12__PACKAGE__->mk_accessors('include_path');
8077080c 13
14=head1 NAME
15
16Catalyst::View::TT - Template View Class
17
18=head1 SYNOPSIS
19
8cd017a8 20# use the helper to create View
722fede4 21 myapp_create.pl view TT TT
8077080c 22
8cd017a8 23# configure in lib/MyApp.pm
24
8cd017a8 25 MyApp->config({
26 name => 'MyApp',
7d8aa5ec 27 root => MyApp->path_to('root');,
7d607671 28 'V::TT' => {
8cd017a8 29 # any TT configurations items go here
30 INCLUDE_PATH => [
93f25382 31 MyApp->path_to( 'root', 'src' ),
32 MyApp->path_to( 'root', 'lib' ),
8cd017a8 33 ],
07571b2f 34 PRE_PROCESS => 'config/main',
35 WRAPPER => 'site/wrapper',
36 TEMPLATE_EXTENSION => '.tt',
8cd017a8 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 {
94b3529a 47 my ( $self, $c ) = @_;
48 $c->stash->{template} = 'message.tt2';
49 $c->stash->{message} = 'Hello World!';
8cd017a8 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
69This is the Catalyst view class for the L<Template Toolkit|Template>.
70Your application should defined a view class which is a subclass of
71this module. The easiest way to achieve this is using the
72F<myapp_create.pl> script (where F<myapp> should be replaced with
73whatever your application is called). This script is created as part
74of the Catalyst setup.
75
76 $ script/myapp_create.pl view TT TT
77
78This creates a MyApp::V::TT.pm module in the F<lib> directory (again,
79replacing C<MyApp> with the name of your application) which looks
80something 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 89Now you can modify your action handlers in the main application and/or
90controllers to forward to your view class. You might choose to do this
91in the end() method, for example, to automatically forward all actions
92to the TT view class.
722fede4 93
8cd017a8 94 # In MyApp or MyApp::Controller::SomeController
8077080c 95
8cd017a8 96 sub end : Private {
94b3529a 97 my( $self, $c ) = @_;
8cd017a8 98 $c->forward('MyApp::V::TT');
99 }
8077080c 100
8cd017a8 101=head2 CONFIGURATION
4687ac0d 102
8cd017a8 103There are a three different ways to configure your view class. The
104first way is to call the C<config()> method in the view subclass. This
105happens 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 MyApp::V::TT->config({
7d8aa5ec 113 INCLUDE_PATH => [
114 MyApp->path_to( 'root', 'templates', 'lib' ),
115 MyApp->path_to( 'root', 'templates', 'src' ),
116 ],
8cd017a8 117 PRE_PROCESS => 'config/main',
118 WRAPPER => 'site/wrapper',
119 });
120
121The second way is to define a C<new()> method in your view subclass.
122This performs the configuration when the view object is created,
123shortly after being loaded. Remember to delegate to the base class
124C<new()> method (via C<$self-E<gt>NEXT::new()> in the example below) after
125performing any configuration.
126
127 sub new {
128 my $self = shift;
129 $self->config({
7d8aa5ec 130 INCLUDE_PATH => [
131 MyApp->path_to( 'root', 'templates', 'lib' ),
132 MyApp->path_to( 'root', 'templates', 'src' ),
133 ],
8cd017a8 134 PRE_PROCESS => 'config/main',
135 WRAPPER => 'site/wrapper',
136 });
137 return $self->NEXT::new(@_);
138 }
139
94b3529a 140The final, and perhaps most direct way, is to define a class
8cd017a8 141item in your main application configuration, again by calling the
94b3529a 142uniquitous C<config()> method. The items in the class hash are
8cd017a8 143added to those already defined by the above two methods. This happens
144in the base class new() method (which is one reason why you must
145remember to call it via C<NEXT> if you redefine the C<new()> method in a
146subclass).
147
148 package MyApp;
149
150 use strict;
151 use Catalyst;
152
8cd017a8 153 MyApp->config({
154 name => 'MyApp',
7d8aa5ec 155 root => MyApp->path_to('root'),
7d607671 156 'V::TT' => {
7d8aa5ec 157 INCLUDE_PATH => [
158 MyApp->path_to( 'root', 'templates', 'lib' ),
159 MyApp->path_to( 'root', 'templates', 'src' ),
160 ],
8cd017a8 161 PRE_PROCESS => 'config/main',
162 WRAPPER => 'site/wrapper',
163 },
164 });
165
166Note that any configuration items defined by one of the earlier
167methods will be overwritten by items of the same name provided by the
168latter methods.
169
e2bbd784 170=head2 DYNAMIC INCLUDE_PATH
171
172It is sometimes needed to dynamically add additional paths to the
173INCLUDE_PATH variable of the template object. This can be done by setting
174'additional_include_paths' on stash to a referrence to an array with
175additional paths:
176
177 $c->stash->{additional_template_paths} = [$c->config->{root} . '/test_include_path'];
178
8cd017a8 179=head2 RENDERING VIEWS
180
181The view plugin renders the template specified in the C<template>
182item in the stash.
183
184 sub message : Global {
94b3529a 185 my ( $self, $c ) = @_;
186 $c->stash->{template} = 'message.tt2';
8cd017a8 187 $c->forward('MyApp::V::TT');
188 }
722fede4 189
94b3529a 190If a class item isn't defined, then it instead uses the
8cd017a8 191current match, as returned by C<$c-E<gt>match>. In the above
192example, this would be C<message>.
193
194The items defined in the stash are passed to the Template Toolkit for
195use as template variables.
196
197sub message : Global {
198 sub default : Private {
94b3529a 199 my ( $self, $c ) = @_;
200 $c->stash->{template} = 'message.tt2';
201 $c->stash->{message} = 'Hello World!';
8cd017a8 202 $c->forward('MyApp::V::TT');
203 }
7b592fc7 204
8cd017a8 205A number of other template variables are also added:
8077080c 206
8cd017a8 207 c A reference to the context object, $c
208 base The URL base, from $c->req->base()
209 name The application name, from $c->config->{ name }
210
211These can be accessed from the template in the usual way:
212
213<message.tt2>:
214
215 The message is: [% message %]
216 The base is [% base %]
217 The name is [% name %]
218
8cd017a8 219
220The output generated by the template is stored in
221C<$c-E<gt>response-E<gt>output>.
222
223=head2 TEMPLATE PROFILING
224
caa61517 225=head2 METHODS
8077080c 226
2774dc77 227=over 4
228
229=item new
230
231The constructor for the TT view. Sets up the template provider,
232and reads the application config.
233
8077080c 234=cut
235
83056300 236sub _coerce_paths {
e2bbd784 237 my ( $paths, $dlim ) = shift;
bec996dc 238 return () if ( !$paths );
239 return @{$paths} if ( ref $paths eq 'ARRAY' );
9300af5b 240
bec996dc 241 # tweak delim to ignore C:/
242 unless ( defined $dlim ) {
243 $dlim = ( $^O eq 'MSWin32' ) ? ':(?!\\/)' : ':';
244 }
245 return split( /$dlim/, $paths );
246}
9300af5b 247
e2bbd784 248sub new {
249 my ( $class, $c, $arguments ) = @_;
8d116b20 250 my $config = {
251 EVAL_PERL => 0,
252 TEMPLATE_EXTENSION => '',
253 %{ $class->config },
254 %{$arguments},
255 };
256 if ( ! (ref $config->{INCLUDE_PATH} eq 'ARRAY') ) {
257 my $delim = $config->{DELIMITER};
bec996dc 258 my @include_path
8d116b20 259 = _coerce_paths( $config->{INCLUDE_PATH}, $delim );
bec996dc 260 if ( !@include_path ) {
3a4ffa2a 261 my $root = $c->config->{root};
bec996dc 262 my $base = Path::Class::dir( $root, 'base' );
3a4ffa2a 263 @include_path = ( "$root", "$base" );
264 }
8d116b20 265 $config->{INCLUDE_PATH} = \@include_path;
e2bbd784 266 }
8d116b20 267
268
62728755 269
8cd017a8 270 # if we're debugging and/or the TIMER option is set, then we install
271 # Template::Timer as a custom CONTEXT object, but only if we haven't
272 # already got a custom CONTEXT defined
273
dccfa0ff 274 if ( $config->{TIMER} ) {
94b3529a 275 if ( $config->{CONTEXT} ) {
8cd017a8 276 $c->log->error(
bec996dc 277 'Cannot use Template::Timer - a TT CONFIG is already defined'
278 );
8cd017a8 279 }
280 else {
94b3529a 281 $config->{CONTEXT} = Template::Timer->new(%$config);
8cd017a8 282 }
283 }
284
94b3529a 285 if ( $c->debug && $config->{DUMP_CONFIG} ) {
8cd017a8 286 use Data::Dumper;
94b3529a 287 $c->log->debug( "TT Config: ", Dumper($config) );
62728755 288 }
d7e9a1b2 289 if ( $config->{PROVIDERS} ) {
290 my @providers = ();
291 if ( ref($config->{PROVIDERS}) eq 'ARRAY') {
292 foreach my $p (@{$config->{PROVIDERS}}) {
293 my $pname = $p->{name};
294 eval "require Template::Provider::$pname";
295 if(!$@) {
296 push @providers, "Template::Provider::${pname}"->new($p->{args});
297 }
298 }
299 }
300 delete $config->{PROVIDERS};
301 if(@providers) {
302 $config->{LOAD_TEMPLATES} = \@providers;
303 }
304 }
305
e3a53d5b 306 my $self = $class->NEXT::new(
8cd017a8 307 $c,
bec996dc 308 { template => Template->new($config) || do {
8cd017a8 309 my $error = Template->error();
310 $c->log->error($error);
311 $c->error($error);
312 return undef;
bec996dc 313 },
314 %{$config},
48d69085 315 },
8cd017a8 316 );
8d116b20 317 $self->include_path($config->{INCLUDE_PATH});
e3a53d5b 318 $self->config($config);
319
320 return $self;
8077080c 321}
322
2774dc77 323=item process
8077080c 324
722fede4 325Renders the template specified in C<$c-E<gt>stash-E<gt>{template}> or
326C<$c-E<gt>request-E<gt>match>. Template variables are set up from the
327contents of C<$c-E<gt>stash>, augmented with C<base> set to
328C<$c-E<gt>req-E<gt>base>, C<c> to C<$c> and C<name> to
8cd017a8 329C<$c-E<gt>config-E<gt>{name}>. Alternately, the C<CATALYST_VAR>
330configuration item can be defined to specify the name of a template
331variable through which the context reference (C<$c>) can be accessed.
332In this case, the C<c>, C<base> and C<name> variables are omitted.
333Output is stored in C<$c-E<gt>response-E<gt>output>.
8077080c 334
335=cut
336
337sub process {
338 my ( $self, $c ) = @_;
23042c3c 339
7d8aa5ec 340 my $template = $c->stash->{template}
850ee226 341 || ( $c->request->match || $c->request->action )
342 . $self->config->{TEMPLATE_EXTENSION};
23042c3c 343
344 unless ($template) {
8077080c 345 $c->log->debug('No template specified for rendering') if $c->debug;
346 return 0;
347 }
23042c3c 348
349 $c->log->debug(qq/Rendering template "$template"/) if $c->debug;
23042c3c 350
8cd017a8 351 my $output;
850ee226 352 my $vars = { $self->template_vars($c) };
353
bec996dc 354 unshift @{ $self->include_path },
850ee226 355 @{ $c->stash->{additional_template_paths} }
356 if ref $c->stash->{additional_template_paths};
8cd017a8 357 unless ( $self->template->process( $template, $vars, \$output ) ) {
8077080c 358 my $error = $self->template->error;
359 $error = qq/Couldn't render template "$error"/;
360 $c->log->error($error);
becb7ac2 361 $c->error($error);
23042c3c 362 return 0;
363 }
bec996dc 364 splice @{ $self->include_path }, 0,
850ee226 365 scalar @{ $c->stash->{additional_template_paths} }
366 if ref $c->stash->{additional_template_paths};
bec996dc 367
23042c3c 368 unless ( $c->response->content_type ) {
369 $c->response->content_type('text/html; charset=utf-8');
8077080c 370 }
23042c3c 371
372 $c->response->body($output);
373
8077080c 374 return 1;
375}
376
850ee226 377=item template_vars
378
379Returns a list of keys/values to be used as the variables in the
380template.
381
382=cut
383
384sub template_vars {
385 my ( $self, $c ) = @_;
386
387 my $cvar = $self->config->{CATALYST_VAR};
388
389 defined $cvar
390 ? ( $cvar => $c )
391 : (
392 c => $c,
393 base => $c->req->base,
394 name => $c->config->{name}
395 ),
396 %{ $c->stash() }
397
398}
2774dc77 399=item config
8077080c 400
8cd017a8 401This method allows your view subclass to pass additional settings to
4729c102 402the TT configuration hash, or to set the options as below:
403
404=over 2
405
406=item C<CATALYST_VAR>
407
408Allows you to change the name of the Catalyst context object. If set, it will also
409remove the base and name aliases, so you will have access them through <context>.
410
411For example:
412
413 MyApp->config({
414 name => 'MyApp',
7d8aa5ec 415 root => MyApp->path_to('root'),
4729c102 416 'V::TT' => {
417 CATALYST_VAR => 'Catalyst',
418 },
419 });
420
421F<message.tt2>:
422
423 The base is [% Catalyst.req.base %]
424 The name is [% Catalyst.config.name %]
425
426=item C<TIMER>
427
428If you have configured Catalyst for debug output, and turned on the TIMER setting,
429C<Catalyst::View::TT> will enable profiling of template processing
430(using L<Template::Timer>). This will embed HTML comments in the
431output from your templates, such as:
432
433 <!-- TIMER START: process mainmenu/mainmenu.ttml -->
434 <!-- TIMER START: include mainmenu/cssindex.tt -->
435 <!-- TIMER START: process mainmenu/cssindex.tt -->
436 <!-- TIMER END: process mainmenu/cssindex.tt (0.017279 seconds) -->
437 <!-- TIMER END: include mainmenu/cssindex.tt (0.017401 seconds) -->
438
439 ....
440
441 <!-- TIMER END: process mainmenu/footer.tt (0.003016 seconds) -->
442
443
be7c1afa 444=item C<TEMPLATE_EXTENSION>
4729c102 445
747f2b6d 446a sufix to add when looking for templates bases on the C<match> method in L<Catalyst::Request>.
4729c102 447
448For example:
449
450 package MyApp::C::Test;
451 sub test : Local { .. }
452
453Would by default look for a template in <root>/test/test. If you set TEMPLATE_EXTENSION to '.tt', it will look for
454<root>/test/test.tt.
455
456=back
8077080c 457
2774dc77 458=back
8077080c 459
8cd017a8 460=head2 HELPERS
461
462The L<Catalyst::Helper::View::TT> and
463L<Catalyst::Helper::View::TTSite> helper modules are provided to create
464your view module. There are invoked by the F<myapp_create.pl> script:
465
466 $ script/myapp_create.pl view TT TT
467
468 $ script/myapp_create.pl view TT TTSite
469
470The L<Catalyst::Helper::View::TT> module creates a basic TT view
471module. The L<Catalyst::Helper::View::TTSite> module goes a little
472further. It also creates a default set of templates to get you
473started. It also configures the view module to locate the templates
474automatically.
475
8077080c 476=head1 SEE ALSO
477
8cd017a8 478L<Catalyst>, L<Catalyst::Helper::View::TT>,
479L<Catalyst::Helper::View::TTSite>, L<Template::Manual>
8077080c 480
c0eb0527 481=head1 AUTHORS
8077080c 482
483Sebastian Riedel, C<sri@cpan.org>
c0eb0527 484
d938377b 485Marcus Ramberg, C<mramberg@cpan.org>
c0eb0527 486
722fede4 487Jesse Sheidlower, C<jester@panix.com>
c0eb0527 488
8cd017a8 489Andy Wardley, C<abw@cpan.org>
8077080c 490
491=head1 COPYRIGHT
492
2774dc77 493This program is free software, you can redistribute it and/or modify it
494under the same terms as Perl itself.
8077080c 495
496=cut
497
4981;