File Coverage

blib/lib/Catalyst/View/TT/Alloy.pm
Criterion Covered Total %
statement 75 87 86.2
branch 20 34 58.8
condition 3 9 33.3
subroutine 13 13 100.0
pod 3 3 100.0
total 114 146 78.0


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