diff options
Diffstat (limited to 'lib/Tree/Transform/XSLTish.pm')
-rw-r--r-- | lib/Tree/Transform/XSLTish.pm | 159 |
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> |