Fixed the run-on sentence in License; trailing whitespace removal in Helper
[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
6229ce0d 12our $VERSION = '0.30';
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
210 my $output = $self->render($c, $template);
211
212 if (UNIVERSAL::isa($output, 'Template::Exception')) {
82b741a3 213 my $error = qq/Couldn't render template "$output"/;
bd4ee63a 214 $c->log->error($error);
215 $c->error($error);
216 return 0;
217 }
218
219 unless ( $c->response->content_type ) {
220 $c->response->content_type('text/html; charset=utf-8');
221 }
222
223 $c->response->body($output);
224
225 return 1;
226}
227
228sub render {
229 my ($self, $c, $template, $args) = @_;
230
231 $c->log->debug(qq/Rendering template "$template"/) if $c->debug;
232
233 my $output;
f5b61ad7 234 my $vars = {
bd4ee63a 235 (ref $args eq 'HASH' ? %$args : %{ $c->stash() }),
236 $self->template_vars($c)
237 };
238
f5b61ad7 239 local $self->{include_path} =
bd4ee63a 240 [ @{ $vars->{additional_template_paths} }, @{ $self->{include_path} } ]
241 if ref $vars->{additional_template_paths};
242
243 unless ($self->template->process( $template, $vars, \$output ) ) {
f5b61ad7 244 return $self->template->error;
bd4ee63a 245 } else {
246 return $output;
247 }
248}
249
250sub template_vars {
251 my ( $self, $c ) = @_;
252
253 my $cvar = $self->config->{CATALYST_VAR};
254
255 defined $cvar
256 ? ( $cvar => $c )
257 : (
258 c => $c,
259 base => $c->req->base,
260 name => $c->config->{name}
261 )
262}
263
264
2651;
266
267__END__
268
8cd017a8 269=head1 DESCRIPTION
270
271This is the Catalyst view class for the L<Template Toolkit|Template>.
272Your application should defined a view class which is a subclass of
273this module. The easiest way to achieve this is using the
274F<myapp_create.pl> script (where F<myapp> should be replaced with
275whatever your application is called). This script is created as part
276of the Catalyst setup.
277
278 $ script/myapp_create.pl view TT TT
279
6229ce0d 280This creates a MyApp::View::TT.pm module in the F<lib> directory (again,
8cd017a8 281replacing C<MyApp> with the name of your application) which looks
282something like this:
283
6229ce0d 284 package FooBar::View::TT;
f5b61ad7 285
8cd017a8 286 use strict;
c18eae32 287 use warnings;
f5b61ad7 288
c18eae32 289 use base 'Catalyst::View::TT';
8077080c 290
291 __PACKAGE__->config->{DEBUG} = 'all';
292
8cd017a8 293Now you can modify your action handlers in the main application and/or
294controllers to forward to your view class. You might choose to do this
295in the end() method, for example, to automatically forward all actions
296to the TT view class.
722fede4 297
8cd017a8 298 # In MyApp or MyApp::Controller::SomeController
f5b61ad7 299
8cd017a8 300 sub end : Private {
94b3529a 301 my( $self, $c ) = @_;
c18eae32 302 $c->forward( $c->view('TT') );
8cd017a8 303 }
8077080c 304
8cd017a8 305=head2 CONFIGURATION
4687ac0d 306
8cd017a8 307There are a three different ways to configure your view class. The
308first way is to call the C<config()> method in the view subclass. This
309happens when the module is first loaded.
8077080c 310
6229ce0d 311 package MyApp::View::TT;
f5b61ad7 312
8cd017a8 313 use strict;
314 use base 'Catalyst::View::TT';
722fede4 315
6229ce0d 316 MyApp::View::TT->config({
7d8aa5ec 317 INCLUDE_PATH => [
318 MyApp->path_to( 'root', 'templates', 'lib' ),
319 MyApp->path_to( 'root', 'templates', 'src' ),
320 ],
8cd017a8 321 PRE_PROCESS => 'config/main',
322 WRAPPER => 'site/wrapper',
323 });
324
325The second way is to define a C<new()> method in your view subclass.
326This performs the configuration when the view object is created,
327shortly after being loaded. Remember to delegate to the base class
e8693f1b 328C<new()> method (via C<$self-E<gt>next::method()> in the example below) after
8cd017a8 329performing any configuration.
330
331 sub new {
332 my $self = shift;
333 $self->config({
7d8aa5ec 334 INCLUDE_PATH => [
335 MyApp->path_to( 'root', 'templates', 'lib' ),
336 MyApp->path_to( 'root', 'templates', 'src' ),
337 ],
8cd017a8 338 PRE_PROCESS => 'config/main',
339 WRAPPER => 'site/wrapper',
340 });
e8693f1b 341 return $self->next::method(@_);
8cd017a8 342 }
f5b61ad7 343
94b3529a 344The final, and perhaps most direct way, is to define a class
8cd017a8 345item in your main application configuration, again by calling the
ad50568f 346ubiquitous C<config()> method. The items in the class hash are
8cd017a8 347added to those already defined by the above two methods. This happens
348in the base class new() method (which is one reason why you must
f5b61ad7 349remember to call it via C<MRO::Compat> if you redefine the C<new()>
e8693f1b 350method in a subclass).
8cd017a8 351
352 package MyApp;
f5b61ad7 353
8cd017a8 354 use strict;
355 use Catalyst;
f5b61ad7 356
8cd017a8 357 MyApp->config({
358 name => 'MyApp',
7d8aa5ec 359 root => MyApp->path_to('root'),
6229ce0d 360 'View::TT' => {
7d8aa5ec 361 INCLUDE_PATH => [
362 MyApp->path_to( 'root', 'templates', 'lib' ),
363 MyApp->path_to( 'root', 'templates', 'src' ),
364 ],
8cd017a8 365 PRE_PROCESS => 'config/main',
366 WRAPPER => 'site/wrapper',
367 },
368 });
369
370Note that any configuration items defined by one of the earlier
371methods will be overwritten by items of the same name provided by the
f5b61ad7 372latter methods.
8cd017a8 373
e2bbd784 374=head2 DYNAMIC INCLUDE_PATH
375
f7dfca06 376Sometimes it is desirable to modify INCLUDE_PATH for your templates at run time.
f5b61ad7 377
f7dfca06 378Additional paths can be added to the start of INCLUDE_PATH via the stash as
379follows:
380
381 $c->stash->{additional_template_paths} =
382 [$c->config->{root} . '/test_include_path'];
383
384If you need to add paths to the end of INCLUDE_PATH, there is also an
385include_path() accessor available:
386
387 push( @{ $c->view('TT')->include_path }, qw/path/ );
388
389Note that if you use include_path() to add extra paths to INCLUDE_PATH, you
390MUST check for duplicate paths. Without such checking, the above code will add
391"path" to INCLUDE_PATH at every request, causing a memory leak.
392
393A safer approach is to use include_path() to overwrite the array of paths
394rather than adding to it. This eliminates both the need to perform duplicate
395checking and the chance of a memory leak:
e2bbd784 396
f7dfca06 397 @{ $c->view('TT')->include_path } = qw/path another_path/;
e2bbd784 398
f5b61ad7 399If you are calling C<render> directly then you can specify dynamic paths by
9d574573 400having a C<additional_template_paths> key with a value of additonal directories
1bc9fc55 401to search. See L<CAPTURING TEMPLATE OUTPUT> for an example showing this.
402
8cd017a8 403=head2 RENDERING VIEWS
404
405The view plugin renders the template specified in the C<template>
f5b61ad7 406item in the stash.
8cd017a8 407
408 sub message : Global {
94b3529a 409 my ( $self, $c ) = @_;
410 $c->stash->{template} = 'message.tt2';
6229ce0d 411 $c->forward( $c->view('TT') );
8cd017a8 412 }
722fede4 413
c969f23a 414If a stash item isn't defined, then it instead uses the
415stringification of the action dispatched to (as defined by $c->action)
416in the above example, this would be C<message>, but because the default
417is to append '.tt', it would load C<root/message.tt>.
8cd017a8 418
419The items defined in the stash are passed to the Template Toolkit for
420use as template variables.
421
8cd017a8 422 sub default : Private {
94b3529a 423 my ( $self, $c ) = @_;
424 $c->stash->{template} = 'message.tt2';
425 $c->stash->{message} = 'Hello World!';
6229ce0d 426 $c->forward( $c->view('TT') );
8cd017a8 427 }
7b592fc7 428
8cd017a8 429A number of other template variables are also added:
8077080c 430
8cd017a8 431 c A reference to the context object, $c
432 base The URL base, from $c->req->base()
433 name The application name, from $c->config->{ name }
434
435These can be accessed from the template in the usual way:
436
437<message.tt2>:
438
439 The message is: [% message %]
440 The base is [% base %]
441 The name is [% name %]
442
8cd017a8 443
1bc9fc55 444The output generated by the template is stored in C<< $c->response->body >>.
445
446=head2 CAPTURING TEMPLATE OUTPUT
447
448If you wish to use the output of a template for some other purpose than
449displaying in the response, e.g. for sending an email, this is possible using
450L<Catalyst::Plugin::Email> and the L<render> method:
451
452 sub send_email : Local {
453 my ($self, $c) = @_;
f5b61ad7 454
1bc9fc55 455 $c->email(
456 header => [
457 To => 'me@localhost',
458 Subject => 'A TT Email',
459 ],
460 body => $c->view('TT')->render($c, 'email.tt', {
9d574573 461 additional_template_paths => [ $c->config->{root} . '/email_templates'],
1bc9fc55 462 email_tmpl_param1 => 'foo'
463 }
464 ),
465 );
466 # Redirect or display a message
467 }
8cd017a8 468
469=head2 TEMPLATE PROFILING
470
1bc9fc55 471See L<C<TIMER>> property of the L<config> method.
472
caa61517 473=head2 METHODS
8077080c 474
b63ddd04 475=head2 new
2774dc77 476
f5b61ad7 477The constructor for the TT view. Sets up the template provider,
2774dc77 478and reads the application config.
479
b63ddd04 480=head2 process
8077080c 481
1bc9fc55 482Renders the template specified in C<< $c->stash->{template} >> or
ad50568f 483C<< $c->action >> (the private name of the matched action). Calls L<render> to
1bc9fc55 484perform actual rendering. Output is stored in C<< $c->response->body >>.
8077080c 485
b63ddd04 486=head2 render($c, $template, \%args)
1bc9fc55 487
488Renders the given template and returns output, or a L<Template::Exception>
f5b61ad7 489object upon error.
1bc9fc55 490
f5b61ad7 491The template variables are set to C<%$args> if $args is a hashref, or
492$C<< $c->stash >> otherwise. In either case the variables are augmented with
1bc9fc55 493C<base> set to C< << $c->req->base >>, C<c> to C<$c> and C<name> to
494C<< $c->config->{name} >>. Alternately, the C<CATALYST_VAR> configuration item
495can be defined to specify the name of a template variable through which the
496context reference (C<$c>) can be accessed. In this case, the C<c>, C<base> and
497C<name> variables are omitted.
498
f5b61ad7 499C<$template> can be anything that Template::process understands how to
1bc9fc55 500process, including the name of a template file or a reference to a test string.
501See L<Template::process|Template/process> for a full list of supported formats.
502
b63ddd04 503=head2 template_vars
850ee226 504
1bc9fc55 505Returns a list of keys/values to be used as the catalyst variables in the
850ee226 506template.
507
b63ddd04 508=head2 config
8077080c 509
8cd017a8 510This method allows your view subclass to pass additional settings to
4729c102 511the TT configuration hash, or to set the options as below:
512
b63ddd04 513=head2 paths
514
515The list of paths TT will look for templates in.
4729c102 516
f5b61ad7 517=head2 C<CATALYST_VAR>
4729c102 518
519Allows you to change the name of the Catalyst context object. If set, it will also
520remove the base and name aliases, so you will have access them through <context>.
521
522For example:
523
524 MyApp->config({
525 name => 'MyApp',
7d8aa5ec 526 root => MyApp->path_to('root'),
6229ce0d 527 'View::TT' => {
4729c102 528 CATALYST_VAR => 'Catalyst',
529 },
530 });
531
532F<message.tt2>:
533
534 The base is [% Catalyst.req.base %]
535 The name is [% Catalyst.config.name %]
536
b63ddd04 537=head2 C<TIMER>
4729c102 538
539If you have configured Catalyst for debug output, and turned on the TIMER setting,
540C<Catalyst::View::TT> will enable profiling of template processing
541(using L<Template::Timer>). This will embed HTML comments in the
542output from your templates, such as:
543
544 <!-- TIMER START: process mainmenu/mainmenu.ttml -->
545 <!-- TIMER START: include mainmenu/cssindex.tt -->
546 <!-- TIMER START: process mainmenu/cssindex.tt -->
547 <!-- TIMER END: process mainmenu/cssindex.tt (0.017279 seconds) -->
548 <!-- TIMER END: include mainmenu/cssindex.tt (0.017401 seconds) -->
549
550 ....
551
552 <!-- TIMER END: process mainmenu/footer.tt (0.003016 seconds) -->
553
554
b63ddd04 555=head2 C<TEMPLATE_EXTENSION>
4729c102 556
747f2b6d 557a sufix to add when looking for templates bases on the C<match> method in L<Catalyst::Request>.
4729c102 558
559For example:
560
6229ce0d 561 package MyApp::Controller::Test;
f5b61ad7 562 sub test : Local { .. }
4729c102 563
564Would by default look for a template in <root>/test/test. If you set TEMPLATE_EXTENSION to '.tt', it will look for
565<root>/test/test.tt.
566
51199593 567=head2 C<PROVIDERS>
568
569Allows you to specify the template providers that TT will use.
570
571 MyApp->config({
572 name => 'MyApp',
573 root => MyApp->path_to('root'),
6229ce0d 574 'View::TT' => {
51199593 575 PROVIDERS => [
576 {
577 name => 'DBI',
578 args => {
579 DBI_DSN => 'dbi:DB2:books',
580 DBI_USER=> 'foo'
581 }
582 }, {
583 name => '_file_',
584 args => {}
585 }
586 ]
587 },
588 });
589
590The 'name' key should correspond to the class name of the provider you
591want to use. The _file_ name is a special case that represents the default
592TT file-based provider. By default the name is will be prefixed with
593'Template::Provider::'. You can fully qualify the name by using a unary
594plus:
595
596 name => '+MyApp::Provider::Foo'
597
6175bba7 598You can also specify the 'copy_config' key as an arrayref, to copy those keys
599from the general config, into the config for the provider:
f5b61ad7 600
6175bba7 601 DEFAULT_ENCODING => 'utf-8',
602 PROVIDERS => [
603 {
604 name => 'Encoding',
605 copy_config => [qw(DEFAULT_ENCODING INCLUDE_PATH)]
606 }
607 ]
f5b61ad7 608
6175bba7 609This can prove useful when you want to use the additional_template_paths hack
610in your own provider, or if you need to use Template::Provider::Encoding
611
8cd017a8 612=head2 HELPERS
613
614The L<Catalyst::Helper::View::TT> and
615L<Catalyst::Helper::View::TTSite> helper modules are provided to create
616your view module. There are invoked by the F<myapp_create.pl> script:
617
618 $ script/myapp_create.pl view TT TT
619
620 $ script/myapp_create.pl view TT TTSite
621
622The L<Catalyst::Helper::View::TT> module creates a basic TT view
623module. The L<Catalyst::Helper::View::TTSite> module goes a little
624further. It also creates a default set of templates to get you
625started. It also configures the view module to locate the templates
626automatically.
627
8077080c 628=head1 SEE ALSO
629
8cd017a8 630L<Catalyst>, L<Catalyst::Helper::View::TT>,
631L<Catalyst::Helper::View::TTSite>, L<Template::Manual>
8077080c 632
c0eb0527 633=head1 AUTHORS
8077080c 634
635Sebastian Riedel, C<sri@cpan.org>
c0eb0527 636
d938377b 637Marcus Ramberg, C<mramberg@cpan.org>
c0eb0527 638
722fede4 639Jesse Sheidlower, C<jester@panix.com>
c0eb0527 640
8cd017a8 641Andy Wardley, C<abw@cpan.org>
8077080c 642
643=head1 COPYRIGHT
644
f5b61ad7 645This program is free software. You can redistribute it and/or modify it
2774dc77 646under the same terms as Perl itself.
8077080c 647
648=cut