NAME
Tree::Transform::XSLTish - transform tree data, like XSLT but in Perl
SYNOPSIS
package MyTransform;
use Tree::Transform::XSLTish;
default_rules;
tree_rule match => 'node[@id=5]', action => sub {
return $_[0]->it->data();
};
package main;
use My::Tree;
my $tree= My::Tree->new();
# build something inside the tree
my ($node5_data)=MyTransform->new->transform($tree);
Transforming an HTML document:
package HtmlTransform;
use Tree::Transform::XSLTish;
use strict;
use warnings;
engine_class 'XML::XPathEngine';
default_rules;
tree_rule match => 'img[@alt="pick"]', action => sub {
return $_[0]->it->findvalue('@src');
};
package main;
use HTML::TreeBuilder::XPath;
my $tree=HTML::TreeBuilder::XPath->new();
$tree->parse_file('mypage.html');
my $trans=HtmlTransform->new();
my ($image_srce)=$trans->transform($tree);
DESCRIPTION
This module allows you to transform tree with Perl subroutines, just like XSLT does for XML documents.
It tries to model as closely as reasonable the semantic of XSLT.
REQUIREMENTS
By default, this module uses Tree::XPathEngine as its XPath engine, but you can use any other similar module, provided it implements the method findnodes
with the same signature and meaning. XML::XPathEngine is a good candidate, or you could use XML::LibXML::XPathContext.
The tree that you intend to manipulate must be implemented by classes that are compatible with the XPath engine; for example, Tree::DAG_Node::XPath if you use Tree::XPathEngine, or HTML::TreeBuilder::XPath if you use XML::XPathEngine.
EXPORTS
tree_rule
tree_rule match => '//node_name',
priority => 1,
action => sub { ... };
This is the basic fuction to declare a transformation rule; it's equivalent to the template
element is XSLT. It takes its parameters as a hash:
match
-
this is equivalent to the
match
attribute oftemplate
: it specifies the pattern for the nodes to which this rule applies.From the XSLT spec:
A pattern is defined to match a node if and only if there is a possible context such that when the pattern is evaluated as an expression with that context, the node is a member of the resulting node-set. When a node is being matched, the possible contexts have a context node that is the node being matched or any ancestor of that node, and a context node list containing just the context node.
name
-
this is equivalent of the
name
attribute oftemplate
: it allows calling rules by name (see call_rule) priority
-
this is equivalent of the
priority
attribute oftemplate
; currently the "default priority" as specified in the spec is not implemented action
-
this code-ref will be called (in list context) when the rule is to be applied; it can return whatever you want: call_rule will return the result unchanged, apply_rules will return the list of all results of all the applied rules
The action
code-ref will be called (by apply_rules or call_rule) with a Tree::Transform::XSLTish::Transformer object as its only parameter.
default_rules
This function will declare two rules that mimic the implicit rules of XSLT. It's equivalent to:
tree_rule match => '/', priority => 0, action => sub {$_[0]->apply_rules};
tree_rule match => '*', priority => 0, action => sub {$_[0]->apply_rules};
engine_class
engine_class 'XML::LibXML::XPathContext';
This function declares that the Tree::Transform::XSLTish::Transformer object returned by "new" should use this class to build its XPath engine.
This function is not exported by default: you have to use the module as:
use Tree::Transform::XSLTish ':engine';
engine_factory
engine_factory { My::XPath::Engine->new(params=>$whatever) };
This function declares that the Tree::Transform::XSLTish::Transformer object returned by "new" should call the passed code-ref to get its engine.
engine_class $classname
is equivalent to engine_factory { $classname->new }
.
This function is not exported by default: you have to use the module as:
use Tree::Transform::XSLTish ':engine';
new
Returns a Tree::Transform::XSLTish::Transformer for the rules declared in this package.
INHERITANCE
Stylesheet import is implented with the usual Perl inheritance scheme. It should even work with Class::C3, since we use Class::MOP's class_precedence_list
to get the list of inherited packages.
Engine factories are inherited, too, so you can extend a rules package without re-specifying the engine (you can, of course, override this and specify another one).
IMPORTING
This module uses Sub::Exporter, see that module's documentation for things like renaming the imports.
KNOWN BUGS & ISSUES
It's slow. Right now each rule application is linear in the number of defined rules times the depth of the node being transformed. There are several ways to optimize this for most common cases (patches welcome), but I prefer to "make it correct, before making it fast"
Some sugaring with Devel::Declare could make everything look better
AUTHOR
Gianni Ceccarelli <dakkar@thenautilus.net>