1 package Catalyst::View::TT;
7 use Scalar::Util qw/blessed/;
8 use namespace::autoclean;
10 extends 'Catalyst::View';
12 our $VERSION = '0.33';
14 has template => ( is => 'ro' );
15 has include_path => ( is => 'rw' );
17 __PACKAGE__->meta->add_method('paths' => __PACKAGE__->meta->find_method_by_name('include_path'));
21 Catalyst::View::TT - Template View Class
25 # use the helper to create your View
27 myapp_create.pl view HTML TT
29 # configure in lib/MyApp.pm (Could be set from configfile instead)
33 root => MyApp->path_to('root'),
36 # any TT configurations items go here
38 MyApp->path_to( 'root', 'src' ),
39 MyApp->path_to( 'root', 'lib' ),
41 TEMPLATE_EXTENSION => '.tt',
45 PRE_PROCESS => 'config/main',
46 WRAPPER => 'site/wrapper',
47 render_die => 1, # Default for new apps, see render method docs
51 # render view from lib/MyApp.pm or lib/MyApp::Controller::SomeController.pm
53 sub message : Global {
54 my ( $self, $c ) = @_;
55 $c->stash->{template} = 'message.tt2';
56 $c->stash->{message} = 'Hello World!';
57 $c->forward( $c->view('TT') );
60 # access variables from template
62 The message is: [% message %].
64 # example when CATALYST_VAR is set to 'Catalyst'
65 Context is [% Catalyst %]
66 The base is [% Catalyst.req.base %]
67 The name is [% Catalyst.config.name %]
69 # example when CATALYST_VAR isn't set
71 The base is [% base %]
72 The name is [% name %]
77 my ( $paths, $dlim ) = shift;
78 return () if ( !$paths );
79 return @{$paths} if ( ref $paths eq 'ARRAY' );
81 # tweak delim to ignore C:/
82 unless ( defined $dlim ) {
83 $dlim = ( $^O eq 'MSWin32' ) ? ':(?!\\/)' : ':';
85 return split( /$dlim/, $paths );
88 #$class->merge_config_hashes( $class->config, $args );
90 around BUILDARGS => sub {
91 my ( $orig, $class, $c, $arguments ) = @_;
92 my $config = $class->merge_config_hashes(
95 TEMPLATE_EXTENSION => '',
97 $class->$orig($c, $arguments)
99 if ( ! (ref $config->{INCLUDE_PATH} eq 'ARRAY') ) {
100 my $delim = $config->{DELIMITER};
102 = _coerce_paths( $config->{INCLUDE_PATH}, $delim );
103 if ( !@include_path ) {
104 my $root = $c->config->{root};
105 my $base = Path::Class::dir( $root, 'base' );
106 @include_path = ( "$root", "$base" );
108 $config->{INCLUDE_PATH} = \@include_path;
111 # if we're debugging and/or the TIMER option is set, then we install
112 # Template::Timer as a custom CONTEXT object, but only if we haven't
113 # already got a custom CONTEXT defined
115 if ( $config->{TIMER} ) {
116 if ( $config->{CONTEXT} ) {
118 'Cannot use Template::Timer - a TT CONTEXT is already defined'
122 $config->{CONTEXT} = Template::Timer->new(%$config);
126 if ( $c->debug && $config->{DUMP_CONFIG} ) {
127 $c->log->debug( "TT Config: ", dump($config) );
130 if ( $config->{PROVIDERS} ) {
132 if ( ref($config->{PROVIDERS}) eq 'ARRAY') {
133 foreach my $p (@{$config->{PROVIDERS}}) {
134 my $pname = $p->{name};
135 my $prov = 'Template::Provider';
136 if($pname eq '_file_')
138 $p->{args} = { %$config };
142 if($pname =~ s/^\+//) {
149 # We copy the args people want from the config
152 if ($p->{copy_config}) {
153 map { $p->{args}->{$_} = $config->{$_} }
154 grep { exists $config->{$_} }
155 @{ $p->{copy_config} };
159 eval "require $prov";
161 push @providers, "$prov"->new($p->{args});
165 $c->log->warn("Can't load $prov, ($@)");
169 delete $config->{PROVIDERS};
171 $config->{LOAD_TEMPLATES} = \@providers;
175 $config->{template} =
176 Template->new($config) || do {
177 my $error = Template->error();
178 $c->log->error($error);
187 my ($self, $config) = @_;
188 # Set base include paths. Local'd in render if needed
189 $self->include_path($config->{INCLUDE_PATH});
191 $self->config($config);
193 # Creation of template outside of call to new so that we can pass [ $self ]
194 # as INCLUDE_PATH config item, which then gets ->paths() called to get list
195 # of include paths to search for templates.
197 # Use a weakend copy of self so we dont have loops preventing GC from working
199 Scalar::Util::weaken($copy);
200 $config->{INCLUDE_PATH} = [ sub { $copy->paths } ];
204 my ( $self, $c ) = @_;
206 my $template = $c->stash->{template}
207 || $c->action . $self->config->{TEMPLATE_EXTENSION};
209 unless (defined $template) {
210 $c->log->debug('No template specified for rendering') if $c->debug;
215 my $output = eval { $self->render($c, $template) };
217 return $self->_rendering_error($c, $err);
219 if (blessed($output) && $output->isa('Template::Exception')) {
220 $self->_rendering_error($c, $output);
223 unless ( $c->response->content_type ) {
224 $c->response->content_type('text/html; charset=utf-8');
227 $c->response->body($output);
232 sub _rendering_error {
233 my ($self, $c, $err) = @_;
234 my $error = qq/Couldn't render template "$err"/;
235 $c->log->error($error);
241 my ($self, $c, $template, $args) = @_;
243 $c->log->debug(qq/Rendering template "$template"/) if $c && $c->debug;
247 (ref $args eq 'HASH' ? %$args : %{ $c->stash() }),
248 $self->template_vars($c)
251 local $self->{include_path} =
252 [ @{ $vars->{additional_template_paths} }, @{ $self->{include_path} } ]
253 if ref $vars->{additional_template_paths};
255 unless ( $self->template->process( $template, $vars, \$output ) ) {
256 if (exists $self->{render_die}) {
257 die $self->template->error if $self->{render_die};
258 return $self->template->error;
260 $c->log->debug('The Catalyst::View::TT render() method of will die on error in a future release. Unless you are calling the render() method manually, you probably want the new behaviour, so set render_die => 1 in config for ' . blessed($self) . '. If you are calling the render() method manually and you wish it to continue to return the exception rather than throwing it, add render_die => 0 to your config.') if $c->debug;
261 return $self->template->error;
267 my ( $self, $c ) = @_;
270 my $cvar = $self->config->{CATALYST_VAR};
276 base => $c->req->base,
277 name => $c->config->{name}
288 This is the Catalyst view class for the L<Template Toolkit|Template>.
289 Your application should defined a view class which is a subclass of
290 this module. The easiest way to achieve this is using the
291 F<myapp_create.pl> script (where F<myapp> should be replaced with
292 whatever your application is called). This script is created as part
293 of the Catalyst setup.
295 $ script/myapp_create.pl view TT TT
297 This creates a MyApp::View::TT.pm module in the F<lib> directory (again,
298 replacing C<MyApp> with the name of your application) which looks
301 package FooBar::View::TT;
306 use base 'Catalyst::View::TT';
308 __PACKAGE__->config(DEBUG => 'all');
310 Now you can modify your action handlers in the main application and/or
311 controllers to forward to your view class. You might choose to do this
312 in the end() method, for example, to automatically forward all actions
313 to the TT view class.
315 # In MyApp or MyApp::Controller::SomeController
318 my( $self, $c ) = @_;
319 $c->forward( $c->view('TT') );
322 But if you are using the standard auto-generated end action, you don't even need
325 # in MyApp::Controller::Root
326 sub end : ActionClass('RenderView') {} # no need to change this line
331 default_view => 'TT',
334 This will Just Work. And it has the advantages that:
340 If you want to use a different view for a given request, just set
341 << $c->stash->{current_view} >>. (See L<Catalyst>'s C<< $c->view >> method
346 << $c->res->redirect >> is handled by default. If you just forward to
347 C<View::TT> in your C<end> routine, you could break this by sending additional
352 See L<Catalyst::Action::RenderView> for more details.
356 There are a three different ways to configure your view class. The
357 first way is to call the C<config()> method in the view subclass. This
358 happens when the module is first loaded.
360 package MyApp::View::TT;
363 use base 'Catalyst::View::TT';
365 MyApp::View::TT->config({
367 MyApp->path_to( 'root', 'templates', 'lib' ),
368 MyApp->path_to( 'root', 'templates', 'src' ),
370 PRE_PROCESS => 'config/main',
371 WRAPPER => 'site/wrapper',
374 The second way is to define a C<new()> method in your view subclass.
375 This performs the configuration when the view object is created,
376 shortly after being loaded. Remember to delegate to the base class
377 C<new()> method (via C<$self-E<gt>next::method()> in the example below) after
378 performing any configuration.
384 MyApp->path_to( 'root', 'templates', 'lib' ),
385 MyApp->path_to( 'root', 'templates', 'src' ),
387 PRE_PROCESS => 'config/main',
388 WRAPPER => 'site/wrapper',
390 return $self->next::method(@_);
393 The final, and perhaps most direct way, is to define a class
394 item in your main application configuration, again by calling the
395 ubiquitous C<config()> method. The items in the class hash are
396 added to those already defined by the above two methods. This happens
397 in the base class new() method (which is one reason why you must
398 remember to call it via C<MRO::Compat> if you redefine the C<new()>
399 method in a subclass).
408 root => MyApp->path_to('root'),
411 MyApp->path_to( 'root', 'templates', 'lib' ),
412 MyApp->path_to( 'root', 'templates', 'src' ),
414 PRE_PROCESS => 'config/main',
415 WRAPPER => 'site/wrapper',
419 Note that any configuration items defined by one of the earlier
420 methods will be overwritten by items of the same name provided by the
423 =head2 DYNAMIC INCLUDE_PATH
425 Sometimes it is desirable to modify INCLUDE_PATH for your templates at run time.
427 Additional paths can be added to the start of INCLUDE_PATH via the stash as
430 $c->stash->{additional_template_paths} =
431 [$c->config->{root} . '/test_include_path'];
433 If you need to add paths to the end of INCLUDE_PATH, there is also an
434 include_path() accessor available:
436 push( @{ $c->view('TT')->include_path }, qw/path/ );
438 Note that if you use include_path() to add extra paths to INCLUDE_PATH, you
439 MUST check for duplicate paths. Without such checking, the above code will add
440 "path" to INCLUDE_PATH at every request, causing a memory leak.
442 A safer approach is to use include_path() to overwrite the array of paths
443 rather than adding to it. This eliminates both the need to perform duplicate
444 checking and the chance of a memory leak:
446 @{ $c->view('TT')->include_path } = qw/path another_path/;
448 If you are calling C<render> directly then you can specify dynamic paths by
449 having a C<additional_template_paths> key with a value of additonal directories
450 to search. See L<CAPTURING TEMPLATE OUTPUT> for an example showing this.
452 =head2 RENDERING VIEWS
454 The view plugin renders the template specified in the C<template>
457 sub message : Global {
458 my ( $self, $c ) = @_;
459 $c->stash->{template} = 'message.tt2';
460 $c->forward( $c->view('TT') );
463 If a stash item isn't defined, then it instead uses the
464 stringification of the action dispatched to (as defined by $c->action)
465 in the above example, this would be C<message>, but because the default
466 is to append '.tt', it would load C<root/message.tt>.
468 The items defined in the stash are passed to the Template Toolkit for
469 use as template variables.
471 sub default : Private {
472 my ( $self, $c ) = @_;
473 $c->stash->{template} = 'message.tt2';
474 $c->stash->{message} = 'Hello World!';
475 $c->forward( $c->view('TT') );
478 A number of other template variables are also added:
480 c A reference to the context object, $c
481 base The URL base, from $c->req->base()
482 name The application name, from $c->config->{ name }
484 These can be accessed from the template in the usual way:
488 The message is: [% message %]
489 The base is [% base %]
490 The name is [% name %]
493 The output generated by the template is stored in C<< $c->response->body >>.
495 =head2 CAPTURING TEMPLATE OUTPUT
497 If you wish to use the output of a template for some other purpose than
498 displaying in the response, e.g. for sending an email, this is possible using
499 L<Catalyst::Plugin::Email> and the L<render> method:
501 sub send_email : Local {
506 To => 'me@localhost',
507 Subject => 'A TT Email',
509 body => $c->view('TT')->render($c, 'email.tt', {
510 additional_template_paths => [ $c->config->{root} . '/email_templates'],
511 email_tmpl_param1 => 'foo'
515 # Redirect or display a message
518 =head2 TEMPLATE PROFILING
520 See L<C<TIMER>> property of the L<config> method.
526 The constructor for the TT view. Sets up the template provider,
527 and reads the application config.
531 Renders the template specified in C<< $c->stash->{template} >> or
532 C<< $c->action >> (the private name of the matched action). Calls L<render> to
533 perform actual rendering. Output is stored in C<< $c->response->body >>.
535 It is possible to forward to the process method of a TT view from inside
538 $c->forward('View::TT');
540 N.B. This is usually done automatically by L<Catalyst::Action::RenderView>.
542 =head2 render($c, $template, \%args)
544 Renders the given template and returns output. Throws a L<Template::Exception>
547 The template variables are set to C<%$args> if $args is a hashref, or
548 $C<< $c->stash >> otherwise. In either case the variables are augmented with
549 C<base> set to C< << $c->req->base >>, C<c> to C<$c> and C<name> to
550 C<< $c->config->{name} >>. Alternately, the C<CATALYST_VAR> configuration item
551 can be defined to specify the name of a template variable through which the
552 context reference (C<$c>) can be accessed. In this case, the C<c>, C<base> and
553 C<name> variables are omitted.
555 C<$template> can be anything that Template::process understands how to
556 process, including the name of a template file or a reference to a test string.
557 See L<Template::process|Template/process> for a full list of supported formats.
559 To use the render method outside of your Catalyst app, just pass a undef context.
560 This can be useful for tests, for instance.
562 It is possible to forward to the render method of a TT view from inside Catalyst
563 to render page fragments like this:
565 my $fragment = $c->forward("View::TT", "render", $template_name, $c->stash->{fragment_data});
567 =head3 Backwards compatibility note
569 The render method used to just return the Template::Exception object, rather
570 than just throwing it. This is now deprecated and instead the render method
571 will throw an exception for new applications.
573 This behaviour can be activated (and is activated in the default skeleton
574 configuration) by using C<< render_die => 1 >>. If you rely on the legacy
575 behaviour then a warning will be issued.
577 To silence this warning, set C<< render_die => 0 >>, but it is recommended
578 you adjust your code so that it works with C<< render_die => 1 >>.
580 In a future release, C<< render_die => 1 >> will become the default if
585 Returns a list of keys/values to be used as the catalyst variables in the
590 This method allows your view subclass to pass additional settings to
591 the TT configuration hash, or to set the options as below:
595 The list of paths TT will look for templates in.
597 =head2 C<CATALYST_VAR>
599 Allows you to change the name of the Catalyst context object. If set, it will also
600 remove the base and name aliases, so you will have access them through <context>.
606 root => MyApp->path_to('root'),
608 CATALYST_VAR => 'Catalyst',
614 The base is [% Catalyst.req.base %]
615 The name is [% Catalyst.config.name %]
619 If you have configured Catalyst for debug output, and turned on the TIMER setting,
620 C<Catalyst::View::TT> will enable profiling of template processing
621 (using L<Template::Timer>). This will embed HTML comments in the
622 output from your templates, such as:
624 <!-- TIMER START: process mainmenu/mainmenu.ttml -->
625 <!-- TIMER START: include mainmenu/cssindex.tt -->
626 <!-- TIMER START: process mainmenu/cssindex.tt -->
627 <!-- TIMER END: process mainmenu/cssindex.tt (0.017279 seconds) -->
628 <!-- TIMER END: include mainmenu/cssindex.tt (0.017401 seconds) -->
632 <!-- TIMER END: process mainmenu/footer.tt (0.003016 seconds) -->
635 =head2 C<TEMPLATE_EXTENSION>
637 a sufix to add when looking for templates bases on the C<match> method in L<Catalyst::Request>.
641 package MyApp::Controller::Test;
642 sub test : Local { .. }
644 Would by default look for a template in <root>/test/test. If you set TEMPLATE_EXTENSION to '.tt', it will look for
649 Allows you to specify the template providers that TT will use.
653 root => MyApp->path_to('root'),
659 DBI_DSN => 'dbi:DB2:books',
670 The 'name' key should correspond to the class name of the provider you
671 want to use. The _file_ name is a special case that represents the default
672 TT file-based provider. By default the name is will be prefixed with
673 'Template::Provider::'. You can fully qualify the name by using a unary
676 name => '+MyApp::Provider::Foo'
678 You can also specify the 'copy_config' key as an arrayref, to copy those keys
679 from the general config, into the config for the provider:
681 DEFAULT_ENCODING => 'utf-8',
685 copy_config => [qw(DEFAULT_ENCODING INCLUDE_PATH)]
689 This can prove useful when you want to use the additional_template_paths hack
690 in your own provider, or if you need to use Template::Provider::Encoding
694 The L<Catalyst::Helper::View::TT> and
695 L<Catalyst::Helper::View::TTSite> helper modules are provided to create
696 your view module. There are invoked by the F<myapp_create.pl> script:
698 $ script/myapp_create.pl view TT TT
700 $ script/myapp_create.pl view TT TTSite
702 The L<Catalyst::Helper::View::TT> module creates a basic TT view
703 module. The L<Catalyst::Helper::View::TTSite> module goes a little
704 further. It also creates a default set of templates to get you
705 started. It also configures the view module to locate the templates
710 If you are using the L<CGI> module inside your templates, you will
711 experience that the Catalyst server appears to hang while rendering
712 the web page. This is due to the debug mode of L<CGI> (which is
713 waiting for input in the terminal window). Turning off the
714 debug mode using the "-no_debug" option solves the
717 [% USE CGI('-no_debug') %]
721 L<Catalyst>, L<Catalyst::Helper::View::TT>,
722 L<Catalyst::Helper::View::TTSite>, L<Template::Manual>
726 Sebastian Riedel, C<sri@cpan.org>
728 Marcus Ramberg, C<mramberg@cpan.org>
730 Jesse Sheidlower, C<jester@panix.com>
732 Andy Wardley, C<abw@cpan.org>
736 This program is free software. You can redistribute it and/or modify it
737 under the same terms as Perl itself.