Remove all trailing whitespace
[gitmo/Moose.git] / lib / Moose / Meta / Attribute.pm
index ed87709..e347197 100644 (file)
@@ -4,7 +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;
@@ -24,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
@@ -39,25 +41,46 @@ sub does {
     return $self->Moose::Object::does($name);
 }
 
+sub _error_thrower {
+    my $self = shift;
+    require Moose::Meta::Class;
+    ( ref $self && $self->associated_class ) || "Moose::Meta::Class";
+}
+
 sub throw_error {
     my $self = shift;
-    my $class = ( ref $self && $self->associated_class ) || "Moose::Meta::Class";
+    my $inv = $self->_error_thrower;
     unshift @_, "message" if @_ % 2 == 1;
     unshift @_, attr => $self if ref $self;
-    unshift @_, $class;
-    my $handler = $class->can("throw_error"); # to avoid incrementing depth by 1
+    unshift @_, $inv;
+    my $handler = $inv->can("throw_error"); # to avoid incrementing depth by 1
     goto $handler;
 }
 
 sub _inline_throw_error {
     my ( $self, $msg, $args ) = @_;
-    "\$meta->throw_error($msg" . ($args ? ", $args" : "") . ")"; # FIXME makes deparsing *REALLY* hard
+
+    my $inv = $self->_error_thrower;
+    # 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 =
@@ -106,6 +129,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
@@ -118,17 +143,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;
         }
     }
