File Coverage

blib/lib/Catalyst/View/Xslate.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Catalyst::View::Xslate;
2 1     1   1614 use Moose;
  0            
  0            
3             use Moose::Util::TypeConstraints qw(coerce from where via subtype);
4             use Encode;
5             use Text::Xslate;
6             use namespace::autoclean;
7             use Scalar::Util qw/blessed weaken/;
8             use File::Find ();
9              
10             our $VERSION = '0.00017';
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             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             my $expose_methods_tc = subtype 'HashRef', where { $_ };
137             coerce $expose_methods_tc,
138             from 'ArrayRef',
139             via {
140             my %values = map { $_ => $_ } @$_;
141             return \%values;
142             };
143              
144             has expose_methods => (
145             is => 'ro',
146             isa => $expose_methods_tc,
147             predicate => 'has_expose_methods',
148             coerce => 1,
149             );
150              
151             has preload => (
152             is => 'ro',
153             isa => 'Bool',
154             default => 1,
155             );
156              
157             sub _build_xslate {
158             my $self = shift;
159              
160             my $app = $self->_app;
161             my $name = $app;
162             $name =~ s/::/_/g;
163              
164             my %args = (
165             path => $self->path,
166             cache_dir => $self->cache_dir || File::Spec->catdir(File::Spec->tmpdir, $name),
167             map { ($_ => $self->$_) }
168             qw( cache footer function header module )
169             );
170              
171             # optional stuff
172             foreach my $field ( qw( input_layer syntax escape verbose suffix type ) ) {
173             if (defined(my $value = $self->$field)) {
174             $args{$field} = $value;
175             }
176             }
177              
178             return Text::Xslate->new(%args);
179             }
180              
181             sub BUILD {
182             my $self = shift;
183             if ($self->preload) {
184             $self->preload_templates();
185             }
186             }
187              
188             sub preload_templates {
189             my $self = shift;
190             my ( $paths, $suffix ) = ( $self->path, $self->suffix );
191             my $xslate = $self->xslate;
192             foreach my $path (@$paths) {
193             File::Find::find( sub {
194             if (/\Q$suffix\E$/) {
195             my $file = $File::Find::name;
196             $file =~ s/\Q$path\E .//xsm;
197             $xslate->load_file($file);
198             }
199             }, $path);
200             }
201             }
202              
203             sub process {
204             my ($self, $c) = @_;
205              
206             my $stash = $c->stash;
207             my $template = $stash->{template} || $c->action . $self->template_extension;
208              
209             if (! defined $template) {
210             $c->log->debug('No template specified for rendering') if $c->debug;
211             return 0;
212             }
213              
214             my $output = eval {
215             $self->render( $c, $template, $stash );
216             };
217             if (my $err = $@) {
218             return $self->_rendering_error($c, $err);
219             }
220              
221             my $res = $c->response;
222             if (! $res->content_type) {
223             $res->content_type('text/html; charset=' . $self->content_charset);
224             }
225              
226             if ( $self->encode_body ) {
227             $res->body( encode($self->content_charset, $output) );
228             }
229             else {
230             $res->body( $output );
231             }
232              
233             return 1;
234             }
235              
236             sub build_exposed_method {
237             my ( $self, $ctx, $code ) = @_;
238             return sub { $self->$code($ctx, @_) };
239             }
240              
241             sub render {
242             my ($self, $c, $template, $vars) = @_;
243              
244             $vars = $vars ? $vars : $c->stash;
245              
246             if ($self->has_expose_methods) {
247             foreach my $exposed_method( keys %{$self->expose_methods} ) {
248             if(my $code = $self->can( $self->expose_methods->{$exposed_method} )) {
249             my $weak_ctx = $c;
250             weaken $weak_ctx;
251             $vars->{$exposed_method} = $self->build_exposed_method($weak_ctx, $code);
252             } else {
253             Catalyst::Exception->throw( "$exposed_method not found in Xslate view" );
254             }
255              
256             }
257             }
258              
259             local $vars->{ $self->catalyst_var } =
260             $vars->{ $self->catalyst_var } || $c;
261              
262             if(ref $template eq 'SCALAR') {
263             return $self->xslate->render_string( $$template, $vars );
264             } else {
265             return $self->xslate->render($template, $vars );
266             }
267             }
268              
269             sub _rendering_error {
270             my ($self, $c, $err) = @_;
271             my $error = qq/Couldn't render template "$err"/;
272             $c->log->error($error);
273             $c->error($error);
274             return 0;
275             }
276              
277             __PACKAGE__->meta->make_immutable();
278              
279             1;
280              
281             __END__
282              
283             =head1 NAME
284              
285             Catalyst::View::Xslate - Text::Xslate View Class
286              
287             =head1 SYNOPSIS
288              
289             package MyApp::View::Xslate;
290             use Moose;
291             extends 'Catalyst::View::Xslate';
292              
293             1;
294              
295             =head1 VIEW CONFIGURATION
296              
297             You may specify the following configuration items in from your config file
298             or directly on the view object.
299              
300             =head2 catalyst_var
301              
302             The name used to refer to the Catalyst app object in the template
303              
304             =head2 template_extension
305              
306             The suffix used to auto generate the template name from the action name
307             (when you do not explicitly specify the template filename);
308              
309             Do not confuse this with the C<suffix> option, which is passed directly to
310             the Text::Xslate object instance. This option works on the filename used
311             for the initial request, while C<suffix> controls what C<cascade> and
312             C<include> directives do inside Text::Xslate.
313              
314             =head2 content_charset
315              
316             The charset used to output the response body. The value defaults to 'UTF-8'.
317              
318             =head2 encode_body
319              
320             By default, output will be encoded to C<content_charset>.
321             You can set it to 0 to disable this behavior.
322             (you need to do this if you're using C<Catalyst::Plugin::Unicode::Encoding>)
323              
324             =head2 Text::Xslate CONFIGURATION
325              
326             The following parameters are passed to the Text::Xslate constructor.
327             When reset during the life cyle of the Catalyst app, these parameters will
328             cause the previously created underlying Text::Xslate object to be cleared
329              
330             =head2 path
331              
332             =head2 cache_dir
333              
334             =head2 cache
335              
336             =head2 header
337              
338             =head2 escape
339              
340             =head2 type
341              
342             =head2 footer
343              
344             =head2 function
345              
346             =head2 input_layer
347              
348             =head2 module
349              
350             =head2 syntax
351              
352             =head2 verbose
353              
354             =head2 suffix
355              
356             Use this to enable TT2 compatible variable methods via Text::Xslate::Bridge::TT2 or Text::Xslate::Bridge::TT2Like
357              
358             package MyApp::View::Xslate;
359             use Moose;
360             extends 'Catalyst::View::Xslate';
361              
362             has '+module' => (
363             default => sub { [ 'Text::Xslate::Bridge::TT2Like' ] }
364             );
365              
366             =head1 preload
367              
368             Boolean flag indicating if templates should be preloaded. By default this is enabled.
369              
370             Preloading templates will basically cutdown the cost of template compilation for the first hit.
371              
372             =head2 expose_methods
373              
374             Use this option to specify methods from the View object to be exposed in the
375             template. For example, if you have the following View:
376              
377             package MyApp::View::Xslate;
378             use Moose;
379             extends 'Catalyst::View::Xslate';
380              
381             sub foo {
382             my ( $self, $c, @args ) = @_;
383             return ...; # do something with $self, $c, @args
384             }
385              
386             then by setting expose_methods, you will be able to use $foo() as a function in
387             the template:
388              
389             <: $foo("a", "b", "c") # calls $view->foo( $c, "a", "b", "c" ) :>
390              
391             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.
392              
393             MyApp::View::Xslate->new(
394             # exposes foo(), bar(), baz() in the template
395             expose_methods => [ qw(foo bar baz) ]
396             );
397              
398             MyApp::View::Xslate->new(
399             # exposes $foo_alias(), $bar_alias(), $baz_alias() in the template,
400             # but they will in turn call foo(), bar(), baz(), on the view object.
401             expose_methods => {
402             foo => "foo_alias",
403             bar => "bar_alias",
404             baz => "baz_alias",
405             }
406             );
407              
408             NOTE: you can mangle the process of building the exposed methods, see C<build_exposed_method>.
409              
410             =head1 METHODS
411              
412             =head1 C<$view->process($c)>
413              
414             Called by Catalyst.
415              
416             =head2 C<$view->render($c, $template, \%vars)>
417              
418             Renders the given C<$template> using variables \%vars.
419              
420             C<$template> can be a template file name, or a scalar reference to a template
421             string.
422              
423             $view->render($c, "/path/to/a/template.tx", \%vars );
424              
425             $view->render($c, \'This is a xslate template!', \%vars );
426              
427             =head2 C<$view->preload_templates>
428              
429             Preloads templates in $view-E<gt>path.
430              
431             =head2 C<$view->build_exposed_method>
432              
433             Hook point for mangling the building process of exposed methods.
434              
435             =head1 AUTHOR
436              
437             Copyright (c) 2010 Daisuke Maki C<< <daisuke@endeworks.jp> >>
438              
439             =head1 LICENSE
440              
441             This program is free software; you can redistribute it and/or modify it
442             under the same terms as Perl itself.
443              
444             See http://www.perl.com/perl/misc/Artistic.html
445              
446             =cut