File Coverage

blib/lib/Plack/Middleware/XSLT.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package Plack::Middleware::XSLT;
2             {
3             $Plack::Middleware::XSLT::VERSION = '0.20002';
4             }
5 1     1   44497 use strict;
  1         2  
  1         40  
6              
7             # ABSTRACT: XSLT transformations with Plack
8              
9 1     1   917 use parent 'Plack::Middleware';
  1         328  
  1         7  
10              
11 1     1   15586 use File::Spec;
  1         7  
  1         17  
12 1     1   805 use HTTP::Exception ();
  1         7810  
  1         21  
13 1     1   1206 use Plack::Response;
  1         23285  
  1         38  
14 1     1   9 use Plack::Util::Accessor qw(cache path parser_options);
  1         2  
  1         9  
15 1     1   2161 use Try::Tiny;
  1         1806  
  1         59  
16 1     1   439 use XML::LibXML 1.62;
  0            
  0            
17             use XML::LibXSLT 1.62;
18              
19             my ($parser, $xslt);
20              
21             sub call {
22             my ($self, $env) = @_;
23              
24             my $r = $self->app->($env);
25             my $style = $env->{'xslt.style'};
26              
27             return $r if !defined($style) || $style eq '';
28              
29             my $path = $self->path;
30             $style = File::Spec->catfile($path, $style)
31             if defined($path) && !File::Spec->file_name_is_absolute($style);
32              
33             my ($status, $headers, $body) = @$r;
34             my $doc = $self->_parse_body($body);
35              
36             my ($output, $media_type, $encoding) = $self->_xform($style, $doc);
37              
38             my $res = Plack::Response->new($status, $headers, $output);
39             $res->content_type("$media_type; charset=$encoding");
40             $res->content_length(length($output));
41              
42             return $res->finalize();
43             }
44              
45             sub _xform {
46             my ($self, $style, $doc) = @_;
47              
48             if (!$xslt) {
49             if ($self->cache) {
50             require XML::LibXSLT::Cache;
51             $xslt = XML::LibXSLT::Cache->new;
52             }
53             else {
54             $xslt = XML::LibXSLT->new;
55             }
56             }
57              
58             my $stylesheet = $xslt->parse_stylesheet_file($style);
59              
60             my $result = try {
61             $stylesheet->transform($doc) or die("XSLT transform failed: $!");
62             }
63             catch {
64             for my $line (split(/\n/, $_)) {
65             HTTP::Exception->throw($1) if $line =~ /^(\d\d\d)(?:\s|\z)/;
66             }
67             die($_);
68             };
69              
70             my $output = $stylesheet->output_as_bytes($result);
71             my $media_type = $stylesheet->media_type();
72             my $encoding = $stylesheet->output_encoding();
73              
74             return ($output, $media_type, $encoding);
75             }
76              
77             sub _parse_body {
78             my ($self, $body) = @_;
79              
80             if (!$parser) {
81             my $options = $self->parser_options;
82             $parser = $options
83             ? XML::LibXML->new($options)
84             : XML::LibXML->new;
85             }
86              
87             my $doc;
88              
89             if (ref($body) eq 'ARRAY') {
90             my $xml = join('', @$body);
91              
92             $doc = $parser->parse_string($xml);
93             }
94             else {
95             $doc = $parser->parse_fh($body);
96             }
97              
98             return $doc;
99             }
100              
101             sub _cache_hits {
102             my $self = shift;
103              
104             return $xslt->cache_hits
105             if $xslt && $xslt->isa('XML::LibXSLT::Cache');
106              
107             return 0;
108             }
109              
110             1;
111              
112              
113              
114             =pod
115              
116             =head1 NAME
117              
118             Plack::Middleware::XSLT - XSLT transformations with Plack
119              
120             =head1 VERSION
121              
122             version 0.20002
123              
124             =head1 SYNOPSIS
125              
126             # in your .psgi
127              
128             enable 'XSLT';
129              
130             # in your app
131              
132             $env->{'xslt.style'} = 'stylesheet.xsl';
133              
134             return [ 200, $headers, [ $xml ] ];
135              
136             =head1 DESCRIPTION
137              
138             Plack::Middleware::XSLT converts XML response bodies to HTML, XML, or text
139             using XML::LibXSLT. The XSLT stylesheet is specified by the environment
140             variable 'xslt.style'. If this variable is undefined or empty, the response
141             is not altered. This rather crude mechanism might be enhanced in the future.
142              
143             The Content-Type header is set according to xsl:output. Content-Length is
144             adjusted.
145              
146             =head1 CONFIGURATION
147              
148             =over 4
149              
150             =item cache
151              
152             enable 'XSLT', cache => 1;
153              
154             Enables caching of XSLT stylesheets. Defaults to false.
155              
156             =item path
157              
158             enable 'XSLT', path => 'path/to/xsl/files';
159              
160             Sets a path that will be prepended if xslt.style contains a relative path.
161             Defaults to the current directory.
162              
163             =item parser_options
164              
165             enable 'XSLT', parser_options => \%options;
166              
167             Options that will be passed to the XML parser when parsing the input
168             document. See L.
169              
170             =back
171              
172             =head1 HTTP EXCEPTIONS
173              
174             If the transform exits via C<> and the
175             message contains a line starting with a three-digit HTTP response status
176             code, a corresponding L is thrown.
177              
178             =head1 AUTHOR
179              
180             Nick Wellnhofer
181              
182             =head1 COPYRIGHT AND LICENSE
183              
184             This software is copyright (c) 2013 by Nick Wellnhofer.
185              
186             This is free software; you can redistribute it and/or modify it under
187             the same terms as the Perl 5 programming language system itself.
188              
189             =cut
190              
191              
192             __END__