File Coverage

blib/lib/Catalyst/View/TT/Alloy.pm
Criterion Covered Total %
statement 21 80 26.2
branch 0 32 0.0
condition 0 6 0.0
subroutine 7 12 58.3
pod n/a
total 28 130 21.5


line stmt bran cond sub pod time code
1             package Catalyst::View::TT::Alloy;
2              
3 1     1   21257 use strict;
  1         2  
  1         32  
4 1     1   5 use base qw( Catalyst::View );
  1         1  
  1         617  
5              
6 1     1   6 use Carp qw( croak );
  1         6  
  1         84  
7 1     1   2825 use Data::Dump qw( dump );
  1         11390  
  1         97  
8 1     1   1553 use Path::Class;
  1         66465  
  1         72  
9 1     1   8 use Scalar::Util qw( weaken );
  1         2  
  1         42  
10 1     1   2414 use Template::Alloy qw( Compile Parse TT );
  1         29327  
  1         7  
11              
12             our $VERSION = '0.00003';
13              
14             __PACKAGE__->mk_accessors('template');
15             __PACKAGE__->mk_accessors('include_path');
16              
17             =head1 NAME
18              
19             Catalyst::View::TT::Alloy - Template::Alloy (TT) View Class
20              
21             =head1 SYNOPSIS
22              
23             # use the helper to create your View
24             myapp_create.pl view TT::Alloy TT::Alloy
25              
26             # configure in myapp.yml
27              
28             'View::TT::Alloy':
29             INCLUDE_PATH:
30             - __path_to(root/src)__
31             - __path_to(root/lib)__
32             PRE_PROCESS: 'config/main'
33             WRAPPER: 'site/wrapper'
34             # optional
35             TEMPLATE_EXTENSION: '.tt'
36             CATALYST_VAR: 'Catalyst'
37            
38             # example render view in lib/MyApp/Controller/Root.pm
39            
40             sub default : Private {
41             my ( $self, $c ) = @_;
42             $c->stash->{template} = 'message.tt2';
43             $c->stash->{message} = 'Hello World!';
44             return;
45             }
46            
47             sub end : ActionClass('RenderView') {
48             }
49              
50             # access variables from template
51              
52             The message is: [% message %].
53            
54             # example when CATALYST_VAR is set to 'Catalyst'
55             Context is [% Catalyst %]
56             The base is [% Catalyst.req.base %]
57             The name is [% Catalyst.config.name %]
58            
59             # example when CATALYST_VAR isn't set
60             Context is [% c %]
61             The base is [% base %]
62             The name is [% name %]
63              
64             =cut
65              
66             sub _coerce_paths {
67 0     0     my ( $paths, $dlim ) = shift;
68 0 0         return () if ( !$paths );
69 0 0         return @{$paths} if ( ref $paths eq 'ARRAY' );
  0            
70              
71             # tweak delim to ignore C:/
72 0 0         unless ( defined $dlim ) {
73 0 0         $dlim = ( $^O eq 'MSWin32' ) ? ':(?!\\/)' : ':';
74             }
75 0           return split( /$dlim/, $paths );
76             }
77              
78             sub new {
79 0     0     my ( $class, $c, $arguments ) = @_;
80 0           my $config = {
81             TEMPLATE_EXTENSION => '',
82 0           %{ $class->config },
83 0           %{$arguments},
84             };
85 0 0         if ( ! (ref $config->{INCLUDE_PATH} eq 'ARRAY') ) {
86 0           my $delim = $config->{DELIMITER};
87             my @include_path
88 0           = _coerce_paths( $config->{INCLUDE_PATH}, $delim );
89 0 0         if ( !@include_path ) {
90 0           my $root = $c->config->{root};
91 0           my $base = Path::Class::dir( $root, 'base' );
92 0           @include_path = ( "$root", "$base" );
93             }
94 0           $config->{INCLUDE_PATH} = \@include_path;
95             }
96              
97 0 0 0       if ( $c->debug && $config->{DUMP_CONFIG} ) {
98 0           $c->log->debug( "TT Config: ", dump($config) );
99             }
100              
101 0           my $self = $class->next::method(
102             $c, { %$config },
103             );
104              
105             # Set base include paths. Local'd in render if needed
106 0           $self->include_path($config->{INCLUDE_PATH});
107            
108 0           $self->config($config);
109              
110 0           return $self;
111             }
112              
113             sub process {
114 0     0     my ( $self, $c ) = @_;
115              
116 0   0       my $template = $c->stash->{template}
117             || $c->action . $self->config->{TEMPLATE_EXTENSION};
118              
119 0 0         unless (defined $template) {
120 0 0         $c->log->debug('No template specified for rendering') if $c->debug;
121 0           return 0;
122             }
123              
124 0           my $output;
125 0           eval {
126 0           $output = $self->render($c, $template);
127             };
128              
129 0 0         if ($@) {
130 0           my $error = qq/Couldn't render template "$template"/;
131 0           $c->log->error($@);
132 0           $c->error($@);
133 0           return 0;
134             }
135              
136 0 0         unless ( $c->response->content_type ) {
137 0           $c->response->content_type('text/html; charset=utf-8');
138             }
139              
140 0           $c->response->body($output);
141              
142 0           return 1;
143             }
144              
145             sub render {
146 0     0     my ($self, $c, $template, $args) = @_;
147              
148 0 0         $c->log->debug(qq/Rendering template "$template"/) if $c->debug;
149              
150 0           my $config = $self->config;
151 0           $config->{INCLUDE_PATH} = $self->include_path;
152              
153 0           my $vars = {
154 0 0         (ref $args eq 'HASH' ? %$args : %{ $c->stash() }),
155             $self->_template_vars($c)
156             };
157              
158 0           local $config->{INCLUDE_PATH} =
159 0 0         [ @{ $vars->{additional_template_paths} }, @{ $config->{INCLUDE_PATH} } ]
  0            
160             if ref $vars->{additional_template_paths};
161              
162             # until Template::Alloy either gives us a public method to change
163             # INCLUDE_PATH, or supports a coderef there, we need to create a
164             # new object for every call of render()
165 0           my $tt = Template::Alloy->new($config);
166 0           my $output;
167              
168 0 0         unless ( $tt->process( $template, $vars, \$output ) ) {
169 0           croak $tt->error;
170             }
171             else {
172 0           return $output;
173             }
174             }
175              
176             sub _template_vars {
177 0     0     my ( $self, $c ) = @_;
178              
179 0           my $cvar = $self->config->{CATALYST_VAR};
180              
181 0 0         defined $cvar
182             ? ( $cvar => $c )
183             : (
184             c => $c,
185             base => $c->req->base,
186             name => $c->config->{name}
187             )
188             }
189              
190              
191             1;
192              
193             __END__
194              
195             =head1 DESCRIPTION
196              
197             This is the Catalyst view for the L<TT|Template> emulator
198             L<Template::Alloy>.
199              
200             Your application should define a view class which is a subclass of
201             this module. The easiest way to achieve this is using
202             C<script/myapp_create.pl> (replacing C<myapp> with the name of your
203             application).
204              
205             $ script/myapp_create.pl view TT::Alloy TT::Alloy
206              
207             You can either manually forward to the C<TT::Alloy> as normal, or use
208             L<Catalyst::Action::RenderView> to do it for you.
209              
210             # In MyApp::Controller::Root
211            
212             sub end : ActionClass('RenderView') { }
213              
214             =head2 RATIONAL
215              
216             L<Template::Alloy> is a pure-perl module which emulates most common
217             features of L<TT|Template>, and in some cases is faster too. See
218             L<Template::Alloy::TT> for details of which features are missing.
219              
220             L<Catalyst::View::TT::Alloy> is generally compatible with
221             L<Catalyst::View::TT>. The C<TIMER> configuration option isn't supported,
222             and the C<paths()> alias to C<include_path()> has been removed.
223              
224             Although L<Template::Alloy> emulates several other
225             templating modules, the interface differs for each one. For this reason,
226             this module only provides the L<TT|Template> interface.
227              
228             =head2 DYNAMIC INCLUDE_PATH
229              
230             Sometimes it is desirable to modify INCLUDE_PATH for your templates at run time.
231            
232             Additional paths can be added to the start of INCLUDE_PATH via the stash as
233             follows:
234              
235             $c->stash->{additional_template_paths} =
236             [$c->config->{root} . '/test_include_path'];
237              
238             If you need to add paths to the end of INCLUDE_PATH, there is also an
239             include_path() accessor available:
240              
241             push( @{ $c->view('TT')->include_path }, qw/path/ );
242              
243             Note that if you use include_path() to add extra paths to INCLUDE_PATH, you
244             MUST check for duplicate paths. Without such checking, the above code will add
245             "path" to INCLUDE_PATH at every request, causing a memory leak.
246              
247             A safer approach is to use include_path() to overwrite the array of paths
248             rather than adding to it. This eliminates both the need to perform duplicate
249             checking and the chance of a memory leak:
250              
251             $c->view('TT')->include_path([ qw/ path another_path / ]);
252              
253             If you are calling C<render> directly then you can specify dynamic paths by
254             having a C<additional_template_paths> key with a value of additonal directories
255             to search. See L<CAPTURING TEMPLATE OUTPUT> for an example showing this.
256              
257             =head2 RENDERING VIEWS
258              
259             The view plugin renders the template specified in the C<template>
260             item in the stash.
261              
262             sub message : Global {
263             my ( $self, $c ) = @_;
264            
265             $c->stash->{template} = 'message.tt2';
266            
267             $c->forward('MyApp::View::TT::Alloy');
268             }
269              
270             If C<template> isn't defined, then it builds the filename from
271             C<Catalyst/action> and the C<TEMPLATE_EXTENSION> config setting.
272             In the above example, this would be C<message>.
273              
274             The items defined in the stash are passed to L<Template::Alloy> for
275             use as template variables.
276              
277             sub default : Private {
278             my ( $self, $c ) = @_;
279             $c->stash->{template} = 'message.tt2';
280             $c->stash->{message} = 'Hello World!';
281             $c->forward('MyApp::View::TT::Alloy');
282             }
283              
284             A number of other template variables are also added:
285              
286             c A reference to the context object, $c
287             base The URL base, from $c->req->base()
288             name The application name, from $c->config->{ name }
289              
290             These can be accessed from the template in the usual way:
291              
292             <message.tt2>:
293              
294             The message is: [% message %]
295             The base is [% base %]
296             The name is [% name %]
297              
298              
299             The output generated by the template is stored in C<< $c->response->body >>.
300              
301             =head2 CAPTURING TEMPLATE OUTPUT
302              
303             If you wish to use the output of a template for some other purpose than
304             displaying in the response, e.g. for sending an email, this is possible using
305             L<Catalyst::Plugin::Email> and the L<render> method:
306              
307             sub send_email : Local {
308             my ($self, $c) = @_;
309            
310             $c->email(
311             header => [
312             To => 'me@localhost',
313             Subject => 'A TT Email',
314             ],
315             body => $c->view('TT::Alloy')->render($c, 'email.tt', {
316             additional_template_paths => [ $c->config->{root} . '/email_templates'],
317             email_tmpl_param1 => 'foo'
318             }
319             ),
320             );
321             # Redirect or display a message
322             }
323              
324             =head2 METHODS
325              
326             =over 4
327              
328             =item new
329              
330             The constructor for the TT::Alloy view.
331              
332             =item process
333              
334             Renders the template specified in C<< $c->stash->{template} >> or
335             C<< $c->action >> (the private name of the matched action. Calls C<render>
336             to perform actual rendering. Output is stored in C<< $c->response->body >>.
337              
338             =item render
339              
340             Arguments: ($c, $template, \%args)
341              
342             Renders the given template and returns output, or croaks on error.
343              
344             The template variables are set to C<%$args> if $args is a hashref, or
345             $C<< $c->stash >> otherwise. In either case the variables are augmented with
346             C<base> set to C< << $c->req->base >>, C<c> to C<$c> and C<name> to
347             C<< $c->config->{name} >>. Alternately, the C<CATALYST_VAR> configuration item
348             can be defined to specify the name of a template variable through which the
349             context reference (C<$c>) can be accessed. In this case, the C<c>, C<base> and
350             C<name> variables are omitted.
351              
352             =item config
353              
354             This method allows your view subclass to pass additional settings to
355             the TT configuration hash, or to set the options as below:
356              
357             =over 2
358              
359             =item C<CATALYST_VAR>
360              
361             Allows you to change the name of the Catalyst context object. If set, it will also
362             remove the base and name aliases, so you will have access them through <context>.
363              
364             For example:
365              
366             MyApp->config({
367             name => 'MyApp',
368             root => MyApp->path_to('root'),
369             'View::TT::Alloy' => {
370             CATALYST_VAR => 'Catalyst',
371             },
372             });
373              
374             F<message.tt2>:
375              
376             The base is [% Catalyst.req.base %]
377             The name is [% Catalyst.config.name %]
378              
379             =item C<TEMPLATE_EXTENSION>
380              
381             A sufix to add when building the template name, when
382             C<< $c->stash->{template} >> is not set.
383              
384             For example:
385              
386             package MyApp::Controller::Test;
387             sub test : Local { .. }
388              
389             Would by default look for a template in C<< <root>/test/test >>.
390              
391             If you set TEMPLATE_EXTENSION to '.tt', it will look for
392             C<< <root>/test/test.tt >>.
393              
394             =back
395              
396             =back
397              
398             =head2 HELPERS
399              
400             The L<Catalyst::Helper::View::TT::Alloy> module is provided to create
401             your view module. It is invoked by the C<myapp_create.pl> script:
402              
403             $ script/myapp_create.pl view TT::Alloy TT::Alloy
404              
405             =head1 SUPPORT
406              
407             Catalyst Mailing List:
408              
409             L<http://lists.rawmode.org/mailman/listinfo/catalyst>
410              
411             =head1 SUBVERSION REPOSITORY
412              
413             The publicly viewable subversion code repository is at
414             L<http://html-formfu.googlecode.com/svn/trunk/Catalyst-View-TT-Alloy>.
415              
416             =head1 SEE ALSO
417              
418             L<Catalyst>, L<Catalyst::Helper::View::TT::Alloy>, L<Template::Alloy>
419              
420             =head1 AUTHORS
421              
422             Carl Franks, C<cfranks@cpan.org>
423              
424             Based on the code of C<Catalyst::View::TT>, by
425              
426             Sebastian Riedel, C<sri@cpan.org>
427              
428             Marcus Ramberg, C<mramberg@cpan.org>
429              
430             Jesse Sheidlower, C<jester@panix.com>
431              
432             Andy Wardley, C<abw@cpan.org>
433              
434             =head1 CONTRIBUTORS
435              
436             Moritz Onken, C<onken@netcubed.de>
437              
438             =head1 COPYRIGHT
439              
440             This program is free software, you can redistribute it and/or modify it
441             under the same terms as Perl itself.
442              
443             =cut