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   853 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.00018';
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             B<NOTE> Starting with L<Catalyst> version 5.90080 Catalyst will automatically
325             encode to UTF8 any text like body responses. You should either turn off the
326             body encoding step in this view using this attribute OR disable this feature
327             in the application (your subclass of Catalyst.pm).
328              
329             MyApp->config(encoding => undef);
330              
331             Failure to do so will result in double encoding.
332              
333             =head2 Text::Xslate CONFIGURATION
334              
335             The following parameters are passed to the Text::Xslate constructor.
336             When reset during the life cyle of the Catalyst app, these parameters will
337             cause the previously created underlying Text::Xslate object to be cleared
338              
339             =head2 path
340              
341             =head2 cache_dir
342              
343             =head2 cache
344              
345             =head2 header
346              
347             =head2 escape
348              
349             =head2 type
350              
351             =head2 footer
352              
353             =head2 function
354              
355             =head2 input_layer
356              
357             =head2 module
358              
359             =head2 syntax
360              
361             =head2 verbose
362              
363             =head2 suffix
364              
365             Use this to enable TT2 compatible variable methods via Text::Xslate::Bridge::TT2 or Text::Xslate::Bridge::TT2Like
366              
367             package MyApp::View::Xslate;
368             use Moose;
369             extends 'Catalyst::View::Xslate';
370              
371             has '+module' => (
372             default => sub { [ 'Text::Xslate::Bridge::TT2Like' ] }
373             );
374              
375             =head1 preload
376              
377             Boolean flag indicating if templates should be preloaded. By default this is enabled.
378              
379             Preloading templates will basically cutdown the cost of template compilation for the first hit.
380              
381             =head2 expose_methods
382              
383             Use this option to specify methods from the View object to be exposed in the
384             template. For example, if you have the following View:
385              
386             package MyApp::View::Xslate;
387             use Moose;
388             extends 'Catalyst::View::Xslate';
389              
390             sub foo {
391             my ( $self, $c, @args ) = @_;
392             return ...; # do something with $self, $c, @args
393             }
394              
395             then by setting expose_methods, you will be able to use $foo() as a function in
396             the template:
397              
398             <: $foo("a", "b", "c") # calls $view->foo( $c, "a", "b", "c" ) :>
399              
400             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.
401              
402             MyApp::View::Xslate->new(
403             # exposes foo(), bar(), baz() in the template
404             expose_methods => [ qw(foo bar baz) ]
405             );
406              
407             MyApp::View::Xslate->new(
408             # exposes $foo_alias(), $bar_alias(), $baz_alias() in the template,
409             # but they will in turn call foo(), bar(), baz(), on the view object.
410             expose_methods => {
411             foo => "foo_alias",
412             bar => "bar_alias",
413             baz => "baz_alias",
414             }
415             );
416              
417             NOTE: you can mangle the process of building the exposed methods, see C<build_exposed_method>.
418              
419             =head1 METHODS
420              
421             =head1 C<$view->process($c)>
422              
423             Called by Catalyst.
424              
425             =head2 C<$view->render($c, $template, \%vars)>
426              
427             Renders the given C<$template> using variables \%vars.
428              
429             C<$template> can be a template file name, or a scalar reference to a template
430             string.
431              
432             $view->render($c, "/path/to/a/template.tx", \%vars );
433              
434             $view->render($c, \'This is a xslate template!', \%vars );
435              
436             =head2 C<$view->preload_templates>
437              
438             Preloads templates in $view-E<gt>path.
439              
440             =head2 C<$view->build_exposed_method>
441              
442             Hook point for mangling the building process of exposed methods.
443              
444             =head1 AUTHOR
445              
446             Copyright (c) 2010 Daisuke Maki C<< <daisuke@endeworks.jp> >>
447              
448             =head1 LICENSE
449              
450             This program is free software; you can redistribute it and/or modify it
451             under the same terms as Perl itself.
452              
453             See http://www.perl.com/perl/misc/Artistic.html
454              
455             =cut