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; |