Explicitly pass message
[gitmo/Moose.git] / lib / Moose / Meta / Attribute.pm
index aec1ef1..4391475 100644 (file)
@@ -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;
@@ -25,6 +25,7 @@ Class::MOP::MiniTrait::apply(__PACKAGE__, 'Moose::Meta::Object::Trait');
 __PACKAGE__->meta->add_attribute('traits' => (
     reader    => 'applied_traits',
     predicate => 'has_applied_traits',
+    Class::MOP::_definition_context(),
 ));
 
 # we need to have a ->does method in here to
@@ -48,12 +49,7 @@ sub _error_thrower {
 
 sub throw_error {
     my $self = shift;
-    my $inv = $self->_error_thrower;
-    unshift @_, "message" if @_ % 2 == 1;
-    unshift @_, attr => $self if ref $self;
-    unshift @_, $inv;
-    my $handler = $inv->can("throw_error"); # to avoid incrementing depth by 1
-    goto $handler;
+    Moose::Util::throw(@_);
 }
 
 sub _inline_throw_error {
@@ -63,23 +59,13 @@ sub _inline_throw_error {
     # XXX ugh
     $inv = 'Moose::Meta::Class' unless $inv->can('_inline_throw_error');
 
-    # XXX ugh ugh UGH
-    my $class = $self->associated_class;
-    if ($class) {
-        my $class_name = B::perlstring($class->name);
-        my $attr_name = B::perlstring($self->name);
-        $args = 'attr => Class::MOP::class_of(' . $class_name . ')'
-              . '->find_attribute_by_name(' . $attr_name . '), '
-              . (defined $args ? $args : '');
-    }
-
     return $inv->_inline_throw_error($msg, $args)
 }
 
 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 =
@@ -128,6 +114,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
@@ -140,17 +128,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;
         }
     }
@@ -195,7 +194,7 @@ sub clone_and_inherit_options {
 
     my @found_illegal_options = grep { exists $options{$_} && exists $self->{$_} ? $_ : undef } @illegal_options;
     (scalar @found_illegal_options == 0)
-        || $self->throw_error("Illegal inherited options => (" . (join ', ' => @found_illegal_options) . ")", data => \%options);
+        || $self->throw_error(message => "Illegal inherited options => (" . (join ', ' => @found_illegal_options) . ")", data => \%options);
 
     if ($options{isa}) {
         my $type_constraint;
@@ -203,9 +202,9 @@ 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});
+                || $self->throw_error(message => "Could not find the type constraint '" . $options{isa} . "'", data => $options{isa});
         }
 
         $options{type_constraint} = $type_constraint;
@@ -217,9 +216,9 @@ 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});
+                || $self->throw_error(message => "Could not find the type constraint '" . $options{does} . "'", data => $options{does});
         }
 
         $options{type_constraint} = $type_constraint;
@@ -297,7 +296,7 @@ sub _process_is_option {
 
     if ( $options->{is} eq 'ro' ) {
         $class->throw_error(
-            "Cannot define an accessor name on a read-only attribute, accessors are read/write",
+            message => "Cannot define an accessor name on a read-only attribute, accessors are read/write",
             data => $options )
             if exists $options->{accessor};
         $options->{reader} ||= $name;
@@ -315,9 +314,11 @@ sub _process_is_option {
         # 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(
+            message => "I do not understand this option (is => "
+                     . $options->{is}
+                     . ") on attribute ($name)", data => $options->{is}
+        );
     }
 }
 
@@ -330,12 +331,12 @@ sub _process_isa_option {
         if ( try { $options->{isa}->can('does') } ) {
             ( $options->{isa}->does( $options->{does} ) )
                 || $class->throw_error(
-                "Cannot have an isa option and a does option if the isa does not do the does on attribute ($name)",
+                message => "Cannot have an isa option and a does option if the isa does not do the does on attribute ($name)",
                 data => $options );
         }
         else {
             $class->throw_error(
-                "Cannot have an isa option which cannot ->does() on attribute ($name)",
+                message => "Cannot have an isa option which cannot ->does() on attribute ($name)",
                 data => $options );
         }
     }
@@ -348,7 +349,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} }
+        );
     }
 }
 
@@ -365,7 +368,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} }
+        );
     }
 }
 
