summaryrefslogtreecommitdiff
path: root/lib/Tree/Transform/XSLTish.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Tree/Transform/XSLTish.pm')
-rw-r--r--lib/Tree/Transform/XSLTish.pm159
1 files changed, 147 insertions, 12 deletions
diff --git a/lib/Tree/Transform/XSLTish.pm b/lib/Tree/Transform/XSLTish.pm
index e754cd0..e489992 100644
--- a/lib/Tree/Transform/XSLTish.pm
+++ b/lib/Tree/Transform/XSLTish.pm
@@ -6,12 +6,14 @@ use Params::Validate ':all';
use Tree::Transform::XSLTish::Utils;
use Tree::Transform::XSLTish::Transformer;
use Carp::Clan qw(^Tree::Transform::XSLTish);
+use 5.006;
-our $VERSION='0.1';
+our $VERSION='0.2';
my @DEFAULT_EXPORTS=('tree_rule',
'default_rules',
- 'new_transformer' => {-as => 'new'});
+ 'new_transformer' => {-as => 'new'},
+ );
Sub::Exporter::setup_exporter({
exports => [qw(tree_rule default_rules new_transformer engine_class engine_factory)],
@@ -40,7 +42,7 @@ sub tree_rule {
});
# TODO at least one of 'name' and 'match' must be specified
- # TODO default priority mased on match
+ # TODO default priority based on match
my $store=Tree::Transform::XSLTish::Utils::_rules_store(scalar caller);
@@ -61,8 +63,10 @@ sub tree_rule {
sub engine_class {
my ($classname)=@_;
- my $factory=Tree::Transform::XSLTish::Utils::_engine_factory(scalar caller);
- $$factory=sub{$classname->new()};
+ Tree::Transform::XSLTish::Utils::_set_engine_factory(
+ scalar caller,
+ sub{$classname->new()},
+ );
return;
}
@@ -70,18 +74,18 @@ sub engine_class {
sub engine_factory(&) {
my ($new_factory)=@_;
- my $factory=Tree::Transform::XSLTish::Utils::_engine_factory(scalar caller);
- $$factory=$new_factory;
+ Tree::Transform::XSLTish::Utils::_set_engine_factory(
+ scalar caller,
+ $new_factory,
+ );
return;
}
-sub _transformer_class { 'Tree::Transform::XSLTish::Transformer' };
-
sub new_transformer {
my $rules_package=shift;
- return _transformer_class->new(rules_package=>$rules_package,@_);
+ return Tree::Transform::XSLTish::Transformer->new(rules_package=>$rules_package,@_);
}
1;
@@ -117,6 +121,8 @@ Transforming an HTML document:
use strict;
use warnings;
+ engine_class 'XML::XPathEngine';
+
default_rules;
tree_rule match => 'img[@alt="pick"]', action => sub {
@@ -124,13 +130,12 @@ Transforming an HTML document:
};
package main;
- use XML::XPathEngine;
use HTML::TreeBuilder::XPath;
my $tree=HTML::TreeBuilder::XPath->new();
$tree->parse_file('mypage.html');
- my $trans=HtmlTransform->new(engine=>XML::XPathEngine->new());
+ my $trans=HtmlTransform->new();
my ($image_srce)=$trans->transform($tree);
=head1 DESCRIPTION
@@ -153,6 +158,136 @@ that are compatible with the XPath engine; for example,
L<Tree::DAG_Node::XPath> if you use L<Tree::XPathEngine>, or
L<HTML::TreeBuilder::XPath> if you use L<XML::XPathEngine>.
+=head1 EXPORTS
+
+=head2 C<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 C<template> element is XSLT. It takes its parameters
+as a hash:
+
+=over 4
+
+=item C<match>
+
+this is equivalent to the C<match> attribute of C<template>: it
+specifies the pattern for the nodes to which this rule applies.
+
+From the L<XSLT spec|http://www.w3.org/TR/xslt.html#NT-Pattern>:
+
+I<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.>
+
+=item C<name>
+
+this is equivalent of the C<name> attribute of C<template>: it allows
+calling rules by name (see
+L<call_rule|Tree::Transform::XSLTish::Transformer/call_rule>)
+
+=item C<priority>
+
+this is equivalent of the C<priority> attribute of C<template>;
+currently the "default priority" as specified in the
+L<spec|http://www.w3.org/TR/xslt.html#conflict> is not implemented
+
+=item C<action>
+
+this code-ref will be called (in list context) when the rule is to be
+applied; it can return whatever you want:
+L<call_rule|Tree::Transform::XSLTish::Transformer/call_rule> will
+return the result unchanged,
+L<apply_rules|Tree::Transform::XSLTish::Transformer/apply_rules> will
+return the list of all results of all the applied rules
+
+=back
+
+The C<action> code-ref will be called (by
+L<apply_rules|Tree::Transform::XSLTish::Transformer/apply_rules> or
+L<call_rule|Tree::Transform::XSLTish::Transformer/call_rule>) with a
+L<Tree::Transform::XSLTish::Transformer> object as its only parameter.
+
+=head2 C<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};
+
+=head2 C<engine_class>
+
+ engine_class 'XML::LibXML::XPathContext';
+
+This function declares that the
+L<Tree::Transform::XSLTish::Transformer> object returned by L</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';
+
+=head2 C<engine_factory>
+
+ engine_factory { My::XPath::Engine->new(params=>$whatever) };
+
+This function declares that the
+L<Tree::Transform::XSLTish::Transformer> object returned by L</new>
+should call the passed code-ref to get its engine.
+
+C<engine_class $classname> is equivalent to C<engine_factory {
+$classname->new }>.
+
+This function is not exported by default: you have to use the module as:
+
+ use Tree::Transform::XSLTish ':engine';
+
+=head2 C<new>
+
+Returns a L<Tree::Transform::XSLTish::Transformer> for the rules
+declared in this package.
+
+=head1 INHERITANCE
+
+L<Stylesheet import|http://www.w3.org/TR/xslt.html#import> is implented
+with the usual Perl inheritance scheme. It should even work with
+L<Class::C3>, since we use L<Class::MOP>'s C<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).
+
+=head1 IMPORTING
+
+This module uses L<Sub::Exporter>, see that module's documentation for
+things like renaming the imports.
+
+=head1 KNOWN BUGS & ISSUES
+
+=over 4
+
+=item *
+
+It's I<slow>. Right now each rule application is linear in the number
+of defined rules I<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"
+
+=item *
+
+Some sugaring with L<Devel::Declare> could make everything look better
+
+=back
+
=head1 AUTHOR
Gianni Ceccarelli <dakkar@thenautilus.net>