File Coverage

blib/lib/Catalyst/View/XSLT.pm
Criterion Covered Total %
statement 18 128 14.0
branch 0 66 0.0
condition 0 48 0.0
subroutine 6 12 50.0
pod 3 3 100.0
total 27 257 10.5


line stmt bran cond sub pod time code
1             package Catalyst::View::XSLT;
2              
3 1     1   16320 use strict;
  1         2  
  1         30  
4 1     1   3 use warnings;
  1         5  
  1         24  
5 1     1   4 use base 'Catalyst::View';
  1         3  
  1         356  
6 1     1   362 use Catalyst::View::XSLT::XML::LibXSLT;
  1         1  
  1         24  
7 1     1   670 use Data::Dumper;
  1         7880  
  1         57  
8 1     1   5 use File::Spec;
  1         1  
  1         1142  
9              
10             our $VERSION = '0.09';
11              
12             # check if this is a MS Windows
13             my $isMS = $^O eq 'MSWin32';
14              
15             =head1 NAME
16              
17             Catalyst::View::XSLT - XSLT View Class
18              
19             =head1 SYNOPSIS
20              
21             # use the helper to create your view
22             myapp_create.pl view XSLT XSLT
23              
24             # configure in lib/MyApp/View/XSLT.pm (can be done from a config file too)
25             package MyApp::View::XSLT;
26              
27             use base 'Catalyst::View::XSLT';
28              
29             __PACKAGE__->config(
30             # paths to the directories with templates
31             INCLUDE_PATH => [
32             MyApp->path_to( 'root', 'xslt' ),
33             MyApp->path_to( 'templates', 'xsl' ),
34             ],
35              
36             # default template extension to use
37             # when you don't provide template name
38             TEMPLATE_EXTENSION => '.xsl',
39              
40             # use this for debug purposes
41             # it will dump the the final (merged) config
42             DUMP_CONFIG => 1,
43              
44             # XML::LibXSLT specific configuration
45             LibXSLT => {
46             register_function => [
47             {
48             uri => 'urn:catalyst',
49             name => 'add',
50             subref => sub { return $_[0] + $_[1] },
51             },
52             {
53             uri => 'urn:foo',
54             name => 'Hello',
55             subref => sub { return 'Hello, Catalyst\'s user.' },
56             },
57             ],
58             },
59             );
60              
61             # don't need nothing more
62              
63             1;
64              
65             # in your controller(s) :
66             sub someAction : Local {
67              
68             # 'template' could be string or path to file
69             # see 'xml' for more info about string version
70              
71             # path to the template could be absolute
72             $c->stash->{template} = $c->config->{home} . 'root/some.xsl';
73              
74             # or relative
75             $c->stash->{template} = 'some.xsl'; # this file will be searched in include paths
76              
77             # or if you didn't provide any template name
78             # then the last chance is 'someAction.xsl' ($c->action . $config->{TEMPLATE_EXTENSION})
79              
80             # 'xml' could be string
81             $c->stash->{xml} =<<XML;
82             <root>
83             <level1>data</level>
84             </root>
85             XML
86             # or a relative path which will se searched in include paths
87             # $c->stash->{xml} = 'my.xml';
88              
89             # or an absolute path
90             # $c->stash->{xml} = '/some/where/around.xml';
91              
92             # add more subrefs (these will predefine config ones if they overlap)
93             $c->stash->{additional_register_function} = [
94             {
95             uri => 'urn:catalyst',
96             name => 'doIt',
97             subref => sub { return $obj->method(@_) },
98             }
99             ];
100              
101             # everything else in the stash will be used for parameters (<xsl:param name="param1" />)
102             $c->stash->{param1} = 'Param1 value';'
103             $c->stash->{param2} = 'Param2 value';
104             }
105              
106             # Meanwhile, maybe in an 'end' action
107              
108             $c->forward('MyApp::View::XSLT');
109              
110             # to use your registered functions in some.xsl:
111             <xsl:stylesheet
112             xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
113             xmlns:catalyst="urn:catalyst"
114             xmlns:foo="urn:foo"
115             version="1.0">
116             ...
117             <xsl:value-of select="catalyst:add(4, 5)" />
118             <xsl:value-of select="foo:Hello()" />
119             <xsl:value-of select="catalyst:doIt($param1, 3)" />
120             ...
121             </xsl:stylesheet>
122              
123             =head1 DESCRIPTION
124              
125             This is a C<XSLT> view class for Catalyst.
126             Your application should defined a view class which is a subclass of
127             this module. The easiest way to achieve this is using the
128             F<myapp_create.pl> script (where F<myapp> should be replaced with
129             whatever your application is called). This script is created as part
130             of the Catalyst setup.
131              
132             =head1 METHODS
133              
134             =over 4
135              
136             =item new
137              
138             The constructor for the XSLT view.
139             Reads the application config.
140              
141             =cut
142              
143             # this code is borrowed from Catalyst::View::TT
144             sub _coerce_paths {
145 0     0     my ( $paths, $dlim ) = shift;
146 0 0         return () if ( !$paths );
147 0 0         return @{$paths} if ( ref $paths eq 'ARRAY' );
  0            
148              
149             # tweak delim to ignore C:/
150 0 0         unless ( defined $dlim ) {
151 0 0         $dlim = ( $isMS ) ? ':(?!\\/)' : ':';
152             }
153 0           return split( /$dlim/, $paths );
154             }
155              
156              
157             sub new {
158 0     0 1   my ($proto, $c) = @_;
159              
160 0   0       my $class = ref $proto || $proto;
161              
162             # default configuration
163 0 0         my $config = {
164             # DEFAULT VALUES
165              
166             # this the default internal implementation
167             # can be overwritten in application or class config
168             PROCESSOR => 'Catalyst::View::XSLT::XML::LibXSLT',
169              
170             # default file extension for xslt files
171             TEMPLATE_EXTENSION => '.xsl',
172              
173             # don't dump XSLT view config by default
174             DUMP_CONFIG => 0,
175              
176             # DEFAULT VALUES END
177              
178              
179             # global app config
180 0 0         %{ $c->config->{'View::XSLT'} || {} },
181 0 0         %{ $c->config->{'V::XSLT'} || {} },
182              
183             # class' config has precedence
184 0           %{ $class->config() || {} },
185             };
186              
187              
188 0 0         if ( ! (ref $config->{INCLUDE_PATH} eq 'ARRAY') ) {
189 0           my $delim = $config->{DELIMITER};
190             my @include_path
191 0           = _coerce_paths( $config->{INCLUDE_PATH}, $delim );
192 0 0         if ( !@include_path ) {
193 0           my $root = $c->config->{root};
194 0           my $base = Path::Class::dir( $root, 'base' );
195 0           @include_path = ( "$root", "$base" );
196             }
197 0           $config->{INCLUDE_PATH} = \@include_path;
198             }
199              
200 0 0 0       if ( $c->debug && $config->{DUMP_CONFIG} ) {
201 0           $c->log->debug( 'XSLT Config: ', Dumper($config) );
202             }
203              
204 0           my $self = {};
205              
206 0           bless($self, $class);
207              
208 0           $self->{CONFIG} = $config;
209              
210 0           return $self;
211             }
212              
213             =item render
214              
215             Renders the template specified via C<< $template >>.
216             The template parameters are set to C<%$args> if $args is a hashref, or
217             C<< $c->stash >> otherwise.
218              
219             Templates are accepted as strings, filehandles or objects of the corresponding
220             view types (L<XML::LibXML::Document> for example).
221              
222             =cut
223              
224             sub render {
225 0     0 1   my ( $self, $c, $template, $args ) = @_;
226 0           my $basePath;
227              
228 0 0 0       unless ( $template =~ m/\</ || (ref($template) && $template->isa('GLOB')) || -e $template ||
      0        
      0        
      0        
      0        
229             ( ref($template) && !$template->isa('Path::Class::File') ) ) {
230 0           my $error;
231              
232 0           ($basePath, $error) = $self->_searchInIncPath($c, $template);
233              
234 0 0         if (defined $error) {
235 0           $c->error("Template [$template] does not exists in include path");
236 0           return 0;
237             } else {
238 0           $template = File::Spec->catfile($basePath, $template);
239             }
240             }
241              
242 0 0         unless ($basePath) {
243 0           $basePath = $c->config->{root};
244             }
245              
246 0           my $vars = {
247 0 0         (ref $args eq 'HASH' ? %$args : %{ $c->stash() }),
248             };
249              
250 0 0 0       unless (exists $vars->{ xml } && defined $vars->{ xml }) {
251 0           $c->log->error( 'No xml provided' );
252 0           return undef;
253             }
254              
255 0           my $xml = $vars->{xml};
256              
257             # if xml is not string (therefore is a file (what about file descriptors ?!))
258             # and is not existsting in the file system
259 0 0 0       unless ( $xml =~ m/\</ || (ref($template) && $xml->isa('GLOB')) || -e $xml ||
      0        
      0        
      0        
      0        
260             ( ref($xml) && !$xml->isa('Path::Class::File') ) ) {
261 0           my ($incPath, $error) = $self->_searchInIncPath($c, $xml);
262              
263 0 0         if (defined $error) {
264 0           $c->error("XML file [$xml] does not exists in include path");
265 0           return undef;
266             } else {
267 0           $vars->{xml} = File::Spec->catfile($incPath, $xml);
268             }
269             }
270              
271 0 0         $c->log->debug( qq{Rendering template "$template"} ) if $c->debug;
272              
273             # add runtime register_function(s) from stash
274 0 0 0       if (exists $vars->{additional_register_function} &&
275             ref($vars->{additional_register_function}) eq 'ARRAY' ) {
276 0           my @additional_subrefs = @{ $vars->{additional_register_function} };
  0            
277 0           delete $vars->{additional_register_function};
278              
279 0 0         unless (ref($self->{CONFIG}->{LibXSLT}->{register_function}) eq 'ARRAY') {
280 0           $self->{CONFIG}->{LibXSLT}->{register_function} = [];
281             }
282              
283             unshift(
284 0           @{ $self->{CONFIG}->{LibXSLT}->{register_function} },
  0            
285             @additional_subrefs
286             );
287             }
288              
289 0           my $processor = undef;
290 0           eval {
291 0           $processor = $self->_getProcessor()->new($c, $self->{CONFIG}->{LibXSLT});
292             };
293              
294 0 0 0       if ($@ && (! defined $processor)) {
  0 0          
295 0           $c->error("Could not instanciate XSLT processor: $@");
296 0           return undef;
297             } elsif (scalar @{$c->error}) {
298 0           return undef;
299             }
300              
301 0 0         $c->log->debug("Processing...") if $c->debug;
302 0           my ($output, $error) = $processor->process($template, $vars, $basePath);
303              
304 0 0         if ($error) {
305 0           chomp $error;
306 0           $error = qq{Couldn't render template "$template". Error: "$error"};
307 0           $c->error($error);
308 0           return undef;
309             }
310             else {
311 0           return $output;
312             }
313             }
314              
315             =item process
316              
317             Renders the template specified in C<< $c->stash->{template} >> or C<<
318             $c->action >>. Calls C<< render >> to perform actual rendering.
319             Template params are set up from the contents of C<< $c->stash >>.
320             Output is stored in C<< $c->response->body >>.
321              
322             =cut
323              
324             sub process {
325 0     0 1   my ( $self, $c ) = @_;
326              
327 0           my $template = undef;
328              
329 0 0 0       if (exists $c->stash->{template} && defined $c->stash->{template}) {
330 0           $template = delete $c->stash->{template};
331             } else {
332 0           my $actionName = $c->action;
333 0           my $ext = $self->{CONFIG}->{'TEMPLATE_EXTENSION'};
334              
335 0           $template = $actionName . $ext;
336 0 0         $c->log->debug( "Going to create template name from the action name and default extension: [$template]" ) if $c->debug;
337             }
338              
339 0 0         unless ($template) {
340             # probably this will never happen, but for any case
341 0           $c->log->error( 'No template specified for rendering' );
342 0           return 0;
343             }
344              
345 0           my $output = $self->render($c, $template);
346              
347 0           $c->response->body($output);
348              
349 0           return 1;
350             }
351              
352             # INTERNAL METHODS
353              
354             # returns the current set internal processor
355             sub _getProcessor {
356 0     0     my ($self) = @_;
357              
358 0           return $self->{CONFIG}->{PROCESSOR};
359             }
360              
361             # searchs for a file ($filename) in INCLUDE_PATH
362             # returns the first occurence
363             sub _searchInIncPath {
364 0     0     my ($self, $c, $filename) = @_;
365              
366 0 0         $c->log->debug( "searching in include path for [$filename]") if $c->debug;
367              
368 0           my $arefIncludePath = $self->{CONFIG}->{'INCLUDE_PATH'};
369              
370 0 0         if (ref $c->stash->{additional_template_paths} eq 'ARRAY') {
371 0           unshift( @{ $arefIncludePath },
  0            
372 0           @{ $c->stash->{additional_template_paths} } );
373             }
374              
375 0           foreach my $incEntry ( @{ $arefIncludePath} ) {
  0            
376              
377 0 0         $c->log->debug( "Going to search for file [$filename] in [$incEntry]" ) if $c->debug;
378 0           my $tmpTemplateName = '';
379 0           my $incPath = '';
380              
381 0 0         if (ref $incEntry eq 'Path::Class::File') {
382 0           $incPath = $incEntry->absolute();
383             } else {
384 0           $incPath = $incEntry;
385             }
386              
387 0 0         if (-e File::Spec->catfile($incPath, $filename)) {
388              
389 0 0         $c->log->debug( "File [$filename] found in [$incEntry]") if $c->debug;
390 0           return ($incPath, undef);
391             }
392              
393             }
394              
395 0           return (undef, 1);
396             }
397              
398             =back
399              
400             =head1 NOTE
401              
402             This version works only with L<XML::LibXSLT>.
403              
404             =head1 SEE ALSO
405              
406             L<Catalyst>, L<Catalyst::Base>, L<XML::LibXSLT>
407              
408             =head1 AUTHORS
409              
410             Martin Grigorov, E<lt>mcgregory {at} e-card {dot} bgE<gt>
411              
412             Simon Bertrang, E<lt>simon.bertrang@puzzworks.comE<gt>
413              
414             =head1 COPYRIGHT
415              
416             This program is free software, you can redistribute it and/or modify it
417             under the same terms as Perl itself.
418              
419             =cut
420              
421             1;