Back out 11797 - misscommit to wrong branch. svn merge -r11797:11796 .
[catagits/Catalyst-View-TT.git] / lib / Catalyst / View / TT.pm
CommitLineData
8077080c 1package Catalyst::View::TT;
2
3use strict;
6229ce0d 4use warnings;
5
7d8aa5ec 6use base qw/Catalyst::View/;
9e739e1e 7use Data::Dump 'dump';
8077080c 8use Template;
9use Template::Timer;
e8693f1b 10use MRO::Compat;
8077080c 11
f59953e1 12our $VERSION = '0.31';
8077080c 13
14__PACKAGE__->mk_accessors('template');
e2bbd784 15__PACKAGE__->mk_accessors('include_path');
8077080c 16
9d574573 17*paths = \&include_path;
18
8077080c 19=head1 NAME
20
21Catalyst::View::TT - Template View Class
22
23=head1 SYNOPSIS
24
bd4ee63a 25# use the helper to create your View
ad50568f 26
722fede4 27 myapp_create.pl view TT TT
8077080c 28
6175bba7 29# configure in lib/MyApp.pm (Could be set from configfile instead)
8cd017a8 30
ae035767 31 MyApp->config(
8cd017a8 32 name => 'MyApp',
e1c15a53 33 root => MyApp->path_to('root'),
ae035767 34 'View::TT' => {
8cd017a8 35 # any TT configurations items go here
36 INCLUDE_PATH => [
f5b61ad7 37 MyApp->path_to( 'root', 'src' ),
38 MyApp->path_to( 'root', 'lib' ),
8cd017a8 39 ],
6175bba7 40 TEMPLATE_EXTENSION => '.tt',
41 CATALYST_VAR => 'c',
42 TIMER => 0,
43 # Not set by default
07571b2f 44 PRE_PROCESS => 'config/main',
45 WRAPPER => 'site/wrapper',
8cd017a8 46 },
ae035767 47 );
f5b61ad7 48
6229ce0d 49# render view from lib/MyApp.pm or lib/MyApp::Controller::SomeController.pm
f5b61ad7 50
8cd017a8 51 sub message : Global {
94b3529a 52 my ( $self, $c ) = @_;
53 $c->stash->{template} = 'message.tt2';
54 $c->stash->{message} = 'Hello World!';
c18eae32 55 $c->forward( $c->view('TT') );
8cd017a8 56 }
8077080c 57
8cd017a8 58# access variables from template
59
60 The message is: [% message %].
f5b61ad7 61
8cd017a8 62 # example when CATALYST_VAR is set to 'Catalyst'
f5b61ad7 63 Context is [% Catalyst %]
64 The base is [% Catalyst.req.base %]
65 The name is [% Catalyst.config.name %]
66
8cd017a8 67 # example when CATALYST_VAR isn't set
68 Context is [% c %]
69 The base is [% base %]
70 The name is [% name %]
71
bd4ee63a 72=cut
73
74sub _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
86sub 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} ) {
9e739e1e 122 $c->log->debug( "TT Config: ", dump($config) );
bd4ee63a 123 }
6175bba7 124
e8693f1b 125 my $self = $class->next::method(
f5b61ad7 126 $c, { %$config },
6175bba7 127 );
128
129 # Set base include paths. Local'd in render if needed
130 $self->include_path($config->{INCLUDE_PATH});
f5b61ad7 131
6175bba7 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.
f5b61ad7 137
6175bba7 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
bd4ee63a 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 {
51199593 155 if($pname =~ s/^\+//) {
156 $prov = $pname;
157 }
158 else
159 {
160 $prov .= "::$pname";
161 }
6175bba7 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 }
bd4ee63a 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 }
f5b61ad7 186
187 $self->{template} =
bd4ee63a 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
199sub 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
cad820fe 210 my $output = eval { $self->render($c, $template) };
211 if (my $err = $@) {
212 my $error = qq/Couldn't render template "$template"/;
bd4ee63a 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
227sub render {
228 my ($self, $c, $template, $args) = @_;
229
cbb149dc 230 $c->log->debug(qq/Rendering template "$template"/) if $c && $c->debug;
bd4ee63a 231
232 my $output;
f5b61ad7 233 my $vars = {
bd4ee63a 234 (ref $args eq 'HASH' ? %$args : %{ $c->stash() }),
235 $self->template_vars($c)
236 };
237
f5b61ad7 238 local $self->{include_path} =
bd4ee63a 239 [ @{ $vars->{additional_template_paths} }, @{ $self->{include_path} } ]
240 if ref $vars->{additional_template_paths};
241
cad820fe 242 $self->template->process( $template, $vars, \$output )
243 or die $self->template->error;
244 return $output;
bd4ee63a 245}
246
247sub template_vars {
248 my ( $self, $c ) = @_;
249
cbb149dc 250 return () unless $c;
bd4ee63a 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
2631;
264
265__END__
266
8cd017a8 267=head1 DESCRIPTION
268
269This is the Catalyst view class for the L<Template Toolkit|Template>.
270Your application should defined a view class which is a subclass of
271this module. The easiest way to achieve this is using the
272F<myapp_create.pl> script (where F<myapp> should be replaced with
273whatever your application is called). This script is created as part
274of the Catalyst setup.
275
276 $ script/myapp_create.pl view TT TT
277
6229ce0d 278This creates a MyApp::View::TT.pm module in the F<lib> directory (again,
8cd017a8 279replacing C<MyApp> with the name of your application) which looks
280something like this:
281
6229ce0d 282 package FooBar::View::TT;
f5b61ad7 283
8cd017a8 284 use strict;
c18eae32 285 use warnings;
f5b61ad7 286
c18eae32 287 use base 'Catalyst::View::TT';
8077080c 288
289 __PACKAGE__->config->{DEBUG} = 'all';
290
8cd017a8 291Now you can modify your action handlers in the main application and/or
292controllers to forward to your view class. You might choose to do this
293in the end() method, for example, to automatically forward all actions
294to the TT view class.
722fede4 295
8cd017a8 296 # In MyApp or MyApp::Controller::SomeController
f5b61ad7 297
8cd017a8 298 sub end : Private {
94b3529a 299 my( $self, $c ) = @_;
c18eae32 300 $c->forward( $c->view('TT') );
8cd017a8 301 }
8077080c 302
8cd017a8 303=head2 CONFIGURATION
4687ac0d 304
8cd017a8 305There are a three different ways to configure your view class. The
306first way is to call the C<config()> method in the view subclass. This
307happens when the module is first loaded.
8077080c 308
6229ce0d 309 package MyApp::View::TT;
f5b61ad7 310
8cd017a8 311 use strict;
312 use base 'Catalyst::View::TT';
722fede4 313
6229ce0d 314 MyApp::View::TT->config({
7d8aa5ec 315 INCLUDE_PATH => [
316 MyApp->path_to( 'root', 'templates', 'lib' ),
317 MyApp->path_to( 'root', 'templates', 'src' ),
318 ],
8cd017a8 319 PRE_PROCESS => 'config/main',
320 WRAPPER => 'site/wrapper',
321 });
322
323The second way is to define a C<new()> method in your view subclass.
324This performs the configuration when the view object is created,
325shortly after being loaded. Remember to delegate to the base class
e8693f1b 326C<new()> method (via C<$self-E<gt>next::method()> in the example below) after
8cd017a8 327performing any configuration.
328
329 sub new {
330 my $self = shift;
331 $self->config({
7d8aa5ec 332 INCLUDE_PATH => [
333 MyApp->path_to( 'root', 'templates', 'lib' ),
334 MyApp->path_to( 'root', 'templates', 'src' ),
335 ],
8cd017a8 336 PRE_PROCESS => 'config/main',
337 WRAPPER => 'site/wrapper',
338 });
e8693f1b 339 return $self->next::method(@_);
8cd017a8 340 }
f5b61ad7 341
94b3529a 342The final, and perhaps most direct way, is to define a class
8cd017a8 343item in your main application configuration, again by calling the
ad50568f 344ubiquitous C<config()> method. The items in the class hash are
8cd017a8 345added to those already defined by the above two methods. This happens
346in the base class new() method (which is one reason why you must
f5b61ad7 347remember to call it via C<MRO::Compat> if you redefine the C<new()>
e8693f1b 348method in a subclass).
8cd017a8 349
350 package MyApp;
f5b61ad7 351
8cd017a8 352 use strict;
353 use Catalyst;
f5b61ad7 354
8cd017a8 355 MyApp->config({
356 name => 'MyApp',
7d8aa5ec 357 root => MyApp->path_to('root'),
6229ce0d 358 'View::TT' => {
7d8aa5ec 359 INCLUDE_PATH => [
360 MyApp->path_to( 'root', 'templates', 'lib' ),
361 MyApp->path_to( 'root', 'templates', 'src' ),
362 ],
8cd017a8 363 PRE_PROCESS => 'config/main',
364 WRAPPER => 'site/wrapper',
365 },
366 });
367
368Note that any configuration items defined by one of the earlier
369methods will be overwritten by items of the same name provided by the
f5b61ad7 370latter methods.
8cd017a8 371
e2bbd784 372=head2 DYNAMIC INCLUDE_PATH
373
f7dfca06 374Sometimes it is desirable to modify INCLUDE_PATH for your templates at run time.
f5b61ad7 375
f7dfca06 376Additional paths can be added to the start of INCLUDE_PATH via the stash as
377follows:
378
379 $c->stash->{additional_template_paths} =
380 [$c->config->{root} . '/test_include_path'];
381
382If you need to add paths to the end of INCLUDE_PATH, there is also an
383include_path() accessor available:
384
385 push( @{ $c->view('TT')->include_path }, qw/path/ );
386
387Note that if you use include_path() to add extra paths to INCLUDE_PATH, you
388MUST check for duplicate paths. Without such checking, the above code will add
389"path" to INCLUDE_PATH at every request, causing a memory leak.
390
391A safer approach is to use include_path() to overwrite the array of paths
392rather than adding to it. This eliminates both the need to perform duplicate
393checking and the chance of a memory leak:
e2bbd784 394
f7dfca06 395 @{ $c->view('TT')->include_path } = qw/path another_path/;
e2bbd784 396
f5b61ad7 397If you are calling C<render> directly then you can specify dynamic paths by
9d574573 398having a C<additional_template_paths> key with a value of additonal directories
1bc9fc55 399to search. See L<CAPTURING TEMPLATE OUTPUT> for an example showing this.
400
8cd017a8 401=head2 RENDERING VIEWS
402
403The view plugin renders the template specified in the C<template>
f5b61ad7 404item in the stash.
8cd017a8 405
406 sub message : Global {
94b3529a 407 my ( $self, $c ) = @_;
408 $c->stash->{template} = 'message.tt2';
6229ce0d 409 $c->forward( $c->view('TT') );
8cd017a8 410 }
722fede4 411
c969f23a 412If a stash item isn't defined, then it instead uses the
413stringification of the action dispatched to (as defined by $c->action)
414in the above example, this would be C<message>, but because the default
415is to append '.tt', it would load C<root/message.tt>.
8cd017a8 416
417The items defined in the stash are passed to the Template Toolkit for
418use as template variables.
419
8cd017a8 420 sub default : Private {
94b3529a 421 my ( $self, $c ) = @_;
422 $c->stash->{template} = 'message.tt2';
423 $c->stash->{message} = 'Hello World!';
6229ce0d 424 $c->forward( $c->view('TT') );
8cd017a8 425 }
7b592fc7 426
8cd017a8 427A number of other template variables are also added:
8077080c 428
8cd017a8 429 c A reference to the context object, $c
430 base The URL base, from $c->req->base()
431 name The application name, from $c->config->{ name }
432
433These can be accessed from the template in the usual way:
434
435<message.tt2>:
436
437 The message is: [% message %]
438 The base is [% base %]
439 The name is [% name %]
440
8cd017a8 441
1bc9fc55 442The output generated by the template is stored in C<< $c->response->body >>.
443
444=head2 CAPTURING TEMPLATE OUTPUT
445
446If you wish to use the output of a template for some other purpose than
447displaying in the response, e.g. for sending an email, this is possible using
448L<Catalyst::Plugin::Email> and the L<render> method:
449
450 sub send_email : Local {
451 my ($self, $c) = @_;
f5b61ad7 452
1bc9fc55 453 $c->email(
454 header => [
455 To => 'me@localhost',
456 Subject => 'A TT Email',
457 ],
458 body => $c->view('TT')->render($c, 'email.tt', {
9d574573 459 additional_template_paths => [ $c->config->{root} . '/email_templates'],
1bc9fc55 460 email_tmpl_param1 => 'foo'
461 }
462 ),
463 );
464 # Redirect or display a message
465 }
8cd017a8 466
467=head2 TEMPLATE PROFILING
468
1bc9fc55 469See L<C<TIMER>> property of the L<config> method.
470
caa61517 471=head2 METHODS
8077080c 472
b63ddd04 473=head2 new
2774dc77 474
f5b61ad7 475The constructor for the TT view. Sets up the template provider,
2774dc77 476and reads the application config.
477
81b7f2a2 478=head2 process($c)
8077080c 479
1bc9fc55 480Renders the template specified in C<< $c->stash->{template} >> or
ad50568f 481C<< $c->action >> (the private name of the matched action). Calls L<render> to
1bc9fc55 482perform actual rendering. Output is stored in C<< $c->response->body >>.
8077080c 483
81b7f2a2 484It is possible to forward to the process method of a TT view from inside
485Catalyst like this:
486
487 $c->forward('View::TT');
488
489N.B. This is usually done automatically by L<Catalyst::Action::RenderView>.
f2ee4d6c 490
b63ddd04 491=head2 render($c, $template, \%args)
1bc9fc55 492
cad820fe 493Renders the given template and returns output. Throws a L<Template::Exception>
f5b61ad7 494object upon error.
1bc9fc55 495
f5b61ad7 496The template variables are set to C<%$args> if $args is a hashref, or
497$C<< $c->stash >> otherwise. In either case the variables are augmented with
1bc9fc55 498C<base> set to C< << $c->req->base >>, C<c> to C<$c> and C<name> to
499C<< $c->config->{name} >>. Alternately, the C<CATALYST_VAR> configuration item
500can be defined to specify the name of a template variable through which the
501context reference (C<$c>) can be accessed. In this case, the C<c>, C<base> and
502C<name> variables are omitted.
503
f5b61ad7 504C<$template> can be anything that Template::process understands how to
1bc9fc55 505process, including the name of a template file or a reference to a test string.
506See L<Template::process|Template/process> for a full list of supported formats.
507
f59953e1 508To use the render method outside of your Catalyst app, just pass a undef context.
cbb149dc 509This can be useful for tests, for instance.
510
81b7f2a2 511It is possible to forward to the render method of a TT view from inside Catalyst
512to render page fragments like this:
513
514 my $fragment = $c->forward("View::TT", "render", $template_name, $c->stash->{fragment_data});
f2ee4d6c 515
b63ddd04 516=head2 template_vars
850ee226 517
1bc9fc55 518Returns a list of keys/values to be used as the catalyst variables in the
850ee226 519template.
520
b63ddd04 521=head2 config
8077080c 522
8cd017a8 523This method allows your view subclass to pass additional settings to
4729c102 524the TT configuration hash, or to set the options as below:
525
b63ddd04 526=head2 paths
527
528The list of paths TT will look for templates in.
4729c102 529
f5b61ad7 530=head2 C<CATALYST_VAR>
4729c102 531
532Allows you to change the name of the Catalyst context object. If set, it will also
533remove the base and name aliases, so you will have access them through <context>.
534
535For example:
536
537 MyApp->config({
538 name => 'MyApp',
7d8aa5ec 539 root => MyApp->path_to('root'),
6229ce0d 540 'View::TT' => {
4729c102 541 CATALYST_VAR => 'Catalyst',
542 },
543 });
544
545F<message.tt2>:
546
547 The base is [% Catalyst.req.base %]
548 The name is [% Catalyst.config.name %]
549
b63ddd04 550=head2 C<TIMER>
4729c102 551
552If you have configured Catalyst for debug output, and turned on the TIMER setting,
553C<Catalyst::View::TT> will enable profiling of template processing
554(using L<Template::Timer>). This will embed HTML comments in the
555output from your templates, such as:
556
557 <!-- TIMER START: process mainmenu/mainmenu.ttml -->
558 <!-- TIMER START: include mainmenu/cssindex.tt -->
559 <!-- TIMER START: process mainmenu/cssindex.tt -->
560 <!-- TIMER END: process mainmenu/cssindex.tt (0.017279 seconds) -->
561 <!-- TIMER END: include mainmenu/cssindex.tt (0.017401 seconds) -->
562
563 ....
564
565 <!-- TIMER END: process mainmenu/footer.tt (0.003016 seconds) -->
566
567
b63ddd04 568=head2 C<TEMPLATE_EXTENSION>
4729c102 569
747f2b6d 570a sufix to add when looking for templates bases on the C<match> method in L<Catalyst::Request>.
4729c102 571
572For example:
573
6229ce0d 574 package MyApp::Controller::Test;
f5b61ad7 575 sub test : Local { .. }
4729c102 576
577Would by default look for a template in <root>/test/test. If you set TEMPLATE_EXTENSION to '.tt', it will look for
578<root>/test/test.tt.
579
51199593 580=head2 C<PROVIDERS>
581
582Allows you to specify the template providers that TT will use.
583
584 MyApp->config({
585 name => 'MyApp',
586 root => MyApp->path_to('root'),
6229ce0d 587 'View::TT' => {
51199593 588 PROVIDERS => [
589 {
590 name => 'DBI',
591 args => {
592 DBI_DSN => 'dbi:DB2:books',
593 DBI_USER=> 'foo'
594 }
595 }, {
596 name => '_file_',
597 args => {}
598 }
599 ]
600 },
601 });
602
603The 'name' key should correspond to the class name of the provider you
604want to use. The _file_ name is a special case that represents the default
605TT file-based provider. By default the name is will be prefixed with
606'Template::Provider::'. You can fully qualify the name by using a unary
607plus:
608
609 name => '+MyApp::Provider::Foo'
610
6175bba7 611You can also specify the 'copy_config' key as an arrayref, to copy those keys
612from the general config, into the config for the provider:
f5b61ad7 613
6175bba7 614 DEFAULT_ENCODING => 'utf-8',
615 PROVIDERS => [
616 {
617 name => 'Encoding',
618 copy_config => [qw(DEFAULT_ENCODING INCLUDE_PATH)]
619 }
620 ]
f5b61ad7 621
6175bba7 622This can prove useful when you want to use the additional_template_paths hack
623in your own provider, or if you need to use Template::Provider::Encoding
624
8cd017a8 625=head2 HELPERS
626
627The L<Catalyst::Helper::View::TT> and
628L<Catalyst::Helper::View::TTSite> helper modules are provided to create
629your view module. There are invoked by the F<myapp_create.pl> script:
630
631 $ script/myapp_create.pl view TT TT
632
633 $ script/myapp_create.pl view TT TTSite
634
635The L<Catalyst::Helper::View::TT> module creates a basic TT view
636module. The L<Catalyst::Helper::View::TTSite> module goes a little
637further. It also creates a default set of templates to get you
638started. It also configures the view module to locate the templates
639automatically.
640
02c64502 641=head1 NOTES
642
643If you are using the L<CGI> module inside your templates, you will
644experience that the Catalyst server appears to hang while rendering
645the web page. This is due to the debug mode of L<CGI> (which is
646waiting for input in the terminal window). Turning off the
647debug mode using the "-no_debug" option solves the
648problem, eg.:
649
650 [% USE CGI('-no_debug') %]
651
8077080c 652=head1 SEE ALSO
653
8cd017a8 654L<Catalyst>, L<Catalyst::Helper::View::TT>,
655L<Catalyst::Helper::View::TTSite>, L<Template::Manual>
8077080c 656
c0eb0527 657=head1 AUTHORS
8077080c 658
659Sebastian Riedel, C<sri@cpan.org>
c0eb0527 660
d938377b 661Marcus Ramberg, C<mramberg@cpan.org>
c0eb0527 662
722fede4 663Jesse Sheidlower, C<jester@panix.com>
c0eb0527 664
8cd017a8 665Andy Wardley, C<abw@cpan.org>
8077080c 666
667=head1 COPYRIGHT
668
f5b61ad7 669This program is free software. You can redistribute it and/or modify it
2774dc77 670under the same terms as Perl itself.
8077080c 671
672=cut