File Coverage

blib/lib/Catalyst/View/Xslate.pm
Criterion Covered Total %
statement 74 83 89.1
branch 18 24 75.0
condition 4 9 44.4
subroutine 16 17 94.1
pod 4 5 80.0
total 116 138 84.0


line stmt bran cond sub pod time code
1             package Catalyst::View::Xslate;
2 5     5   7617744 use Moose;
  5         428253  
  5         41  
3 5     5   31336 use Moose::Util::TypeConstraints qw(coerce from where via subtype);
  5         11  
  5         57  
4 5     5   4783 use Encode;
  5         9746  
  5         510  
5 5     5   4595 use Text::Xslate;
  5         48530  
  5         236  
6 5     5   756 use namespace::autoclean;
  5         7621  
  5         54  
7 5     5   343 use Scalar::Util qw/blessed weaken/;
  5         11  
  5         264  
8 5     5   29 use File::Find ();
  5         10  
  5         7031  
9              
10             our $VERSION = '0.00019';
11              
12             extends 'Catalyst::View';
13              
14             with 'Catalyst::Component::ApplicationAttribute';
15              
16             has catalyst_var => (
17             is => 'rw',
18             isa => 'Str',
19             default => 'c'
20             );
21              
22             has template_extension => (
23             is => 'rw',
24             isa => 'Str',
25             default => '.tx'
26             );
27              
28             has content_charset => (
29             is => 'rw',
30             isa => 'Str',
31             default => 'UTF-8'
32             );
33              
34             has encode_body => (
35             is => 'rw',
36             isa => 'Bool',
37             default => 1,
38             );
39              
40             my $clearer = sub { $_[0]->clear_xslate };
41              
42             has path => (
43             is => 'rw',
44             isa => 'ArrayRef',
45             trigger => $clearer,
46             lazy => 1, builder => '_build_path',
47             );
48              
49 18     18   151 sub _build_path { return [ shift->_app->path_to('root') ] }
50              
51             has cache_dir => (
52             is => 'rw',
53             isa => 'Str',
54             trigger => $clearer,
55             );
56              
57             has cache => (
58             is => 'rw',
59             isa => 'Int',
60             default => 1,
61             trigger => $clearer,
62             );
63              
64             has function => (
65             is => 'rw',
66             isa => 'HashRef',
67             default => sub { +{} },
68             trigger => $clearer,
69             );
70              
71             has footer => (
72             is => 'rw',
73             isa => 'ArrayRef',
74             default => sub { +[] },
75             trigger => $clearer
76             );
77              
78             has header => (
79             is => 'rw',
80             isa => 'ArrayRef',
81             default => sub { +[] },
82             trigger => $clearer
83             );
84              
85             has module => (
86             is => 'rw',
87             isa => 'ArrayRef',
88             default => sub { +[] },
89             trigger => $clearer,
90             );
91              
92             has input_layer => (
93             is => 'rw',
94             isa => 'Str',
95             trigger => $clearer,
96             );
97              
98             has syntax => (
99             is => 'rw',
100             isa => 'Str',
101             trigger => $clearer,
102             );
103              
104             has escape => (
105             is => 'rw',
106             isa => 'Str',
107             trigger => $clearer,
108             );
109              
110             has type => (
111             is => 'rw',
112             isa => 'Str',
113             trigger => $clearer,
114             );
115              
116             has suffix => (
117             is => 'rw',
118             isa => 'Str',
119             trigger => $clearer,
120             default => '.tx',
121             );
122              
123             has verbose => (
124             is => 'rw',
125             isa => 'Int',
126             trigger => $clearer,
127             );
128              
129             has xslate => (
130             is => 'rw',
131             isa => 'Text::Xslate',
132             clearer => 'clear_xslate',
133             lazy => 1, builder => '_build_xslate',
134             );
135              
136             has [qw/line_start tag_start tag_end/] => (is=>'rw', isa=>'Str');
137             has [qw/warn_handler die_handler pre_process_handler/] => (is=>'rw', isa=>'CodeRef');
138              
139              
140             my $expose_methods_tc = subtype 'HashRef', where { $_ };
141             coerce $expose_methods_tc,
142             from 'ArrayRef',
143             via {
144             my %values = map { $_ => $_ } @$_;
145             return \%values;
146             };
147              
148             has expose_methods => (
149             is => 'ro',
150             isa => $expose_methods_tc,
151             predicate => 'has_expose_methods',
152             coerce => 1,
153             );
154              
155             has preload => (
156             is => 'ro',
157             isa => 'Bool',
158             default => 1,
159             );
160              
161             sub _build_xslate {
162 20     20   33 my $self = shift;
163              
164 20         91 my $app = $self->_app;
165 20         770 my $name = $app;
166 20         70 $name =~ s/::/_/g;
167              
168             my %args = (
169             path => $self->path,
170             cache_dir => $self->cache_dir || File::Spec->catdir(File::Spec->tmpdir, $name),
171 20   33     693 map { ($_ => $self->$_) }
  100         3799  
172             qw( cache footer function header module )
173             );
174              
175             # optional stuff
176 20         94 foreach my $field ( qw( input_layer syntax escape verbose suffix type line_start tag_start tag_end warn_handler die_handler pre_process_handler) ) {
177 240 100       9260 if (defined(my $value = $self->$field)) {
178 30         81 $args{$field} = $value;
179             }
180             }
181              
182 20         383 return Text::Xslate->new(%args);
183             }
184              
185             sub BUILD {
186 19     19 0 29551 my $self = shift;
187 19 50       837 if ($self->preload) {
188 19         124 $self->preload_templates();
189             }
190             }
191              
192             sub preload_templates {
193 19     19 1 34 my $self = shift;
194 19         763 my ( $paths, $suffix ) = ( $self->path, $self->suffix );
195 19         755 my $xslate = $self->xslate;
196 19         85 foreach my $path (@$paths) {
197             File::Find::find( sub {
198 136 100   136   485844 if (/\Q$suffix\E$/) {
199 96         162 my $file = $File::Find::name;
200 96         472 $file =~ s/\Q$path\E .//xsm;
201 96         3042 $xslate->load_file($file);
202             }
203 19         953 }, $path);
204             }
205             }
206              
207             sub process {
208 5     5 1 102979 my ($self, $c) = @_;
209              
210 5         29 my $stash = $c->stash;
211 5   66     359 my $template = $stash->{template} || $c->action . $self->template_extension;
212              
213 5 50       28 if (! defined $template) {
214 0 0       0 $c->log->debug('No template specified for rendering') if $c->debug;
215 0         0 return 0;
216             }
217              
218 5         11 my $output = eval {
219 5         49 $self->render( $c, $template, $stash );
220             };
221 5 50       29 if (my $err = $@) {
222 0         0 return $self->_rendering_error($c, $err);
223             }
224              
225 5         131 my $res = $c->response;
226 5 100       52 if (! $res->content_type) {
227 4         1277 $res->content_type('text/html; charset=' . $self->content_charset);
228             }
229              
230 5 100       1131 if ( $self->encode_body ) {
231 4         143 $res->body( encode($self->content_charset, $output) );
232             }
233             else {
234 1         31 $res->body( $output );
235             }
236              
237 5         201923 return 1;
238             }
239              
240             sub build_exposed_method {
241 6     6 1 14 my ( $self, $ctx, $code ) = @_;
242 6         26 my $weak_ctx = $ctx;
243 6         18 weaken $weak_ctx;
244              
245 6     4   41 return sub { $self->$code($weak_ctx, @_) };
  4         8762  
246             }
247              
248             sub render {
249 14     14 1 438073 my ($self, $c, $template, $vars) = @_;
250              
251 14 100       60 $vars = $vars ? $vars : $c->stash;
252              
253 14 100       929 if ($self->has_expose_methods) {
254 3         9 foreach my $exposed_method( keys %{$self->expose_methods} ) {
  3         120  
255 6 50       215 if(my $code = $self->can( $self->expose_methods->{$exposed_method} )) {
256 6         28 $vars->{$exposed_method} = $self->build_exposed_method($c, $code);
257             } else {
258 0         0 Catalyst::Exception->throw( "$exposed_method not found in Xslate view" );
259             }
260              
261             }
262             }
263              
264             local $vars->{ $self->catalyst_var } =
265 14   33     566 $vars->{ $self->catalyst_var } || $c;
266              
267 14 100       49 if(ref $template eq 'SCALAR') {
268 3         107 return $self->xslate->render_string( $$template, $vars );
269             } else {
270 11         404 return $self->xslate->render($template, $vars );
271             }
272             }
273              
274             sub _rendering_error {
275 0     0     my ($self, $c, $err) = @_;
276 0           my $error = qq/Couldn't render template "$err"/;
277 0           $c->log->error($error);
278 0           $c->error($error);
279 0           return 0;
280             }
281              
282             __PACKAGE__->meta->make_immutable();
283              
284             1;
285              
286             __END__
287              
288             =head1 NAME
289              
290             Catalyst::View::Xslate - Text::Xslate View Class
291              
292             =head1 SYNOPSIS
293              
294             package MyApp::View::Xslate;
295             use Moose;
296             extends 'Catalyst::View::Xslate';
297              
298             1;
299              
300             =head1 VIEW CONFIGURATION
301              
302             You may specify the following configuration items in from your config file
303             or directly on the view object.
304              
305             =head2 catalyst_var
306              
307             The name used to refer to the Catalyst app object in the template
308              
309             =head2 template_extension
310              
311             The suffix used to auto generate the template name from the action name
312             (when you do not explicitly specify the template filename);
313              
314             Do not confuse this with the C<suffix> option, which is passed directly to
315             the Text::Xslate object instance. This option works on the filename used
316             for the initial request, while C<suffix> controls what C<cascade> and
317             C<include> directives do inside Text::Xslate.
318              
319             =head2 content_charset
320              
321             The charset used to output the response body. The value defaults to 'UTF-8'.
322              
323             =head2 encode_body
324              
325             By default, output will be encoded to C<content_charset>.
326             You can set it to 0 to disable this behavior.
327             (you need to do this if you're using C<Catalyst::Plugin::Unicode::Encoding>)
328              
329             B<NOTE> Starting with L<Catalyst> version 5.90080 Catalyst will automatically
330             encode to UTF8 any text like body responses. You should either turn off the
331             body encoding step in this view using this attribute OR disable this feature
332             in the application (your subclass of Catalyst.pm).
333              
334             MyApp->config(encoding => undef);
335              
336             Failure to do so will result in double encoding.
337              
338             =head2 Text::Xslate CONFIGURATION
339              
340             The following parameters are passed to the Text::Xslate constructor.
341             When reset during the life cyle of the Catalyst app, these parameters will
342             cause the previously created underlying Text::Xslate object to be cleared
343              
344             =head2 path
345              
346             =head2 cache_dir
347              
348             =head2 cache
349              
350             =head2 header
351              
352             =head2 escape
353              
354             =head2 type
355              
356             =head2 footer
357              
358             =head2 function
359              
360             =head2 input_layer
361              
362             =head2 module
363              
364             =head2 syntax
365              
366             =head2 verbose
367              
368             =head2 line_start
369              
370             =head2 tag_start
371              
372             =head2 tag_end
373              
374             =head2 warn_handler
375              
376             =head2 die_handler
377              
378             =head2 pre_process_handler
379              
380             =head2 suffix
381              
382              
383             Use this to enable TT2 compatible variable methods via Text::Xslate::Bridge::TT2 or Text::Xslate::Bridge::TT2Like
384              
385             package MyApp::View::Xslate;
386             use Moose;
387             extends 'Catalyst::View::Xslate';
388              
389             has '+module' => (
390             default => sub { [ 'Text::Xslate::Bridge::TT2Like' ] }
391             );
392              
393             =head1 preload
394              
395             Boolean flag indicating if templates should be preloaded. By default this is enabled.
396              
397             Preloading templates will basically cutdown the cost of template compilation for the first hit.
398              
399             =head2 expose_methods
400              
401             Use this option to specify methods from the View object to be exposed in the
402             template. For example, if you have the following View:
403              
404             package MyApp::View::Xslate;
405             use Moose;
406             extends 'Catalyst::View::Xslate';
407              
408             sub foo {
409             my ( $self, $c, @args ) = @_;
410             return ...; # do something with $self, $c, @args
411             }
412              
413             then by setting expose_methods, you will be able to use $foo() as a function in
414             the template:
415              
416             <: $foo("a", "b", "c") # calls $view->foo( $c, "a", "b", "c" ) :>
417              
418             C<expose_methods> takes either a list of method names to expose, or a hash reference, in order to alias it differently in the template.
419              
420             MyApp::View::Xslate->new(
421             # exposes foo(), bar(), baz() in the template
422             expose_methods => [ qw(foo bar baz) ]
423             );
424              
425             MyApp::View::Xslate->new(
426             # exposes $foo_alias(), $bar_alias(), $baz_alias() in the template,
427             # but they will in turn call foo(), bar(), baz(), on the view object.
428             expose_methods => {
429             foo => "foo_alias",
430             bar => "bar_alias",
431             baz => "baz_alias",
432             }
433             );
434              
435             NOTE: you can mangle the process of building the exposed methods, see C<build_exposed_method>.
436              
437             =head1 METHODS
438              
439             =head1 C<$view->process($c)>
440              
441             Called by Catalyst.
442              
443             =head2 C<$view->render($c, $template, \%vars)>
444              
445             Renders the given C<$template> using variables \%vars.
446              
447             C<$template> can be a template file name, or a scalar reference to a template
448             string.
449              
450             $view->render($c, "/path/to/a/template.tx", \%vars );
451              
452             $view->render($c, \'This is a xslate template!', \%vars );
453              
454             =head2 C<$view->preload_templates>
455              
456             Preloads templates in $view-E<gt>path.
457              
458             =head2 C<$view->build_exposed_method>
459              
460             Hook point for mangling the building process of exposed methods.
461              
462             =head1 AUTHOR
463              
464             Copyright (c) 2010 Daisuke Maki C<< <daisuke@endeworks.jp> >>
465              
466             =head1 LICENSE
467              
468             This program is free software; you can redistribute it and/or modify it
469             under the same terms as Perl itself.
470              
471             See http://www.perl.com/perl/misc/Artistic.html
472              
473             =cut