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