X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FMeta%2FAttribute.pm;h=46a8412f0ecd474a7641091acfeb3475864c798c;hb=a917d5ae83dc260c6a84fed0ffdc0d1b70c50266;hp=b0160bc3a44853d8953a715fd6822f56abb23edc;hpb=dc2b7cc8f128cbc6c677b209553f6fe0c45af8be;p=gitmo%2FMoose.git diff --git a/lib/Moose/Meta/Attribute.pm b/lib/Moose/Meta/Attribute.pm index b0160bc..46a8412 100644 --- a/lib/Moose/Meta/Attribute.pm +++ b/lib/Moose/Meta/Attribute.pm @@ -4,8 +4,8 @@ package Moose::Meta::Attribute; use strict; use warnings; -use Class::MOP (); use B (); +use Class::Load qw(is_class_loaded load_class); use Scalar::Util 'blessed', 'weaken'; use List::MoreUtils 'any'; use Try::Tiny; @@ -80,7 +80,7 @@ sub _inline_throw_error { 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 = @@ -94,7 +94,22 @@ sub new { if (@bad) { - Carp::cluck "Found unknown argument(s) passed to '$name' attribute constructor in '$class': @bad"; + my $s = @bad > 1 ? 's' : ''; + my $list = join "', '", @bad; + + my $package = $options{definition_context}{package}; + my $context = $options{definition_context}{context} + || 'attribute constructor'; + my $type = $options{definition_context}{type} || 'class'; + + my $location = ''; + if (defined($package)) { + $location = " in "; + $location .= "$type " if $type; + $location .= $package; + } + + Carp::cluck "Found unknown argument$s '$list' in the $context for '$name'$location"; } return $class->SUPER::new($name, %options); @@ -129,6 +144,8 @@ sub interpolate_class { if (my $traits = $options->{traits}) { my $i = 0; + my $has_foreign_options = 0; + while ($i < @$traits) { my $trait = $traits->[$i++]; next if ref($trait); # options to a trait we discarded @@ -141,17 +158,28 @@ sub interpolate_class { push @traits, $trait; # are there options? - push @traits, $traits->[$i++] - if $traits->[$i] && ref($traits->[$i]); + if ($traits->[$i] && ref($traits->[$i])) { + $has_foreign_options = 1 + if any { $_ ne '-alias' && $_ ne '-excludes' } keys %{ $traits->[$i] }; + + push @traits, $traits->[$i++]; + } } if (@traits) { - my $anon_class = Moose::Meta::Class->create_anon_class( + my %options = ( superclasses => [ $class ], roles => [ @traits ], - cache => 1, ); + if ($has_foreign_options) { + $options{weaken} = 0; + } + else { + $options{cache} = 1; + } + + my $anon_class = Moose::Meta::Class->create_anon_class(%options); $class = $anon_class->name; } } @@ -204,7 +232,7 @@ sub clone_and_inherit_options { $type_constraint = $options{isa}; } else { - $type_constraint = Moose::Util::TypeConstraints::find_or_create_isa_type_constraint($options{isa}); + $type_constraint = Moose::Util::TypeConstraints::find_or_create_isa_type_constraint($options{isa}, { package_defined_in => $options{definition_context}->{package} }); (defined $type_constraint) || $self->throw_error("Could not find the type constraint '" . $options{isa} . "'", data => $options{isa}); } @@ -218,7 +246,7 @@ sub clone_and_inherit_options { $type_constraint = $options{does}; } else { - $type_constraint = Moose::Util::TypeConstraints::find_or_create_does_type_constraint($options{does}); + $type_constraint = Moose::Util::TypeConstraints::find_or_create_does_type_constraint($options{does}, { package_defined_in => $options{definition_context}->{package} }); (defined $type_constraint) || $self->throw_error("Could not find the type constraint '" . $options{does} . "'", data => $options{does}); } @@ -349,7 +377,9 @@ sub _process_isa_option { else { $options->{type_constraint} = Moose::Util::TypeConstraints::find_or_create_isa_type_constraint( - $options->{isa} ); + $options->{isa}, + { package_defined_in => $options->{definition_context}->{package} } + ); } } @@ -366,7 +396,9 @@ sub _process_does_option { else { $options->{type_constraint} = Moose::Util::TypeConstraints::find_or_create_does_type_constraint( - $options->{does} ); + $options->{does}, + { package_defined_in => $options->{definition_context}->{package} } + ); } } @@ -631,8 +663,7 @@ sub _inline_check_required { return ( 'if (@_ < 2) {', $self->_inline_throw_error( - '"Attribute (' . $attr_name . ') is required, so cannot ' - . 'be set to undef"' # defined $_[1] is not good enough + '"Attribute (' . $attr_name . ') is required"' ) . ';', '}', ); @@ -732,7 +763,7 @@ sub _inline_weaken_value { my $mi = $self->associated_class->get_meta_instance; return ( - $mi->inline_weaken_slot_value($instance, $self->name, $value), + $mi->inline_weaken_slot_value($instance, $self->name), 'if ref ' . $value . ';', ); } @@ -746,6 +777,44 @@ sub _inline_trigger { return '$trigger->(' . $instance . ', ' . $value . ', ' . $old . ');'; } +sub _eval_environment { + my $self = shift; + + my $env = { }; + + $env->{'$trigger'} = \($self->trigger) + if $self->has_trigger; + $env->{'$attr_default'} = \($self->default) + if $self->has_default; + + if ($self->has_type_constraint) { + my $tc_obj = $self->type_constraint; + + $env->{'$type_constraint'} = \( + $tc_obj->_compiled_type_constraint + ) unless $tc_obj->can_be_inlined; + # these two could probably get inlined versions too + $env->{'$type_coercion'} = \( + $tc_obj->coercion->_compiled_type_coercion + ) if $tc_obj->has_coercion; + $env->{'$type_message'} = \( + $tc_obj->has_message ? $tc_obj->message : $tc_obj->_default_message + ); + + $env = { %$env, %{ $tc_obj->inline_environment } }; + } + + # XXX ugh, fix these + $env->{'$attr'} = \$self + if $self->has_initializer && $self->is_lazy; + # pretty sure this is only going to be closed over if you use a custom + # error class at this point, but we should still get rid of this + # at some point + $env->{'$meta'} = \($self->associated_class); + + return $env; +} + sub _weaken_value { my ( $self, $instance ) = @_; @@ -770,6 +839,10 @@ sub get_value { $value = $self->_coerce_and_verify( $value, $instance ); $self->set_initial_value($instance, $value); + + if ( ref $value && $self->is_weak_ref ) { + $self->_weaken_value($instance); + } } } @@ -851,6 +924,7 @@ sub _inline_init_from_default { $self->_inline_check_constraint($default, $tc, $message, $for_lazy)) : (), $self->_inline_init_slot($instance, $default), + $self->_inline_weaken_value($instance, $default), ); } @@ -859,7 +933,7 @@ sub _inline_generate_default { my ($instance, $default) = @_; if ($self->has_default) { - my $source = 'my ' . $default . ' = $default'; + my $source = 'my ' . $default . ' = $attr_default'; $source .= '->(' . $instance . ')' if $self->is_default_a_coderef; return $source . ';'; @@ -986,6 +1060,7 @@ sub _process_accessors { if ( $method + && !$method->is_stub && !$method->isa('Class::MOP::Method::Accessor') && ( !$self->definition_context || $method->package_name eq $self->definition_context->{package} ) @@ -1027,13 +1102,17 @@ sub install_delegation { # install the delegation ... my $associated_class = $self->associated_class; - foreach my $handle (keys %handles) { + foreach my $handle (sort keys %handles) { my $method_to_call = $handles{$handle}; my $class_name = $associated_class->name; my $name = "${class_name}::${handle}"; - (!$associated_class->has_method($handle)) - || $self->throw_error("You cannot overwrite a locally defined method ($handle) with a delegation", method_name => $handle); + if ( my $method = $associated_class->get_method($handle) ) { + $self->throw_error( + "You cannot overwrite a locally defined method ($handle) with a delegation", + method_name => $handle + ) unless $method->is_stub; + } # NOTE: # handles is not allowed to delegate @@ -1096,7 +1175,7 @@ sub _canonicalize_handles { } } - Class::MOP::load_class($handles); + load_class($handles); my $role_meta = Class::MOP::class_of($handles); (blessed $role_meta && $role_meta->isa('Moose::Meta::Role')) @@ -1129,7 +1208,7 @@ sub _get_delegate_method_list { sub _find_delegate_metaclass { my $self = shift; if (my $class = $self->_isa_metadata) { - unless ( Class::MOP::is_class_loaded($class) ) { + unless ( is_class_loaded($class) ) { $self->throw_error( sprintf( 'The %s attribute is trying to delegate to a class which has not been loaded - %s', @@ -1143,7 +1222,7 @@ sub _find_delegate_metaclass { return Class::MOP::Class->initialize($class); } elsif (my $role = $self->_does_metadata) { - unless ( Class::MOP::is_class_loaded($class) ) { + unless ( is_class_loaded($class) ) { $self->throw_error( sprintf( 'The %s attribute is trying to delegate to a role which has not been loaded - %s', @@ -1242,7 +1321,7 @@ L and add Moose specific features. =over 4 -=item B<< Moose::Meta::Attribute->new(%options) >> +=item B<< Moose::Meta::Attribute->new($name, %options) >> This method overrides the L constructor. @@ -1410,14 +1489,14 @@ I 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. +value fails to pass, the set operation dies. 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, fetch the type_constraint from the attribute using L -and call L. See L +and call L. See L for an example. =back