use Scalar::Util 'blessed', 'weaken';
use overload ();
-our $VERSION = '0.74';
+our $VERSION = '0.84';
our $AUTHORITY = 'cpan:STEVAN';
use Moose::Meta::Method::Accessor;
predicate => 'has_applied_traits',
));
-# we need to have a ->does method in here to
-# more easily support traits, and the introspection
+# we need to have a ->does method in here to
+# more easily support traits, and the introspection
# of those traits. We extend the does check to look
# for metatrait aliases.
sub does {
sub new {
my ($class, $name, %options) = @_;
$class->_process_options($name, \%options) unless $options{__hack_no_process_options}; # used from clone()... YECHKKK FIXME ICKY YUCK GROSS
+
+ delete $options{__hack_no_process_options};
+
+ my %attrs =
+ ( map { $_ => 1 }
+ grep { defined }
+ map { $_->init_arg() }
+ $class->meta()->get_all_attributes()
+ );
+
+ my @bad = sort grep { ! $attrs{$_} } keys %options;
+
+ if (@bad)
+ {
+ Carp::cluck "Found unknown argument(s) passed to '$name' attribute constructor in '$class': @bad";
+ }
+
return $class->SUPER::new($name, %options);
}
sub interpolate_class_and_new {
- my ($class, $name, @args) = @_;
+ my ($class, $name, %args) = @_;
- my ( $new_class, @traits ) = $class->interpolate_class(@args);
-
- $new_class->new($name, @args, ( scalar(@traits) ? ( traits => \@traits ) : () ) );
+ my ( $new_class, @traits ) = $class->interpolate_class(\%args);
+
+ $new_class->new($name, %args, ( scalar(@traits) ? ( traits => \@traits ) : () ) );
}
sub interpolate_class {
- my ($class, %options) = @_;
+ my ($class, $options) = @_;
$class = ref($class) || $class;
- if ( my $metaclass_name = delete $options{metaclass} ) {
+ if ( my $metaclass_name = delete $options->{metaclass} ) {
my $new_class = Moose::Util::resolve_metaclass_alias( Attribute => $metaclass_name );
-
+
if ( $class ne $new_class ) {
if ( $new_class->can("interpolate_class") ) {
- return $new_class->interpolate_class(%options);
+ return $new_class->interpolate_class($options);
} else {
$class = $new_class;
}
my @traits;
- if (my $traits = $options{traits}) {
+ if (my $traits = $options->{traits}) {
my $i = 0;
while ($i < @$traits) {
my $trait = $traits->[$i++];
# ...
my @legal_options_for_inheritance = qw(
- default coerce required
- documentation lazy handles
+ default coerce required
+ documentation lazy handles
builder type_constraint
definition_context
lazy_build
sub legal_options_for_inheritance { @legal_options_for_inheritance }
# NOTE/TODO
-# This method *must* be able to handle
-# Class::MOP::Attribute instances as
-# well. Yes, I know that is wrong, but
-# apparently we didn't realize it was
-# doing that and now we have some code
-# which is dependent on it. The real
-# solution of course is to push this
+# This method *must* be able to handle
+# Class::MOP::Attribute instances as
+# well. Yes, I know that is wrong, but
+# apparently we didn't realize it was
+# doing that and now we have some code
+# which is dependent on it. The real
+# solution of course is to push this
# feature back up into Class::MOP::Attribute
# but I not right now, I am too lazy.
-# However if you are reading this and
-# looking for something to do,.. please
+# However if you are reading this and
+# looking for something to do,.. please
# be my guest.
# - stevan
sub clone_and_inherit_options {
my ($self, %options) = @_;
-
+
my %copy = %options;
-
+
my %actual_options;
-
+
# NOTE:
# we may want to extends a Class::MOP::Attribute
- # in which case we need to be able to use the
- # core set of legal options that have always
+ # in which case we need to be able to use the
+ # core set of legal options that have always
# been here. But we allows Moose::Meta::Attribute
# instances to changes them.
# - SL
my @legal_options = $self->can('legal_options_for_inheritance')
? $self->legal_options_for_inheritance
: @legal_options_for_inheritance;
-
+
foreach my $legal_option (@legal_options) {
if (exists $options{$legal_option}) {
$actual_options{$legal_option} = $options{$legal_option};
delete $options{$legal_option};
}
- }
+ }
if ($options{isa}) {
my $type_constraint;
$actual_options{type_constraint} = $type_constraint;
delete $options{isa};
}
-
+
if ($options{does}) {
my $type_constraint;
if (blessed($options{does}) && $options{does}->isa('Moose::Meta::TypeConstraint')) {
$actual_options{type_constraint} = $type_constraint;
delete $options{does};
- }
+ }
# NOTE:
- # this doesn't apply to Class::MOP::Attributes,
+ # this doesn't apply to Class::MOP::Attributes,
# so we can ignore it for them.
# - SL
if ($self->can('interpolate_class')) {
- ( $actual_options{metaclass}, my @traits ) = $self->interpolate_class(%options);
+ ( $actual_options{metaclass}, my @traits ) = $self->interpolate_class(\%options);
my %seen;
my @all_traits = grep { $seen{$_}++ } @{ $self->applied_traits || [] }, @traits;
sub clone {
my ( $self, %params ) = @_;
- my $class = $params{metaclass} || ref $self;
+ my $class = delete $params{metaclass} || ref $self;
my ( @init, @non_init );
## is => rw, accessor => _foo # turns into (accessor => _foo)
## is => ro, accessor => _foo # error, accesor is rw
### -------------------------
-
+
if ($options->{is} eq 'ro') {
$class->throw_error("Cannot define an accessor name on a read-only attribute, accessors are read/write", data => $options)
if exists $options->{accessor};
$options->{accessor} ||= $name;
}
}
+ elsif ($options->{is} eq 'bare') {
+ # do nothing, but don't complain (later) about missing methods
+ }
else {
$class->throw_error("I do not understand this option (is => " . $options->{is} . ") on attribute ($name)", data => $options->{is});
}
$class->throw_error("You can not use lazy_build and default for the same attribute ($name)", data => $options)
if exists $options->{default};
$options->{lazy} = 1;
- $options->{required} = 1;
$options->{builder} ||= "_build_${name}";
if ($name =~ /^_/) {
$options->{clearer} ||= "_clear${name}";
$options->{predicate} ||= "_has${name}";
- }
+ }
else {
$options->{clearer} ||= "clear_${name}";
$options->{predicate} ||= "has_${name}";
my $value_is_set;
if ( defined($init_arg) and exists $params->{$init_arg}) {
$val = $params->{$init_arg};
- $value_is_set = 1;
+ $value_is_set = 1;
}
else {
# skip it if it's lazy
if ($self->has_default) {
$val = $self->default($instance);
$value_is_set = 1;
- }
+ }
elsif ($self->has_builder) {
$val = $self->_call_builder($instance);
$value_is_set = 1;
## Slot management
# FIXME:
-# this duplicates too much code from
-# Class::MOP::Attribute, we need to
+# this duplicates too much code from
+# Class::MOP::Attribute, we need to
# refactor these bits eventually.
# - SL
sub _set_initial_slot_value {
$meta_instance->set_slot_value($instance, $slot_name, $val);
};
-
+
my $initializer = $self->initializer;
# most things will just want to set a value, so make it first arg
my $self = shift;
$self->SUPER::install_accessors(@_);
$self->install_delegation if $self->has_handles;
+ unless (
+ @{ $self->associated_methods }
+ || ($self->_is_metadata || '') eq 'bare'
+ ) {
+ Carp::cluck(
+ 'Attribute (' . $self->name . ') of class '
+ . $self->associated_class->name
+ . ' has no associated methods'
+ . ' (did you mean to provide an "is" argument?)'
+ . "\n"
+ )
+ }
return;
}
+sub _process_accessors {
+ my $self = shift;
+ my ($type, $accessor, $generate_as_inline_methods) = @_;
+ $accessor = (keys %$accessor)[0] if (ref($accessor)||'') eq 'HASH';
+ my $method = $self->associated_class->get_method($accessor);
+ if ($method && !$method->isa('Class::MOP::Method::Accessor')
+ && (!$self->definition_context
+ || $method->package_name eq $self->definition_context->{package})) {
+ Carp::cluck(
+ "You cannot overwrite a locally defined method ($accessor) with "
+ . "an accessor"
+ );
+ }
+ $self->SUPER::_process_accessors(@_);
+}
+
sub remove_accessors {
my $self = shift;
$self->SUPER::remove_accessors(@_);
my $method = $self->_make_delegation_method($handle, $method_to_call);
$self->associated_class->add_method($method->name, $method);
- }
+ $self->associate_method($method);
+ }
}
sub remove_delegation {
elsif ($handle_type eq 'CODE') {
return $handles->($self, $self->_find_delegate_metaclass);
}
+ elsif (blessed($handles) && $handles->isa('Moose::Meta::TypeConstraint::DuckType')) {
+ return map { $_ => $_ } @{ $handles->methods };
+ }
else {
$self->throw_error("Unable to canonicalize the 'handles' option with $handles", data => $handles);
}
}
else {
- my $role_meta = Class::MOP::load_class($handles);
+ Class::MOP::load_class($handles);
+ my $role_meta = Class::MOP::class_of($handles);
(blessed $role_meta && $role_meta->isa('Moose::Meta::Role'))
|| $self->throw_error("Unable to canonicalize the 'handles' option with $handles because its metaclass is not a Moose::Meta::Role", data => $handles);
-
+
return map { $_ => $_ } (
$role_meta->get_method_list,
- $role_meta->get_required_method_list
+ map { $_->name } $role_meta->get_required_method_list,
);
}
}
=over 8
-=item * is => 'ro' or 'rw'
+=item * is => 'ro', 'rw', 'bare'
This provides a shorthand for specifying the C<reader>, C<writer>, or
C<accessor> names. If the attribute is read-only ('ro') then it will
read-write attribute, then you will have a C<reader> with the same
name as the attribute, and a C<writer> with the name you provided.
+Use 'bare' when you are deliberately not installing any methods
+(accessor, reader, etc.) associated with this attribute; otherwise,
+Moose will issue a deprecation warning when this attribute is added to a
+metaclass.
+
=item * isa => $type
This option accepts a type. The type can be a string, which should be
This method overrides the parent to also install delegation methods.
+If, after installing all methods, the attribute object has no associated
+methods, it throws an error unless C<< is => 'bare' >> was passed to the
+attribute constructor. (Trying to add an attribute that has no associated
+methods is almost always an error.)
+
=item B<< $attr->remove_accessors >>
This method overrides the parent to also remove delegation methods.