Subject: | Newval arg may be wrong in a trigger fired during _call_all_triggers. |
When a trigger is fired for an attribute during _call_all_triggers, if
$attr->should_coerce is false, the trigger callback will receive as its
newval arg whatever value was supplied to the constructor, even if the
attribute value has since changed or been cleared.
This looks to me to have been a bug since at least as far back as 2.02,
when _call_all_triggers was introduced.
=====
use Test::More;
# _SortedAttributes IS NEEDED FOR THIS TEST SO THAT TRIGGERS
# FIRE IN A PREDICTABLE ORDER DURING _call_all_triggers.
# ("X" SORTS BEFORE "Y".)
BEGIN { package _SortedAttributes;
use Moose;
extends 'Moose::Meta::Class';
around get_all_attributes => sub {
my ($orig_method, $self_aka_class) = (shift, shift);
my @attributes = $self_aka_class->$orig_method(@_);
return sort({ $a->name cmp $b->name } @attributes);
};
}
{ package MyClass;
use Moose -metaclass => '_SortedAttributes';
our @NEWVALS;
has X => (
is => 'rw',
isa => 'Any',
trigger => sub { shift->clear_Y },
);
has Y => (
is => 'rw',
isa => 'Any',
clearer => 'clear_Y',
trigger => sub {
my ($self, $new, $old) = (shift, @_);
push(@NEWVALS,
[defined($new) ? $new : 'UNDEF'],
[exists($self->{Y}) ? defined($self->{Y}) ? $self->
{Y} : 'UNDEF' : 'NOVAL'],
);
},
);
}
TODO: {
local $TODO = 'bug demo';
# X WILL FIRE, CLEARING Y; THEN Y WILL FIRE.
my $obj = MyClass->new({X => 'X', Y => 'Y'});
is_deeply($MyClass::NEWVALS[0], $MyClass::NEWVALS[1], 'newval arg
equals actual current value');
}