use Scalar::Util 'blessed', 'weaken', 'reftype';
use Carp 'confess';
+use overload ();
-our $VERSION = '0.10';
+our $VERSION = '0.11';
our $AUTHORITY = 'cpan:STEVAN';
use Moose::Meta::Method::Accessor;
sub clone_and_inherit_options {
my ($self, %options) = @_;
- # you can change default, required, coerce and documentation
+ # you can change default, required, coerce, documentation and lazy
my %actual_options;
- foreach my $legal_option (qw(default coerce required documentation)) {
+ foreach my $legal_option (qw(default coerce required documentation lazy)) {
if (exists $options{$legal_option}) {
$actual_options{$legal_option} = $options{$legal_option};
delete $options{$legal_option};
}
}
+
+ # handles can only be added, not changed
+ if ($options{handles}) {
+ confess "You can only add the 'handles' option, you cannot change it"
+ if $self->has_handles;
+ $actual_options{handles} = $options{handles};
+ delete $options{handles};
+ }
+
# isa can be changed, but only if the
# new type is a subtype
if ($options{isa}) {
my $val;
if (exists $params->{$init_arg}) {
$val = $params->{$init_arg};
+
+ if (!defined $val && $self->is_required) {
+ confess "Attribute (" . $self->name . ") is required and cannot be undef";
+ }
}
else {
# skip it if it's lazy
# attribute's default value (if it has one)
if (!defined $val && $self->has_default) {
$val = $self->default($instance);
- }
+ }
- if (defined $val) {
+ if (defined $val || $self->has_default) {
if ($self->has_type_constraint) {
my $type_constraint = $self->type_constraint;
if ($self->should_coerce && $type_constraint->has_coercion) {
$self->name .
") does not pass the type constraint (" .
$type_constraint->name .
- ") with '$val'";
+ ") with '" .
+ (defined $val
+ ? (overload::Overloaded($val)
+ ? overload::StrVal($val)
+ : $val)
+ : 'undef') .
+ "'";
}
}
}
defined($type_constraint->_compiled_type_constraint->($value))
|| confess "Attribute ($attr_name) does not pass the type constraint ("
- . $type_constraint->name . ") with " . (defined($value) ? ("'" . $value . "'") : "undef")
+ . $type_constraint->name
+ . ") with "
+ . (defined($value)
+ ? ("'" . (overload::Overloaded($value) ? overload::StrVal($value) : $value) . "'")
+ : "undef")
if defined($value);
}
# we should check for lack of
# a callable return value from
# the accessor here
- ((shift)->$accessor_name())->$method_to_call(@_);
+ my $proxy = (shift)->$accessor_name();
+ @_ = ($proxy, @_);
+ goto &{ $proxy->can($method_to_call)};
});
}
}
sub _canonicalize_handles {
my $self = shift;
my $handles = $self->handles;
- if (ref($handles) eq 'HASH') {
- return %{$handles};
- }
- elsif (ref($handles) eq 'ARRAY') {
- return map { $_ => $_ } @{$handles};
- }
- elsif (ref($handles) eq 'Regexp') {
- ($self->has_type_constraint)
- || confess "Cannot delegate methods based on a RegExpr without a type constraint (isa)";
- return map { ($_ => $_) }
- grep { /$handles/ } $self->_get_delegate_method_list;
- }
- elsif (ref($handles) eq 'CODE') {
- return $handles->($self, $self->_find_delegate_metaclass);
+ if (my $handle_type = ref($handles)) {
+ if ($handle_type eq 'HASH') {
+ return %{$handles};
+ }
+ elsif ($handle_type eq 'ARRAY') {
+ return map { $_ => $_ } @{$handles};
+ }
+ elsif ($handle_type eq 'Regexp') {
+ ($self->has_type_constraint)
+ || confess "Cannot delegate methods based on a RegExpr without a type constraint (isa)";
+ return map { ($_ => $_) }
+ grep { /$handles/ } $self->_get_delegate_method_list;
+ }
+ elsif ($handle_type eq 'CODE') {
+ return $handles->($self, $self->_find_delegate_metaclass);
+ }
+ else {
+ confess "Unable to canonicalize the 'handles' option with $handles";
+ }
}
else {
- confess "Unable to canonicalize the 'handles' option with $handles";
+ my $role_meta = eval { $handles->meta };
+ if ($@) {
+ confess "Unable to canonicalize the 'handles' option with $handles because : $@";
+ }
+
+ (blessed $role_meta && $role_meta->isa('Moose::Meta::Role'))
+ || confess "Unable to canonicalize the 'handles' option with $handles because ->meta is not a Moose::Meta::Role";
+
+ return map { $_ => $_ } (
+ $role_meta->get_method_list,
+ $role_meta->get_required_method_list
+ );
}
}
=item B<set_value>
+ eval { $point->meta->get_attribute('x')->set_value($point, 'fourty-two') };
+ if($@) {
+ print "Oops: $@\n";
+ }
+
+I<Attribute (x) does not pass the type constraint (Int) with 'fourty-two'>
+
+Before setting the value, a check is made on the type constraint of
+the attribute, if it has one, to see if the value passes it. If the
+value fails to pass, the set operation dies with a L<Carp/confess>.
+
+Any coercion to convert values is done before checking the type constraint.
+
+To check a value against a type constraint before setting it, fetch the
+attribute instance using L<Moose::Meta::Attribute/find_attribute_by_name>,
+fetch the type_constraint from the attribute using L<Moose::Meta::Attribute/type_constraint>
+and call L<Moose::Meta::TypeConstraint/check>. See L<Moose::Cookbook::RecipeX>
+for an example.
+
=back
=head2 Additional Moose features