]> git.vpit.fr Git - perl/modules/LaTeX-TikZ.git/commitdiff
Insert rules after all their subrules and before all their superrules
authorVincent Pit <vince@profvince.com>
Wed, 6 Oct 2010 00:24:00 +0000 (02:24 +0200)
committerVincent Pit <vince@profvince.com>
Wed, 6 Oct 2010 00:24:00 +0000 (02:24 +0200)
lib/LaTeX/TikZ/Functor.pm
lib/LaTeX/TikZ/Functor/Rule.pm
t/30-functor.t

index f71ce6cd5a03fb90c6b44c43e5b1ba1407dbda5d..96ecbb4ff69bfb5681bed550fc6a783e8da5bd7c 100644 (file)
@@ -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<all> 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.
index bccbb48d739707e5a5f416d4356c98533596416d..d4e070011a3b5e7413d81c6b3e67bd3d0ff792dd 100644 (file)
@@ -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;
index 30289c227be85a1a875ea24f0334bb3c54584fe3..bdade4b6c6257b1686636a263258de9d3e00582f 100644 (file)
@@ -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
-