- my ($rule, $list) = @_;
-
- my $spec = $rule->[0];
- for my $i (0 .. $#$list) {
- my $old_spec = $list->[$i]->[0];
- if ($old_spec->isa($spec) or does_role($old_spec, $spec)) {
- splice @$list, $i, 1, $rule;
- return 1;
+ my ($rule, $list, $overwrite) = @_;
+
+ my ($target, $replace, $is_role) = @{$rule}[0, 2, 3];
+
+ if ($replace) {
+ my @remove;
+
+ for my $i (0 .. $#$list) {
+ my $old_target = $list->[$i]->[0];
+ if ($is_role ? does_role($old_target, $target)
+ : $old_target->isa($target)) {
+ if (defined $rule) {
+ splice @$list, $i, 1, $rule;
+ $rule = undef;
+ } else {
+ push @remove, $i;
+ }
+ }
+ }
+
+ my $shift;
+ for (@remove) {
+ splice @$list, $_ - $shift, 1;
+ ++$shift;
+ }
+ return 1 unless defined $rule;
+
+ } else { # Replace only an existent rule
+
+ for my $i (0 .. $#$list) {
+ my $old_target = $list->[$i]->[0];
+ if ($old_target eq $target) {
+ Carp::confess("Default rule already defined for target $target")
+ unless $overwrite;
+ splice @$list, $i, 1, $rule;
+ return 1;
+ }