@@ -524,7 +560,7 @@ sub set_value {
     my ($self, $instance, @args) = @_;
     my $value = $args[0];
 
-    my $attr_name = $self->name;
+    my $attr_name = quotemeta($self->name);
 
     if ($self->is_required and not @args) {
         $self->throw_error("Attribute ($attr_name) is required", object => $instance);
@@ -550,12 +586,13 @@ sub set_value {
 
 sub _inline_set_value {
     my $self = shift;
-    my ($instance, $value, $tc, $tc_obj, $for_constructor) = @_;
+    my ($instance, $value, $tc, $coercion, $message, $for_constructor) = @_;
 
-    my $old   = '@old';
-    my $copy  = '$val';
-    $tc     ||= '$type_constraint';
-    $tc_obj ||= '$type_constraint_obj';
+    my $old     = '@old';
+    my $copy    = '$val';
+    $tc       ||= '$type_constraint';
+    $coercion ||= '$type_coercion';
+    $message  ||= '$type_message';
 
     my @code;
     if ($self->_writer_value_needs_copy) {
@@ -567,7 +604,7 @@ sub _inline_set_value {
     push @code, $self->_inline_check_required
         unless $for_constructor;
 
-    push @code, $self->_inline_tc_code($value, $tc, $tc_obj);
+    push @code, $self->_inline_tc_code($value, $tc, $coercion, $message);
 
     # constructors do triggers all at once at the end
     push @code, $self->_inline_get_old_value_for_trigger($instance, $old)
@@ -616,39 +653,75 @@ sub _inline_check_required {
 
 sub _inline_tc_code {
     my $self = shift;
+    my ($value, $tc, $coercion, $message, $is_lazy) = @_;
     return (
-        $self->_inline_check_coercion(@_),
-        $self->_inline_check_constraint(@_),
+        $self->_inline_check_coercion(
+            $value, $tc, $coercion, $is_lazy,
+        ),
+        $self->_inline_check_constraint(
+            $value, $tc, $message, $is_lazy,
+        ),
     );
 }
 
 sub _inline_check_coercion {
     my $self = shift;
-    my ($value, $tc, $tc_obj) = @_;
+    my ($value, $tc, $coercion) = @_;
 
     return unless $self->should_coerce && $self->type_constraint->has_coercion;
 
-    return $value . ' = ' . $tc_obj . '->coerce(' . $value . ');';
+    if ( $self->type_constraint->can_be_inlined ) {
+        return (
+            'if (! (' . $self->type_constraint->_inline_check($value) . ')) {',
+                $value . ' = ' . $coercion . '->(' . $value . ');',
+            '}',
+        );
+    }
+    else {
+        return (
+            'if (!' . $tc . '->(' . $value . ')) {',
+                $value . ' = ' . $coercion . '->(' . $value . ');',
+            '}',
+        );
+    }
 }
 
 sub _inline_check_constraint {
     my $self = shift;
-    my ($value, $tc, $tc_obj) = @_;
+    my ($value, $tc, $message) = @_;
 
     return unless $self->has_type_constraint;
 
     my $attr_name = quotemeta($self->name);
 
-    return (
-        'if (!' . $tc . '->(' . $value . ')) {',
-            $self->_inline_throw_error(
-                '"Attribute (' . $attr_name . ') does not pass the type '
-              . 'constraint because: " . '
-              . $tc_obj . '->get_message(' . $value . ')',
-                'data => ' . $value
-            ) . ';',
-        '}',
-    );
+    if ( $self->type_constraint->can_be_inlined ) {
+        return (
+            'if (! (' . $self->type_constraint->_inline_check($value) . ')) {',
+                $self->_inline_throw_error(
+                    '"Attribute (' . $attr_name . ') does not pass the type '
+                  . 'constraint because: " . '
+                  . 'do { local $_ = ' . $value . '; '
+                      . $message . '->(' . $value . ')'
+                  . '}',
+                    'data => ' . $value
+                ) . ';',
+            '}',
+        );
+    }
+    else {
+        return (
+            'if (!' . $tc . '->(' . $value . ')) {',
+                $self->_inline_throw_error(
+                    '"Attribute (' . $attr_name . ') does not pass the type '
+                  . 'constraint because: " . '
+                  . 'do { local $_ = ' . $value . '; '
+                      . $message . '->(' . $value . ')'
+                  . '}',
+                    'data => ' . $value
+                ) . ';',
+            '}',
+        );
+    }
 }
 
 sub _inline_get_old_value_for_trigger {
@@ -683,7 +756,45 @@ sub _inline_trigger {
 
     return unless $self->has_trigger;
 
-    return '$attr->trigger->(' . $instance . ', ' . $value . ', ' . $old . ');';
+    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 {
@@ -740,21 +851,22 @@ sub get_value {
 
 sub _inline_get_value {
     my $self = shift;
-    my ($instance, $tc, $tc_obj) = @_;
+    my ($instance, $tc, $coercion, $message) = @_;
 
     my $slot_access = $self->_inline_instance_get($instance);
     $tc           ||= '$type_constraint';
-    $tc_obj       ||= '$type_constraint_obj';
+    $coercion     ||= '$type_coercion';
+    $message      ||= '$type_message';
 
     return (
-        $self->_inline_check_lazy($instance, $tc, $tc_obj),
+        $self->_inline_check_lazy($instance, $tc, $coercion, $message),
         $self->_inline_return_auto_deref($slot_access),
     );
 }
 
 sub _inline_check_lazy {
     my $self = shift;
-    my ($instance, $tc, $tc_obj) = @_;
+    my ($instance, $tc, $coercion, $message) = @_;
 
     return unless $self->is_lazy;
 
@@ -762,14 +874,14 @@ sub _inline_check_lazy {
 
     return (
         'if (!' . $slot_exists . ') {',
-            $self->_inline_init_from_default($instance, '$default', $tc, $tc_obj, 'lazy'),
+            $self->_inline_init_from_default($instance, '$default', $tc, $coercion, $message, 'lazy'),
         '}',
     );
 }
 
 sub _inline_init_from_default {
     my $self = shift;
-    my ($instance, $default, $tc, $tc_obj, $for_lazy) = @_;
+    my ($instance, $default, $tc, $coercion, $message, $for_lazy) = @_;
 
     if (!($self->has_default || $self->has_builder)) {
         $self->throw_error(
@@ -786,8 +898,8 @@ sub _inline_init_from_default {
         # to do things like possibly only do member tc checks, which isn't
         # appropriate for checking the result of a default
         $self->has_type_constraint
-            ? ($self->_inline_check_coercion($default, $tc, $tc_obj, $for_lazy),
-               $self->_inline_check_constraint($default, $tc, $tc_obj, $for_lazy))
+            ? ($self->_inline_check_coercion($default, $tc, $coercion, $for_lazy),
+               $self->_inline_check_constraint($default, $tc, $message, $for_lazy))
             : (),
         $self->_inline_init_slot($instance, $default),
     );
@@ -798,21 +910,26 @@ sub _inline_generate_default {
     my ($instance, $default) = @_;
 
     if ($self->has_default) {
-        return 'my ' . $default . ' = $attr->default(' . $instance . ');';
+        my $source = 'my ' . $default . ' = $attr_default';
+        $source .= '->(' . $instance . ')'
+            if $self->is_default_a_coderef;
+        return $source . ';';
     }
     elsif ($self->has_builder) {
+        my $builder = B::perlstring($self->builder);
+        my $builder_str = quotemeta($self->builder);
+        my $attr_name_str = quotemeta($self->name);
         return (
             'my ' . $default . ';',
-            'if (my $builder = ' . $instance . '->can($attr->builder)) {',
+            'if (my $builder = ' . $instance . '->can(' . $builder . ')) {',
                 $default . ' = ' . $instance . '->$builder;',
             '}',
             'else {',
                 'my $class = ref(' . $instance . ') || ' . $instance . ';',
-                'my $builder_name = $attr->builder;',
-                'my $attr_name = $attr->name;',
                 $self->_inline_throw_error(
                     '"$class does not support builder method '
-                  . '\'$builder_name\' for attribute \'$attr_name\'"'
+                  . '\'' . $builder_str . '\' for attribute '
+                  . '\'' . $attr_name_str . '\'"'
                 ) . ';',
             '}',
         );
@@ -961,7 +1078,7 @@ 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}";
@@ -1030,7 +1147,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'))
@@ -1063,7 +1180,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',
@@ -1077,7 +1194,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',