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 of template: 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 of template: it allows calling rules by name (see call_rule)

priority

this is equivalent of the priority attribute of template; 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>