From: Vincent Pit Date: Wed, 6 Oct 2010 00:24:00 +0000 (+0200) Subject: Insert rules after all their subrules and before all their superrules X-Git-Tag: rt87282~29 X-Git-Url: http://git.vpit.fr/?p=perl%2Fmodules%2FLaTeX-TikZ.git;a=commitdiff_plain;h=b212ec02c4010c5200e7741d432397bfede7ca50 Insert rules after all their subrules and before all their superrules --- diff --git a/lib/LaTeX/TikZ/Functor.pm b/lib/LaTeX/TikZ/Functor.pm index f71ce6c..96ecbb4 100644 --- a/lib/LaTeX/TikZ/Functor.pm +++ b/lib/LaTeX/TikZ/Functor.pm @@ -91,7 +91,7 @@ The default set and mod rules clone their relevant objects, so you get a clone f my $clone = Tikz->functor; my $dup = $set->$clone; -If there is already a default rule for one of the C<$spec>s, it is replaced by the new one ; otherwise, the user rule is appended to the list of default rules. +If there is already a default rule for one of the C<$spec>s, it is replaced by the new one ; otherwise, the user rule is inserted into the list of default rules after all its descendants' rules and before all its ancestors' rules. # A translator my $translate = Tikz->functor( @@ -208,7 +208,7 @@ LaTeX::TikZ::Interface->register( Adds to all subsequently created functors a default rule for the class or role C<$spec>. -An exception is thrown if there is already a default rule for C<$spec> ; otherwise, the new rule is appended to the current list of default rules. +An exception is thrown if there is already a default rule for C<$spec> ; otherwise, the new rule is inserted into the current list of default rules after all its descendants' rules and before all its ancestors' rules. But if C<$spec> begins with C<'+'>, the rule will replace I default rules that apply to subclasses or subroles of C<$spec> (including C<$spec> itself). Returns true if and only if an existent rule was replaced. diff --git a/lib/LaTeX/TikZ/Functor/Rule.pm b/lib/LaTeX/TikZ/Functor/Rule.pm index bccbb48..d4e0700 100644 --- a/lib/LaTeX/TikZ/Functor/Rule.pm +++ b/lib/LaTeX/TikZ/Functor/Rule.pm @@ -123,8 +123,9 @@ around 'BUILDARGS' => sub { =head2 C<< insert into => \@list, overwrite => $overwrite, replace => $replace >> Inserts the current rule into the list of rules C<@list>. +The list is expected to be ordered, in that each rule must come after all the rules that have a target that inherits or consumes the original rule's own target. -If C<$replace> is false, then the rule will be appended to the C<@list> ; except if there already is an existent entry for the same target, in which case it will be overwritten if C<$overwrite> is true, or an exception will be thrown if it is false. +If C<$replace> is false, then the rule will be inserted into C<@list> after all the rules applying to the target's subclasses/subroles and before all its superclasses/superroles ; except if there is already an existent entry for the same target, in which case it will be overwritten if C<$overwrite> is true, or an exception will be thrown if it is false. If C<$replace> is true, then the rule will replace the first rule in the list that is a subclass or that consumes the role denoted by the target. All the subsequent rules in the list that inherit or consume the target will be removed. @@ -167,15 +168,37 @@ sub insert { } else { # Replace only an existent rule my $target = $rule->target; + my $last_descendant = undef; + my $first_ancestor = undef; + for my $i (0 .. $#$list) { - my $old_target = $list->[$i]->target; + my $old_rule = $list->[$i]; + my $old_target = $old_rule->target; if ($old_target eq $target) { Carp::confess("Default rule already defined for target $target") unless $overwrite; splice @$list, $i, 1, $rule; return 1; + } elsif ($rule->handles($old_target)) { + $last_descendant = $i; + } elsif ($old_rule->handles($target)) { + $first_ancestor = $i; } } + + my $pos; + if (defined $first_ancestor) { + Carp::confess("Unsorted rule list") + if defined $last_descendant and $first_ancestor <= $last_descendant; + $pos = $first_ancestor; + } elsif (defined $last_descendant) { + $pos = $last_descendant + 1; + } + + if (defined $pos) { + splice @$list, $pos, 1, $rule; + return 0; + } } push @$list, $rule; diff --git a/t/30-functor.t b/t/30-functor.t index 30289c2..bdade4b 100644 --- a/t/30-functor.t +++ b/t/30-functor.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 5 + 2 * 5; +use Test::More tests => 10 + 2 * 7; use LaTeX::TikZ; @@ -122,6 +122,42 @@ check $seq3, 'the stripped sequence', <<'RES'; \draw (-2cm,1cm) -- (2cm,1cm) ; RES +my $special = eval { + Tikz->functor( + '+LaTeX::TikZ::Mod' => sub { die "mod\n" }, + '+LaTeX::TikZ::Set' => sub { die "set\n" }, + ); +}; +is $@, '', 'creating a special functor with + rules doesn\'t croak'; + +eval { $seq->$special }; +is $@, "set\n", 'special functor with + rules eats everything properly'; + +$special = eval { + Tikz->functor( + '+LaTeX::TikZ::Mod' => sub { die "mod\n" }, + '+LaTeX::TikZ::Set' => sub { die "set\n" }, + 'LaTeX::TikZ::Set::Point' => sub { Tikz->point(7) }, + 'LaTeX::TikZ::Set::Op' => sub { Tikz->raw('moo') }, + ); +}; +is $@, '', 'creating a special functor with + and normal rules doesn\'t croak'; + +my $res = eval { Tikz->point(3, 4)->$special }; +is $@, '', 'special functor with + and normal rules orders its rules properly'; + +check $res, 'the result of the special functor', <<'RES'; +\draw (7cm,0cm) ; +RES + +$res = eval { Tikz->raw('hlagh')->$special }; +is $@, '', + 'special functor with + and normal rules orders its rules properly again'; + +check $res, 'the result of the special functor', <<'RES'; +\draw moo ; +RES + $tikz = eval { Tikz->formatter( origin => [ -1, 1 ], @@ -141,4 +177,3 @@ check $seq, 'a sequence translated by an origin', <<'RES'; \end{scope} \end{scope} RES -