File Coverage

blib/lib/Role/LibXSLT/Extender.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 Role::LibXSLT::Extender;
2             {
3             $Role::LibXSLT::Extender::VERSION = '1.140260';
4             }
5 1     1   2762 use Moose::Role;
  0            
  0            
6             use XML::LibXSLT;
7             use namespace::autoclean;
8              
9              
10              
11             =head1 NAME
12              
13             Role::LibXSLT::Extender
14              
15             =head1 VERSION
16              
17             version 1.140260
18              
19             =head1 SYNOPSIS
20              
21             # your extension class
22             package My::Special::XSLTProcessor
23             use Moose;
24             with 'MooseX::LibXSLT::Extender';
25              
26             sub set_extension_namespace {
27             return 'http:/fake.tld/my/app/namespace/v1'
28             }
29              
30             sub special_text_munger {
31             my $self = shift;
32             my $text = shift;
33              
34             # magic happens here
35              
36             return $text;
37             }
38              
39             -------------------
40             # in your XSLT stylesheet
41              
42             <?xml version="1.0"?>
43             <xsl:stylesheet version="1.0"
44             xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
45             xmlns:myapp="http:/fake.tld/my/app/namespace/v1">
46              
47             <xsl:template match="/some/xpath">
48             <!-- pass the current text node to special_text_munger()
49             in your extension class. -->
50             <foo><xsl:value-of select="myapp:special_text_munger(.)"/></foo>
51             </xsl:template>
52              
53             -------------------
54             # in your application or script
55              
56             my $extended = My::Special::XSLTProcessor->new();
57              
58             # load the XML and XSLT files
59             my $style_dom = XML::LibXML->load_xml( location=> $xsl_file );
60             my $input_dom = XML::LibXML->load_xml( location=> $xml_file );
61             my $stylesheet = $extended->parse_stylesheet($style_dom);
62              
63             # your custom extensions are called here
64             my $transformed_dom = $stylesheet->transform( $input_dom );
65              
66             # dump the result to STDOUT
67             print $stylesheet->output_as_bytes($transformed_dom);
68              
69              
70             =head1 DESCRIPTION
71              
72             Simple Moose Role that instantiates an XML::LibXSLT processor and registers a series of site-specific Perl extension functions that can be called from within your XSLT stylesheets.
73              
74             =head1 WHY WOULD I WANT THIS?
75              
76             XSLT is great for recursively transforming nested XML documents but operating on the text in those documents can be time consuming and error-prone. Perl is great for all sort of things, but transforming nested XML documents programmatically is the source of much unnecessary pain and consternation. This module seeks to bridge the gap by letting you use XSLT for what it is best at while using the power of Perl for everything else.
77              
78             =head1 METHODS
79              
80             =over
81              
82             =item set_extension_namespace
83              
84             In addition to the various custom functions in your extention class, you are required to implement the set_extension_namespace() method. This namespace URI will be used to register your functions with the LibXSLT processor and is the mechanism by which your custom functions will be available from within your XSLT stylesheets.
85              
86             For example, if your extention class has the following:
87              
88             sub set_extension_namespace { return 'http:/fake.tld/my/app/namespace/v1'; }
89              
90             You can access functions in this namespace by declaring that namespace in your XSLT stylesheet:
91              
92             <?xml version="1.0"?>
93             <xsl:stylesheet version="1.0"
94             xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
95             xmlns:myapp="http:/fake.tld/my/app/namespace/v1"
96             >
97              
98             And then using the bound prefix to call the functions in that namespace:
99              
100             <xsl:value-of select="myapp:some_function_name( arguments )" />
101              
102             =item xslt_processor
103              
104             This method returns the instance of the L<XML::LibXSLT> processor with your extension functions registered and ready to go.
105              
106             =back
107              
108             =head1 FUNCTIONS ARE METHODS, NOT SIMPLE SUBROUTINES
109              
110             Note that this Role gives your extension functions extra magical powers beyond the mechanism provided by L<XML::LibXSLT> by making your functions into methods rather than simple subroutines. From the example above:
111              
112             sub special_text_munger {
113             my $self = shift;
114             my $text = shift;
115              
116             # magic happens here
117              
118             return $text;
119             }
120              
121             Note that the first argument passed to this function is the instance of this class, and the text node (or nodelist) sent over from the XML document is the second argument. This gives your functions access to all other attributes, methods, etc. contained in this class without having to resort to global variables and so forth. This gives your functions (and the stylesheets that use them) the power to easily alter the document by adding nodes based on database queries, perform complex operations on data using other objects, and a myriad of other options.
122              
123             =head1 KEEPING PRIVATE THINGS PRIVATE
124              
125             This Role uses Moose's handy introspection facilities to avoid registering methods that you probably don't want to make available to your stylesheets (attribute accessors, builders, etc.) but it has no way of differentiating between methods that you want to register and those that you will use for other purposes. To that end, if you want to implement methods that B<will not> be registered, simply use the "make this private" convention and prepend the method's name with an underscore:
126              
127             sub my_function {
128             # i'll be registered and available in the stylesheets.
129             }
130              
131             sub _my_function {
132             # But I won't be.
133             }
134              
135             =cut
136              
137             has _extension_namespace => (
138             is => 'ro',
139             isa => 'Str|Undef',
140             lazy => 1,
141             builder => 'set_extension_namespace',
142             );
143              
144             sub set_extension_namespace { return undef; }
145              
146             has xslt_processor => (
147             is => 'ro',
148             isa => 'XML::LibXSLT',
149             lazy_build => 1,
150             handles => [qw(parse_stylesheet parse_stylesheet_file)],
151             );
152              
153             sub _build_xslt_processor {
154             my $self = shift;
155             my $class_meta = __PACKAGE__->meta;
156             my $meta = $self->meta;
157              
158             my @class_methods = ($class_meta->get_method_list, 'set_namespace');
159              
160             my $ns = $self->_extension_namespace || die "No extention namespace declared. Use set_namespace() to bind your functions.";
161              
162             foreach my $method_name ( $meta->get_method_list ) {
163             next if grep {$_ eq $method_name} @class_methods;
164             if ( my $method = $meta->get_method( $method_name ) ) {
165             # attribute accessors, etc. have
166             # specialized 'Moose::Meta::Method::* subclasses,
167             # plain old methods don't.
168              
169             next unless blessed( $method ) eq 'Moose::Meta::Method';
170              
171             # keep private methods private
172              
173             next if $method_name =~ /^_/;
174             #warn "registering $method_name to namespace $ns \n";
175             XML::LibXSLT->register_function($ns, $method_name, sub { $self->$method_name( @_ ) });
176             }
177             }
178              
179             return XML::LibXSLT->new();
180             }
181              
182             1;