my $orig = shift;
my $self = shift;
- my $ia = $self->init_arg;
-
- # $_[2] is the hashref of options passed to the constructor. If our
- # parameter passed in was undef, pop it off the args...
- pop unless (defined $ia && defined($_[2]->{$ia}));
-
- # Invoke the real init, as the above line cleared the unef
+ my $key_name = $self->init_arg;
+
+ # $_[2] is the hashref of options passed to the constructor.
+ # If our parameter passed in was undef, pop it off the args...
+ # but leave the value unscathed if the attribute's type constraint can
+ # handle undef (or doesn't have one, which implicitly means it can)
+ if (not defined $key_name or not defined($_[2]->{$key_name}))
+ {
+ my $type_constraint = $self->type_constraint;
+ if ($type_constraint and not $type_constraint->check(undef))
+ {
+ pop;
+ }
+ }
+
+ # Invoke the real init, as the above line cleared the undef
$self->$orig(@_)
});
# insert a line of code at the start of the initializer,
# clearing the param if it's undefined.
- if (defined $key_name) {
- my $tolerant_code =
- qq# delete \$params->{'$key_name'} unless # .
+ if (defined $key_name)
+ {
+ # leave the value unscathed if the attribute's type constraint can
+ # handle undef (or doesn't have one, which implicitly means it can)
+ my $type_constraint = $self->_attributes->[$_[0]]->type_constraint;
+ if ($type_constraint and not $type_constraint->check(undef))
+ {
+ my $tolerant_code =
+ qq# delete \$params->{'$key_name'} unless # .
qq# exists \$params->{'$key_name'} && defined \$params->{'$key_name'};\n#;
return $tolerant_code . $self->$orig(@_);
+ }
}
- else {
- return $self->$orig(@_);
- }
- });
-}
+
+ return $self->$orig(@_);
+});
no Moose::Role;
-use Test::More;
+#use Test::More;
+use Test::Most 'die';
use Test::Fatal;
{
isa => 'Num',
predicate => 'has_attr1',
);
-
has 'attr2' => (
is => 'ro',
isa => 'Num',
predicate => 'has_attr2',
);
+ has 'attr3' => (
+ is => 'ro',
+ isa => 'Maybe[Num]',
+ predicate => 'has_attr3',
+ );
}
{
isa => 'Num',
predicate => 'has_attr2',
);
+ has 'attr3' => (
+ is => 'ro',
+ isa => 'Maybe[Num]',
+ predicate => 'has_attr3',
+ );
}
package main;
my $obj = Foo->new;
ok(!$obj->has_attr1, 'attr1 has no value before it is assigned');
ok(!$obj->has_attr2, 'attr2 has no value before it is assigned');
+ ok(!$obj->has_attr3, 'attr3 has no value before it is assigned');
}
{
my $obj = Foo->new(attr1 => undef);
ok(!$obj->has_attr1, 'UT attr1 has no value when assigned undef in constructor');
- is (exception { $obj = Foo->new(attr2 => undef) }, undef,
+ isnt (exception { $obj = Foo->new(attr2 => undef) }, undef,
'But assigning undef to attr2 generates a type constraint error');
+
+ is (exception { $obj = Foo->new(attr3 => undef) }, undef,
+ 'assigning undef to attr3 is acceptable');
+ ok($obj->has_attr3, 'attr3 retains its undef value when assigned undef in constructor');
}
{
- my $obj = Foo->new(attr1 => 1234, attr2 => 5678);
+ my $obj = Foo->new(attr1 => 1234, attr2 => 5678, attr3 => 9012);
is($obj->attr1, 1234, 'assigning a defined value during construction works as normal');
ok($obj->has_attr1, '...and the predicate returns true as normal');
+
is($obj->attr2, 5678, 'assigning a defined value during construction works as normal');
ok($obj->has_attr2, '...and the predicate returns true as normal');
+
+ is($obj->attr3, 9012, 'assigning a defined value during construction works as normal');
+ ok($obj->has_attr3, '...and the predicate returns true as normal');
}
{
my $obj = Bar->new;
ok(!$obj->has_attr1, 'attr1 has no value before it is assigned');
+ ok(!$obj->has_attr2, 'attr2 has no value before it is assigned');
+ ok(!$obj->has_attr3, 'attr3 has no value before it is assigned');
}
{
my $obj = Bar->new(attr1 => undef);
ok(!$obj->has_attr1, 'attr1 has no value when assigned undef in constructor');
+ # note this test differs from the Foo case above
is (exception { $obj = Bar->new(attr2 => undef) }, undef,
'assigning undef to attr2 does not produce an error');
ok(!$obj->has_attr2, 'attr2 has no value when assigned undef in constructor');
+
+ is( exception { $obj = Foo->new(attr3 => undef) }, undef,
+ 'assigning undef to attr3 is acceptable');
+ ok($obj->has_attr3, 'attr3 retains its undef value when assigned undef in constructor');
}
{
- my $obj = Bar->new(attr1 => 1234);
+ my $obj = Bar->new(attr1 => 1234, attr2 => 5678, attr3 => 9012);
is($obj->attr1, 1234, 'assigning a defined value during construction works as normal');
ok($obj->has_attr1, '...and the predicate returns true as normal');
+
+ is($obj->attr2, 5678, 'assigning a defined value during construction works as normal');
+ ok($obj->has_attr2, '...and the predicate returns true as normal');
+
+ is($obj->attr3, 9012, 'assigning a defined value during construction works as normal');
+ ok($obj->has_attr3, '...and the predicate returns true as normal');
}
}
-use Test::More;
+#use Test::More;
+use Test::Most 'die';
use Test::Fatal;
use MooseX::UndefTolerant::Attribute ();
predicate => 'has_attr2',
default => 2,
);
+ has 'attr3' => (
+ is => 'ro',
+ isa => 'Maybe[Num]',
+ predicate => 'has_attr3',
+ default => 3,
+ );
}
{
predicate => 'has_attr2',
default => 2,
);
+ has 'attr3' => (
+ is => 'ro',
+ isa => 'Maybe[Num]',
+ predicate => 'has_attr3',
+ default => 3,
+ );
}
my $obj = $class->new;
ok($obj->has_attr1, 'attr1 has a value');
ok($obj->has_attr2, 'attr2 has a value');
+ ok($obj->has_attr3, 'attr3 has a value');
+
is($obj->attr1, 1, 'attr1\'s value is its default');
is($obj->attr2, 2, 'attr2\'s value is its default');
+ is($obj->attr3, 3, 'attr3\'s value is its default');
}
{
- my $obj = $class->new(attr1 => undef);
+ my $obj = $class->new(attr1 => undef, attr3 => undef);
ok($obj->has_attr1, 'UT attr1 has a value when assigned undef in constructor');
+ ok($obj->has_attr3, 'attr3 retains its undef value when assigned undef in constructor');
+
is($obj->attr1, 1, 'attr1\'s value is its default');
is($obj->attr2, 2, 'attr2\'s value is its default');
+ is($obj->attr3, undef, 'attr3\'s value is not its default (explicitly set)');
}
{
- my $obj = $class->new(attr1 => 1234, attr2 => 5678);
+ my $obj = $class->new(attr1 => 1234, attr2 => 5678, attr3 => 9012);
is($obj->attr1, 1234, 'assigning a defined value during construction works as normal');
ok($obj->has_attr1, '...and the predicate returns true as normal');
+
is($obj->attr2, 5678, 'assigning a defined value during construction works as normal');
ok($obj->has_attr2, '...and the predicate returns true as normal');
+
+ is($obj->attr3, 9012, 'assigning a defined value during construction works as normal');
+ ok($obj->has_attr3, '...and the predicate returns true as normal');
}
}