File Coverage

blib/lib/Catalyst/View/HTML/CTPP2.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Catalyst::View::HTML::CTPP2;
2              
3 1     1   21722 use strict;
  1         2  
  1         46  
4              
5 1     1   2824 use MRO::Compat;
  1         4705  
  1         72  
6              
7 1     1   652 use HTML::CTPP2;
  0            
  0            
8             use File::Spec 'tmpdir';
9              
10             use base 'Catalyst::View';
11              
12             our $VERSION = '0.02';
13              
14             my %ctpp2_allow_params = map { $_, 1 }
15             qw/arg_stack_size code_stack_size steps_limit max_functions source_charset destination_charset/;
16              
17              
18             =head1 NAME
19              
20             Catalyst::View::HTML::CTPP2 - HTML::CTPP2 View Class
21              
22             =head1 SYNOPSIS
23              
24             # use the helper
25             create.pl view HTML::CTPP2 HTML::CTPP2
26              
27             # lib/MyApp/View/HTML/CTPP2.pm
28             package MyApp::View::HTML::CTPP2;
29              
30             use base 'Catalyst::View::HTML::CTPP2';
31              
32             __PACKAGE__->config(
33             INCLUDE_PATH => [
34             MyApp->path_to( 'root', 'src' ),
35             MyApp->path_to( 'root', 'lib' )
36             ),
37             TEMPLATE_EXTENSION => '.ctpp2c',
38              
39             file_cache => 1,
40             file_cache_time => 24*60*60,
41             file_cache_dir => '/tmp/myapp_cache',
42              
43             arg_stack_size => 1024,
44             .....
45             source_charset => 'CP1251',
46             destination_charset => 'utf-8'
47              
48             );
49              
50             1;
51              
52             # Meanwhile, maybe in an 'end' action
53             $c->forward('MyApp::View::HTML::CTPP2');
54              
55             =head1 DESCRIPTION
56              
57             This is the C<HTML::CTPP2> view class. Your subclass should inherit from this
58             class.
59              
60             =head2 METHODS
61              
62             =over 4
63              
64             =item new
65              
66             Internally used by C<Catalyst>. Used to configure some internal stuff.
67              
68             =cut
69              
70             sub new {
71             my ($class, $c, $args) = @_;
72              
73             my $config = {
74             TEMPLATE_EXTENSION => '.ctpp2',
75             %{$class->config},
76             %{$args}
77             };
78              
79             if (!(ref $config->{INCLUDE_PATH} eq 'ARRAY')) {
80             my $delim = $config->{DELIMITER};
81             my @include_path = _coerce_paths($config->{INCLUDE_PATH}, $delim);
82              
83             if (!@include_path) {
84             my $root = $c->config->{root};
85             my $base = Path::Class::dir($root, 'base');
86             @include_path = ("$root", "$base");
87             }
88              
89             $config->{INCLUDE_PATH} = \@include_path;
90             }
91              
92              
93             if (!exists $config->{file_cache} || $config->{file_cache} != 0) {
94             if ( (exists $config->{file_cache_time} && $config->{file_cache_time} > 0)
95             || exists $config->{file_cache_dir}
96             || (!exists $config->{file_cache_time} && !exists $config->{file_cache_dir}))
97             {
98             $config->{file_cache_time} ||= 60 * 60;
99             $config->{file_cache_dir} ||= Path::Class::dir(File::Spec->tmpdir, $c->config->{name});
100             }
101             }
102              
103             for (keys %{$config}) {
104             $config->{ctpp2_init_args}{$_} = delete $config->{$_} if exists $ctpp2_allow_params{$_};
105             }
106              
107             my $self = $class->next::method($c, {%$config});
108              
109             $self->config($config);
110              
111             $self->{ctpp2_obj} = $self->_init_ctpp2($config->{ctpp2_init_args});
112              
113             return $self;
114             }
115              
116             =item process
117              
118             Renders the template specified in C<< $c->stash->{template} >> or C<<
119             $c->request->match >>.
120             Template params are set up from the contents of C<< $c->stash >>,
121             augmented with C<base> set to C<< $c->req->base >> and C<name> to
122             C<< $c->config->{name} >>. Output is stored in C<< $c->response->body >>.
123              
124             =cut
125              
126             sub process {
127             my ($self, $c) = @_;
128              
129             my $template = $c->stash->{template} || $c->req->match . $self->config->{TEMPLATE_EXTENSION};
130              
131             unless (defined $template) {
132             $c->log->debug('No template specified for rendering') if $c->debug;
133             return 0;
134             }
135              
136             my $output = $self->render($c, $template);
137              
138             unless ($c->response->headers->content_type) {
139             $c->response->headers->content_type('text/html; charset=utf-8');
140             }
141              
142             $c->response->body($output);
143              
144             return 1;
145             }
146              
147             =item render
148              
149             Renders the given template and returns output. Template params are set up
150             either from the contents of C<%$args> if $args is a hashref, or C<< $c->stash >>,
151             augmented with C<base> set to C<< $c->req->base >> and C<name> to
152             C<< $c->config->{name} >>.
153              
154             =cut
155              
156             sub render {
157             my ($self, $c, $filename, $args) = @_;
158              
159             $c->log->debug(qq/Rendering template "$filename"/) if $c->debug;
160              
161             my $ctpp2_obj = $self->{ctpp2_obj};
162             $ctpp2_obj->include_dirs($self->{INCLUDE_PATH});
163              
164             my $bytecode = $self->_get_bytecode($filename);
165              
166             my $template_params = $args && ref($args) eq 'HASH' ? $args : $c->stash;
167              
168             $ctpp2_obj->param(
169             { base => $c->req->base,
170             name => $c->config->{name},
171             %$template_params
172             }
173             );
174              
175             $c->log->debug("Dumping template parameters:\n" . $ctpp2_obj->dump_params) if $c->debug;
176              
177             my $ctpp2_error = $ctpp2_obj->get_last_error();
178              
179             if ($ctpp2_error->{error_code}) {
180             my $error = sprintf(
181             "CTPP2-error: %s\nLine: %s\nPosition: %s\n",
182             $ctpp2_error->{error_str},
183             $ctpp2_error->{line}, $ctpp2_error->{pos}
184             );
185              
186             $self->{ctpp2_obj} = $self->_init_ctpp2($self->{ctpp2_init_args});
187             $c->error($error);
188             }
189              
190             my $output;
191              
192             eval { $output = $ctpp2_obj->output($bytecode) };
193              
194             if (my $error = $@) {
195             chomp $error;
196             $error = qq/Couldn't render template "$filename". Error: "$error"/;
197              
198             $c->log->error($error);
199             $c->error($error);
200             }
201              
202             $ctpp2_obj->clear_params();
203              
204             return @{$c->error} ? 0 : $output;
205             }
206              
207             =item config
208              
209             This allows your view subclass to pass additional settings to the
210             HTML::CTPP2 config-hash.
211              
212             =back
213              
214             =cut
215              
216             =head1 Template Configuration
217              
218             =head2 PATH CONFIGURATION AND TEMPLATE EXTENSION
219              
220             =head3 INCLUDE_PATH
221              
222             The C<INCLUDE_PATH> is used to specify one or more directories in which
223             template files are located. When a template is requested that isn't
224             defined locally as a C<BLOCK>, each of the C<INCLUDE_PATH> directories is
225             searched in turn to locate the template file. Multiple directories
226             can be specified as a reference to a list or as a single string where
227             each directory is delimited by 'C<:>'.
228              
229             __PACKAGE__->config(
230             INCLUDE_PATH => MyApp->path_to('root', 'src')
231             );
232              
233             __PACKAGE__->config(
234             INCLUDE_PATH => '/myapp/path1:/myapp/path2:path3'
235             );
236              
237             __PACKAGE__->config(
238             INCLUDE_PATH => [
239             MyApp->path_to('root', 'src'),
240             MyApp->path_to('root', 'lib')
241             ]
242             );
243              
244             On Win32 systems, a little extra magic is invoked, ignoring delimiters
245             that have 'C<:>' followed by a 'C</>' or 'C<\>'. This avoids confusion when using
246             directory names like 'C<C:\Blah Blah>'.
247              
248              
249             =head3 DELIMITER
250              
251             Used to provide an alternative delimiter character sequence for
252             separating paths specified in the C<INCLUDE_PATH>. The default
253             value for C<DELIMITER> is 'C<:>'.
254              
255             __PACKAGE__->config(
256             DELIMITER => '; ',
257             INCLUDE_PATH => '/myapp/path1;/myapp/path2;path3'
258             );
259              
260             On Win32 systems, the default delimiter is a little more intelligent,
261             splitting paths only on 'C<:>' characters that aren't followed by a 'C</>'.
262             This means that the following should work as planned, splitting the
263             C<INCLUDE_PATH> into 2 separate directories, C<C:/foo> and C<C:/bar>.
264              
265             # on Win32 only
266             __PACKAGE__->config(
267             INCLUDE_PATH => 'C:/Foo:C:/Bar'
268             );
269              
270             However, if you're using Win32 then it's recommended that you
271             explicitly set the C<DELIMITER> character to something else (e.g. 'C<;>')
272             rather than rely on this subtle magic.
273              
274              
275             =head3 TEMPLATE_EXTENSION
276              
277             If C<TEMPLATE_EXTENSION> is defined then use template files with the
278             C<TEMPLATE_EXTENSION> extension will be loaded. Default extension - 'C<.ctpp2>'
279              
280             __PACKAGE__->config(
281             TEMPLATE_EXTENSION => '.myext'
282             );
283              
284              
285             =head2 CACHING
286              
287             If any of parameters C<file_cache> ( and > 0 ), C<file_cache_time>, C<file_cache_dir>
288             is defined - cache will be used. Default value - caching is off.
289              
290             =head3 file_cache
291              
292             Set use caching or not. Integer (default - C<0 [caching off]>).
293              
294             #caching is on
295             __PACKAGE__->config(
296             file_cache => 1,
297             file_cache_time => 24*60*60,
298             file_cache_dir => '/tmp/myapp_cache'
299             );
300              
301             #caching is off
302             __PACKAGE__->config(
303             file_cache => 0,
304             file_cache_time => 24*60*60
305              
306             );
307              
308              
309             =head3 file_cache_time
310              
311             This value can be set to control how many long the
312             template cached before checking to see if the source template has
313             changed. Default cache time - C<1 hour>.
314              
315             #set cache time 1 day
316             __PACKAGE__->config(
317             file_cache_time => 24*60*60
318             );
319              
320              
321             =head3 file_cache_dir
322              
323             The C<file_cache_dir> option is used to specify an alternate directory which compiled
324             template files should be saved.
325              
326             #set cache directory
327             #is '/tmp/catalysts/myapp'
328              
329             __PACKAGE__->config(
330             file_cache_dir => '/tmp/catalysts/myapp'
331             );
332              
333              
334             =head2 CTPP2-Params
335              
336             See here - L<HTML::CTPP2>
337              
338             =cut
339              
340             sub _init_ctpp2 {
341             my ($self, $args) = @_;
342              
343             return HTML::CTPP2->new(%{$args});
344             }
345              
346             sub _coerce_paths {
347             my ($paths, $dlim) = shift;
348              
349             return () if (!$paths);
350             return @{$paths} if (ref $paths eq 'ARRAY');
351              
352             $dlim = ($^O eq 'MSWin32') ? ':(?!\\/)' : ':' unless (defined $dlim);
353              
354             return split(/$dlim/, $paths);
355             }
356              
357             sub _get_bytecode {
358             my ($self, $filename) = @_;
359              
360             my $filename_cmpl = Path::Class::file($self->{file_cache_dir}, $filename . 'c');
361              
362             if (exists $self->{file_cache_dir}) {
363             my $time_now = time;
364             my $file_mtime = (lstat $filename_cmpl)[9];
365              
366             my $bytecode;
367              
368             if (-e $filename_cmpl && ($time_now - $file_mtime < $self->{file_cache_time})) {
369             $bytecode = $self->{ctpp2_obj}->load_bytecode($filename_cmpl);
370             }
371             else {
372             my $filename_cmpl_path = Path::Class::file($filename_cmpl)->dir;
373              
374             eval { $filename_cmpl_path->mkpath } if (!-e $filename_cmpl_path);
375              
376             $bytecode = $self->{ctpp2_obj}->parse_template($filename);
377             $bytecode->save($filename_cmpl);
378             }
379             return $bytecode;
380             }
381             else {
382             return $self->{ctpp2_obj}->parse_template($filename);
383             }
384             }
385              
386             =head1 SEE ALSO
387              
388             L<HTML::CTPP2>, L<Catalyst>, L<Catalyst::Base>.
389              
390             =head1 AUTHOR
391              
392             Victor M Elfimov (victor@sols.ru)
393              
394             =head1 COPYRIGHT
395              
396             This program is free software, you can redistribute it and/or modify it
397             under the same terms as Perl itself.
398              
399             =cut
400              
401             1;
402