@@ -376,11 +381,11 @@ sub _process_coerce_option {
 
     ( exists $options->{type_constraint} )
         || $class->throw_error(
-        "You cannot have coercion without specifying a type constraint on attribute ($name)",
+        message => "You cannot have coercion without specifying a type constraint on attribute ($name)",
         data => $options );
 
     $class->throw_error(
-        "You cannot have a weak reference to a coerced value on attribute ($name)",
+        message => "You cannot have a weak reference to a coerced value on attribute ($name)",
         data => $options )
         if $options->{weak_ref};
 
@@ -401,7 +406,7 @@ sub _process_trigger_option {
     return unless exists $options->{trigger};
 
     ( 'CODE' eq ref $options->{trigger} )
-        || $class->throw_error("Trigger must be a CODE ref on attribute ($name)", data => $options->{trigger});
+        || $class->throw_error(message => "Trigger must be a CODE ref on attribute ($name)", data => $options->{trigger});
 }
 
 sub _process_auto_deref_option {
@@ -411,13 +416,13 @@ sub _process_auto_deref_option {
 
     ( exists $options->{type_constraint} )
         || $class->throw_error(
-        "You cannot auto-dereference without specifying a type constraint on attribute ($name)",
+        message => "You cannot auto-dereference without specifying a type constraint on attribute ($name)",
         data => $options );
 
     ( $options->{type_constraint}->is_a_type_of('ArrayRef')
       || $options->{type_constraint}->is_a_type_of('HashRef') )
         || $class->throw_error(
-        "You cannot auto-dereference anything other than a ArrayRef or HashRef on attribute ($name)",
+        message => "You cannot auto-dereference anything other than a ArrayRef or HashRef on attribute ($name)",
         data => $options );
 }
 
@@ -427,7 +432,7 @@ sub _process_lazy_build_option {
     return unless $options->{lazy_build};
 
     $class->throw_error(
-        "You can not use lazy_build and default for the same attribute ($name)",
+        message => "You can not use lazy_build and default for the same attribute ($name)",
         data => $options )
         if exists $options->{default};
 
@@ -451,7 +456,7 @@ sub _process_lazy_option {
 
     ( exists $options->{default} || defined $options->{builder} )
         || $class->throw_error(
-        "You cannot have a lazy attribute ($name) without specifying a default value for it",
+        message => "You cannot have a lazy attribute ($name) without specifying a default value for it",
         data => $options );
 }
 
@@ -467,7 +472,7 @@ sub _process_required_option {
         )
         ) {
         $class->throw_error(
-            "You cannot have a required attribute ($name) without a default, builder, or an init_arg",
+            message => "You cannot have a required attribute ($name) without a default, builder, or an init_arg",
             data => $options );
     }
 }
@@ -487,7 +492,7 @@ sub initialize_instance_slot {
         # skip it if it's lazy
         return if $self->is_lazy;
         # and die if it's required and doesn't have a default value
-        $self->throw_error("Attribute (" . $self->name . ") is required", object => $instance, data => $params)
+        $self->throw_error(message => "Attribute (" . $self->name . ") is required", object => $instance, data => $params)
             if $self->is_required && !$self->has_default && !$self->has_builder;
 
         # if nothing was in the %params, we can use the
@@ -521,12 +526,13 @@ sub _call_builder {
     return $instance->$builder()
         if $instance->can( $self->builder );
 
-    $self->throw_error(  blessed($instance)
-            . " does not support builder method '"
-            . $self->builder
-            . "' for attribute '"
-            . $self->name
-            . "'",
+    $self->throw_error(
+            message => blessed($instance)
+                     . " does not support builder method '"
+                     . $self->builder
+                     . "' for attribute '"
+                     . $self->name
+                     . "'",
             object => $instance,
      );
 }
@@ -549,7 +555,7 @@ sub set_value {
     my $attr_name = quotemeta($self->name);
 
     if ($self->is_required and not @args) {
-        $self->throw_error("Attribute ($attr_name) is required", object => $instance);
+        $self->throw_error(message => "Attribute ($attr_name) is required", object => $instance);
     }
 
     $value = $self->_coerce_and_verify( $value, $instance );
@@ -731,7 +737,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 . ';',
     );
 }
@@ -745,6 +751,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 ) = @_;
 
@@ -769,6 +813,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);
+            }
         }
     }
 
