1 package Catalyst::View::TT;
6 use base qw/Catalyst::View/;
11 use Scalar::Util qw/blessed/;
13 our $VERSION = '0.34';
15 __PACKAGE__->mk_accessors('template');
16 __PACKAGE__->mk_accessors('include_path');
18 *paths = \&include_path;
22 Catalyst::View::TT - Template View Class
26 # use the helper to create your View
28 myapp_create.pl view Web TT
30 # add custom configration in View/Web.pm
33 # any TT configuration items go here
35 MyApp->path_to( 'root', 'src' ),
36 MyApp->path_to( 'root', 'lib' ),
38 TEMPLATE_EXTENSION => '.tt',
42 PRE_PROCESS => 'config/main',
43 WRAPPER => 'site/wrapper',
44 render_die => 1, # Default for new apps, see render method docs
45 expose_methods => [qw/method_in_view_class/],
48 # render view from lib/MyApp.pm or lib/MyApp::Controller::SomeController.pm
50 sub message : Global {
51 my ( $self, $c ) = @_;
52 $c->stash->{template} = 'message.tt2';
53 $c->stash->{message} = 'Hello World!';
54 $c->forward( $c->view('Web') );
57 # access variables from template
59 The message is: [% message %].
61 # example when CATALYST_VAR is set to 'Catalyst'
62 Context is [% Catalyst %]
63 The base is [% Catalyst.req.base %]
64 The name is [% Catalyst.config.name %]
66 # example when CATALYST_VAR isn't set
68 The base is [% base %]
69 The name is [% name %]
74 my ( $paths, $dlim ) = shift;
75 return () if ( !$paths );
76 return @{$paths} if ( ref $paths eq 'ARRAY' );
78 # tweak delim to ignore C:/
79 unless ( defined $dlim ) {
80 $dlim = ( $^O eq 'MSWin32' ) ? ':(?!\\/)' : ':';
82 return split( /$dlim/, $paths );
86 my ( $class, $c, $arguments ) = @_;
89 TEMPLATE_EXTENSION => '',
93 if ( ! (ref $config->{INCLUDE_PATH} eq 'ARRAY') ) {
94 my $delim = $config->{DELIMITER};
96 = _coerce_paths( $config->{INCLUDE_PATH}, $delim );
97 if ( !@include_path ) {
98 my $root = $c->config->{root};
99 my $base = Path::Class::dir( $root, 'base' );
100 @include_path = ( "$root", "$base" );
102 $config->{INCLUDE_PATH} = \@include_path;
105 # if we're debugging and/or the TIMER option is set, then we install
106 # Template::Timer as a custom CONTEXT object, but only if we haven't
107 # already got a custom CONTEXT defined
109 if ( $config->{TIMER} ) {
110 if ( $config->{CONTEXT} ) {
112 'Cannot use Template::Timer - a TT CONTEXT is already defined'
116 $config->{CONTEXT} = Template::Timer->new(%$config);
120 if ( $c->debug && $config->{DUMP_CONFIG} ) {
121 $c->log->debug( "TT Config: ", dump($config) );
124 my $self = $class->next::method(
128 # Set base include paths. Local'd in render if needed
129 $self->include_path($config->{INCLUDE_PATH});
131 $self->config($config);
133 # Creation of template outside of call to new so that we can pass [ $self ]
134 # as INCLUDE_PATH config item, which then gets ->paths() called to get list
135 # of include paths to search for templates.
137 # Use a weakend copy of self so we dont have loops preventing GC from working
139 Scalar::Util::weaken($copy);
140 $config->{INCLUDE_PATH} = [ sub { $copy->paths } ];
142 if ( $config->{PROVIDERS} ) {
144 if ( ref($config->{PROVIDERS}) eq 'ARRAY') {
145 foreach my $p (@{$config->{PROVIDERS}}) {
146 my $pname = $p->{name};
147 my $prov = 'Template::Provider';
148 if($pname eq '_file_')
150 $p->{args} = { %$config };
154 if($pname =~ s/^\+//) {
161 # We copy the args people want from the config
164 if ($p->{copy_config}) {
165 map { $p->{args}->{$_} = $config->{$_} }
166 grep { exists $config->{$_} }
167 @{ $p->{copy_config} };
171 eval "require $prov";
173 push @providers, "$prov"->new($p->{args});
177 $c->log->warn("Can't load $prov, ($@)");
181 delete $config->{PROVIDERS};
183 $config->{LOAD_TEMPLATES} = \@providers;
188 Template->new($config) || do {
189 my $error = Template->error();
190 $c->log->error($error);
200 my ( $self, $c ) = @_;
202 my $template = $c->stash->{template}
203 || $c->action . $self->config->{TEMPLATE_EXTENSION};
205 unless (defined $template) {
206 $c->log->debug('No template specified for rendering') if $c->debug;
211 my $output = eval { $self->render($c, $template) };
213 return $self->_rendering_error($c, $template . ': ' . $err);
215 if (blessed($output) && $output->isa('Template::Exception')) {
216 $self->_rendering_error($c, $output);
219 unless ( $c->response->content_type ) {
220 $c->response->content_type('text/html; charset=utf-8');
223 $c->response->body($output);
228 sub _rendering_error {
229 my ($self, $c, $err) = @_;
230 my $error = qq/Couldn't render template "$err"/;
231 $c->log->error($error);
237 my ($self, $c, $template, $args) = @_;
239 $c->log->debug(qq/Rendering template "$template"/) if $c && $c->debug;
243 (ref $args eq 'HASH' ? %$args : %{ $c->stash() }),
244 $self->template_vars($c)
247 local $self->{include_path} =
248 [ @{ $vars->{additional_template_paths} }, @{ $self->{include_path} } ]
249 if ref $vars->{additional_template_paths};
251 unless ( $self->template->process( $template, $vars, \$output ) ) {
252 if (exists $self->{render_die}) {
253 die $self->template->error if $self->{render_die};
254 return $self->template->error;
256 $c->log->debug('The Catalyst::View::TT render() method will start dying 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 wish to continue to return the exception rather than throwing it, add render_die => 0 to your config.') if $c->debug;
257 return $self->template->error;
263 my ( $self, $c ) = @_;
266 my $cvar = $self->config->{CATALYST_VAR};
268 my %vars = defined $cvar
272 base => $c->req->base,
273 name => $c->config->{name}
276 if ($self->config->{expose_methods}) {
277 my $meta = $self->meta;
278 foreach my $method_name (@{$self->config->{expose_methods}}) {
279 my $method = $meta->get_method( $method_name );
281 Catalyst::Exception->throw( "$method_name not found in TT view" );
283 my $method_body = $method->body;
285 $self->$method_body($c, @_);
287 $vars{$method_name} = $sub;
299 This is the Catalyst view class for the L<Template Toolkit|Template>.
300 Your application should defined a view class which is a subclass of
301 this module. Throughout this manual it will be assumed that your application
302 is named F<MyApp> and you are creating a TT view named F<Web>; these names
303 are placeholders and should always be replaced with whatever name you've
304 chosen for your application and your view. The easiest way to create a TT
305 view class is through the F<myapp_create.pl> script that is created along
306 with the application:
308 $ script/myapp_create.pl view Web TT
310 This creates a F<MyApp::View::Web.pm> module in the F<lib> directory (again,
311 replacing C<MyApp> with the name of your application) which looks
314 package FooBar::View::Web;
319 use base 'Catalyst::View::TT';
321 __PACKAGE__->config(DEBUG => 'all');
323 Now you can modify your action handlers in the main application and/or
324 controllers to forward to your view class. You might choose to do this
325 in the end() method, for example, to automatically forward all actions
326 to the TT view class.
328 # In MyApp or MyApp::Controller::SomeController
331 my( $self, $c ) = @_;
332 $c->forward( $c->view('Web') );
335 But if you are using the standard auto-generated end action, you don't even need
338 # in MyApp::Controller::Root
339 sub end : ActionClass('RenderView') {} # no need to change this line
344 default_view => 'Web',
347 This will Just Work. And it has the advantages that:
353 If you want to use a different view for a given request, just set
354 << $c->stash->{current_view} >>. (See L<Catalyst>'s C<< $c->view >> method
359 << $c->res->redirect >> is handled by default. If you just forward to
360 C<View::Web> in your C<end> routine, you could break this by sending additional
365 See L<Catalyst::Action::RenderView> for more details.
369 There are a three different ways to configure your view class. The
370 first way is to call the C<config()> method in the view subclass. This
371 happens when the module is first loaded.
373 package MyApp::View::Web;
376 use base 'Catalyst::View::TT';
378 __PACKAGE__->config({
380 MyApp->path_to( 'root', 'templates', 'lib' ),
381 MyApp->path_to( 'root', 'templates', 'src' ),
383 PRE_PROCESS => 'config/main',
384 WRAPPER => 'site/wrapper',
387 You may also override the configuration provided in the view class by adding
388 a 'View::Web' section to your application config (either in the application
389 main class, or in your configuration file). This should be reserved for
390 deployment-specific concerns. For example:
392 # MyApp_local.conf (Config::General format)
395 WRAPPER "custom_wrapper"
396 INCLUDE_PATH __path_to('root/templates/custom_site')__
397 INCLUDE_PATH __path_to('root/templates')__
400 might be used as part of a simple way to deploy different instances of the
401 same application with different themes.
403 =head2 DYNAMIC INCLUDE_PATH
405 Sometimes it is desirable to modify INCLUDE_PATH for your templates at run time.
407 Additional paths can be added to the start of INCLUDE_PATH via the stash as
410 $c->stash->{additional_template_paths} =
411 [$c->config->{root} . '/test_include_path'];
413 If you need to add paths to the end of INCLUDE_PATH, there is also an
414 include_path() accessor available:
416 push( @{ $c->view('Web')->include_path }, qw/path/ );
418 Note that if you use include_path() to add extra paths to INCLUDE_PATH, you
419 MUST check for duplicate paths. Without such checking, the above code will add
420 "path" to INCLUDE_PATH at every request, causing a memory leak.
422 A safer approach is to use include_path() to overwrite the array of paths
423 rather than adding to it. This eliminates both the need to perform duplicate
424 checking and the chance of a memory leak:
426 @{ $c->view('Web')->include_path } = qw/path another_path/;
428 If you are calling C<render> directly then you can specify dynamic paths by
429 having a C<additional_template_paths> key with a value of additonal directories
430 to search. See L<CAPTURING TEMPLATE OUTPUT> for an example showing this.
432 =head2 RENDERING VIEWS
434 The view plugin renders the template specified in the C<template>
437 sub message : Global {
438 my ( $self, $c ) = @_;
439 $c->stash->{template} = 'message.tt2';
440 $c->forward( $c->view('Web') );
443 If a stash item isn't defined, then it instead uses the
444 stringification of the action dispatched to (as defined by $c->action)
445 in the above example, this would be C<message>, but because the default
446 is to append '.tt', it would load C<root/message.tt>.
448 The items defined in the stash are passed to the Template Toolkit for
449 use as template variables.
451 sub default : Private {
452 my ( $self, $c ) = @_;
453 $c->stash->{template} = 'message.tt2';
454 $c->stash->{message} = 'Hello World!';
455 $c->forward( $c->view('Web') );
458 A number of other template variables are also added:
460 c A reference to the context object, $c
461 base The URL base, from $c->req->base()
462 name The application name, from $c->config->{ name }
464 These can be accessed from the template in the usual way:
468 The message is: [% message %]
469 The base is [% base %]
470 The name is [% name %]
473 The output generated by the template is stored in C<< $c->response->body >>.
475 =head2 CAPTURING TEMPLATE OUTPUT
477 If you wish to use the output of a template for some other purpose than
478 displaying in the response, e.g. for sending an email, this is possible using
479 L<Catalyst::Plugin::Email> and the L<render> method:
481 sub send_email : Local {
486 To => 'me@localhost',
487 Subject => 'A TT Email',
489 body => $c->view('Web')->render($c, 'email.tt', {
490 additional_template_paths => [ $c->config->{root} . '/email_templates'],
491 email_tmpl_param1 => 'foo'
495 # Redirect or display a message
498 =head2 TEMPLATE PROFILING
500 See L<C<TIMER>> property of the L<config> method.
506 The constructor for the TT view. Sets up the template provider,
507 and reads the application config.
511 Renders the template specified in C<< $c->stash->{template} >> or
512 C<< $c->action >> (the private name of the matched action). Calls L<render> to
513 perform actual rendering. Output is stored in C<< $c->response->body >>.
515 It is possible to forward to the process method of a TT view from inside
518 $c->forward('View::Web');
520 N.B. This is usually done automatically by L<Catalyst::Action::RenderView>.
522 =head2 render($c, $template, \%args)
524 Renders the given template and returns output. Throws a L<Template::Exception>
527 The template variables are set to C<%$args> if C<$args> is a hashref, or
528 C<< $c->stash >> otherwise. In either case the variables are augmented with
529 C<base> set to C<< $c->req->base >>, C<c> to C<$c>, and C<name> to
530 C<< $c->config->{name} >>. Alternately, the C<CATALYST_VAR> configuration item
531 can be defined to specify the name of a template variable through which the
532 context reference (C<$c>) can be accessed. In this case, the C<c>, C<base>, and
533 C<name> variables are omitted.
535 C<$template> can be anything that Template::process understands how to
536 process, including the name of a template file or a reference to a test string.
537 See L<Template::process|Template/process> for a full list of supported formats.
539 To use the render method outside of your Catalyst app, just pass a undef context.
540 This can be useful for tests, for instance.
542 It is possible to forward to the render method of a TT view from inside Catalyst
543 to render page fragments like this:
545 my $fragment = $c->forward("View::Web", "render", $template_name, $c->stash->{fragment_data});
547 =head3 Backwards compatibility note
549 The render method used to just return the Template::Exception object, rather
550 than just throwing it. This is now deprecated and instead the render method
551 will throw an exception for new applications.
553 This behaviour can be activated (and is activated in the default skeleton
554 configuration) by using C<< render_die => 1 >>. If you rely on the legacy
555 behaviour then a warning will be issued.
557 To silence this warning, set C<< render_die => 0 >>, but it is recommended
558 you adjust your code so that it works with C<< render_die => 1 >>.
560 In a future release, C<< render_die => 1 >> will become the default if
565 Returns a list of keys/values to be used as the catalyst variables in the
570 This method allows your view subclass to pass additional settings to
571 the TT configuration hash, or to set the options as below:
575 The list of paths TT will look for templates in.
577 =head2 expose_methods
579 The list of methods in your View class which should be made available to the templates.
583 expose_methods => [qw/uri_for_static/],
588 my ($self, $c, $filename) = @_;
590 # additional complexity like checking file exists here
592 return $c->uri_for('/static/css/' . $filename);
595 Then in the template:
597 [% uri_for_css('home.css') %]
599 =head2 C<CATALYST_VAR>
601 Allows you to change the name of the Catalyst context object. If set, it will also
602 remove the base and name aliases, so you will have access them through <context>.
604 For example, if CATALYST_VAR has been set to "Catalyst", a template might
607 The base is [% Catalyst.req.base %]
608 The name is [% Catalyst.config.name %]
612 If you have configured Catalyst for debug output, and turned on the TIMER setting,
613 C<Catalyst::View::TT> will enable profiling of template processing
614 (using L<Template::Timer>). This will embed HTML comments in the
615 output from your templates, such as:
617 <!-- TIMER START: process mainmenu/mainmenu.ttml -->
618 <!-- TIMER START: include mainmenu/cssindex.tt -->
619 <!-- TIMER START: process mainmenu/cssindex.tt -->
620 <!-- TIMER END: process mainmenu/cssindex.tt (0.017279 seconds) -->
621 <!-- TIMER END: include mainmenu/cssindex.tt (0.017401 seconds) -->
625 <!-- TIMER END: process mainmenu/footer.tt (0.003016 seconds) -->
628 =head2 C<TEMPLATE_EXTENSION>
630 a sufix to add when looking for templates bases on the C<match> method in L<Catalyst::Request>.
634 package MyApp::Controller::Test;
635 sub test : Local { .. }
637 Would by default look for a template in <root>/test/test. If you set TEMPLATE_EXTENSION to '.tt', it will look for
642 Allows you to specify the template providers that TT will use.
646 root => MyApp->path_to('root'),
652 DBI_DSN => 'dbi:DB2:books',
663 The 'name' key should correspond to the class name of the provider you
664 want to use. The _file_ name is a special case that represents the default
665 TT file-based provider. By default the name is will be prefixed with
666 'Template::Provider::'. You can fully qualify the name by using a unary
669 name => '+MyApp::Provider::Foo'
671 You can also specify the 'copy_config' key as an arrayref, to copy those keys
672 from the general config, into the config for the provider:
674 DEFAULT_ENCODING => 'utf-8',
678 copy_config => [qw(DEFAULT_ENCODING INCLUDE_PATH)]
682 This can prove useful when you want to use the additional_template_paths hack
683 in your own provider, or if you need to use Template::Provider::Encoding
687 The L<Catalyst::Helper::View::TT> and
688 L<Catalyst::Helper::View::TTSite> helper modules are provided to create
689 your view module. There are invoked by the F<myapp_create.pl> script:
691 $ script/myapp_create.pl view Web TT
693 $ script/myapp_create.pl view Web TTSite
695 The L<Catalyst::Helper::View::TT> module creates a basic TT view
696 module. The L<Catalyst::Helper::View::TTSite> module goes a little
697 further. It also creates a default set of templates to get you
698 started. It also configures the view module to locate the templates
703 If you are using the L<CGI> module inside your templates, you will
704 experience that the Catalyst server appears to hang while rendering
705 the web page. This is due to the debug mode of L<CGI> (which is
706 waiting for input in the terminal window). Turning off the
707 debug mode using the "-no_debug" option solves the
710 [% USE CGI('-no_debug') %]
714 L<Catalyst>, L<Catalyst::Helper::View::TT>,
715 L<Catalyst::Helper::View::TTSite>, L<Template::Manual>
719 Sebastian Riedel, C<sri@cpan.org>
721 Marcus Ramberg, C<mramberg@cpan.org>
723 Jesse Sheidlower, C<jester@panix.com>
725 Andy Wardley, C<abw@cpan.org>
729 This program is free software. You can redistribute it and/or modify it
730 under the same terms as Perl itself.