@@ -787,7 +835,7 @@ sub get_value {
             return wantarray ? %{ $rv } : $rv;
         }
         else {
-            $self->throw_error("Can not auto de-reference the type constraint '" . $type_constraint->name . "'", object => $instance, type_constraint => $type_constraint);
+            $self->throw_error(message => "Can not auto de-reference the type constraint '" . $type_constraint->name . "'", object => $instance, type_constraint => $type_constraint);
         }
 
     }
@@ -833,7 +881,7 @@ sub _inline_init_from_default {
 
     if (!($self->has_default || $self->has_builder)) {
         $self->throw_error(
-            'You cannot have a lazy attribute '
+            message => 'You cannot have a lazy attribute '
           . '(' . $self->name . ') '
           . 'without specifying a default value for it',
             attr => $self,
@@ -850,6 +898,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),
     );
 }
 
@@ -858,7 +907,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 . ';';
@@ -884,7 +933,7 @@ sub _inline_generate_default {
     }
     else {
         $self->throw_error(
-            "Can't generate a default for " . $self->name
+            message => "Can't generate a default for " . $self->name
           . " since no default or builder was specified"
         );
     }
@@ -925,7 +974,7 @@ sub _auto_deref {
     }
     else {
         $self->throw_error(
-            'Can not auto de-reference the type constraint \''
+            message => 'Can not auto de-reference the type constraint \''
           . $type_constraint->name
           . '\'',
             type_constraint => $type_constraint,
@@ -985,6 +1034,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} )
@@ -1026,13 +1076,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(
+                message => "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
@@ -1077,7 +1131,7 @@ sub _canonicalize_handles {
         }
         elsif ($handle_type eq 'Regexp') {
             ($self->has_type_constraint)
-                || $self->throw_error("Cannot delegate methods based on a Regexp without a type constraint (isa)", data => $handles);
+                || $self->throw_error(message => "Cannot delegate methods based on a Regexp without a type constraint (isa)", data => $handles);
             return map  { ($_ => $_) }
                    grep { /$handles/ } $self->_get_delegate_method_list;
         }
@@ -1091,15 +1145,15 @@ sub _canonicalize_handles {
             $handles = $handles->role;
         }
         else {
-            $self->throw_error("Unable to canonicalize the 'handles' option with $handles", data => $handles);
+            $self->throw_error(message => "Unable to canonicalize the 'handles' option with $handles", data => $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'))
-        || $self->throw_error("Unable to canonicalize the 'handles' option with $handles because its metaclass is not a Moose::Meta::Role", data => $handles);
+        || $self->throw_error(message => "Unable to canonicalize the 'handles' option with $handles because its metaclass is not a Moose::Meta::Role", data => $handles);
 
     return map { $_ => $_ }
         map { $_->name }
@@ -1121,16 +1175,16 @@ sub _get_delegate_method_list {
         return $meta->get_method_list;
     }
     else {
-        $self->throw_error("Unable to recognize the delegate metaclass '$meta'", data => $meta);
+        $self->throw_error(message => "Unable to recognize the delegate metaclass '$meta'", data => $meta);
     }
 }
 
 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(
+                message => sprintf(
                     'The %s attribute is trying to delegate to a class which has not been loaded - %s',
                     $self->name, $class
                 )
@@ -1142,9 +1196,9 @@ 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(
+                message => sprintf(
                     'The %s attribute is trying to delegate to a role which has not been loaded - %s',
                     $self->name, $role
                 )
@@ -1154,7 +1208,7 @@ sub _find_delegate_metaclass {
         return Class::MOP::class_of($role);
     }
     else {
-        $self->throw_error("Cannot find delegate metaclass for attribute " . $self->name);
+        $self->throw_error(message => "Cannot find delegate metaclass for attribute " . $self->name);
     }
 }
 
@@ -1409,14 +1463,14 @@ I<Attribute (x) does not pass the type constraint (Int) with 'forty-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</throw_error>.
+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<Class::MOP::Class/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::Basics::Recipe4>
+and call L<Moose::Meta::TypeConstraint/check>. See L<Moose::Cookbook::Basics::Company_Subtypes>
 for an example.
 
 =back