foo
[gitmo/Moose.git] / lib / Moose / Meta / Attribute.pm
index 14036a1..c806cde 100644 (file)
@@ -7,7 +7,7 @@ use warnings;
 use Scalar::Util 'blessed', 'weaken', 'reftype';
 use Carp         'confess';
 
-our $VERSION = '0.05';
+our $VERSION = '0.06';
 
 use Moose::Util::TypeConstraints ();
 
@@ -15,18 +15,9 @@ use base 'Class::MOP::Attribute';
 
 # options which are not directly used
 # but we store them for metadata purposes
-__PACKAGE__->meta->add_attribute('isa'  => (
-    reader    => 'isa_metadata',
-    predicate => 'has_isa_metadata',    
-));
-__PACKAGE__->meta->add_attribute('does' => (
-    reader    => 'does_metadata',
-    predicate => 'has_does_metadata',    
-));
-__PACKAGE__->meta->add_attribute('is'   => (
-    reader    => 'is_metadata',
-    predicate => 'has_is_metadata',    
-));
+__PACKAGE__->meta->add_attribute('isa'  => (reader    => '_isa_metadata'));
+__PACKAGE__->meta->add_attribute('does' => (reader    => '_does_metadata'));
+__PACKAGE__->meta->add_attribute('is'   => (reader    => '_is_metadata'));
 
 # these are actual options for the attrs
 __PACKAGE__->meta->add_attribute('required'   => (reader => 'is_required'      ));
@@ -50,8 +41,7 @@ __PACKAGE__->meta->add_attribute('handles' => (
 sub new {
        my ($class, $name, %options) = @_;
        $class->_process_options($name, \%options);
-       my $self = $class->SUPER::new($name, %options);    
-    return $self;      
+       return $class->SUPER::new($name, %options);    
 }
 
 sub clone_and_inherit_options {
@@ -98,15 +88,15 @@ sub _process_options {
                }
                elsif ($options->{is} eq 'rw') {
                        $options->{accessor} = $name;                                           
+           ((reftype($options->{trigger}) || '') eq 'CODE')
+               || confess "Trigger must be a CODE ref"
+                   if exists $options->{trigger};                      
                }
                else {
                    confess "I do not understand this option (is => " . $options->{is} . ")"
                }                       
        }
        
-       # process and check trigger here ...
-       
-       
        if (exists $options->{isa}) {
            
            if (exists $options->{does}) {
@@ -232,6 +222,8 @@ sub initialize_instance_slot {
         if ref $val && $self->is_weak_ref;
 }
 
+## Accessor inline subroutines
+
 sub _inline_check_constraint {
        my ($self, $value) = @_;
        return '' unless $self->has_type_constraint;
@@ -245,6 +237,26 @@ defined($attr->type_constraint->check(%s))
 EOF
 }
 
+sub _inline_check_coercion {
+    my $self = shift;
+       return '' unless $self->should_coerce;
+    return 'my $val = $attr->type_constraint->coercion->coerce($_[1]);'
+}
+
+sub _inline_check_required {
+    my $self = shift;
+       return '' unless $self->is_required;
+    return 'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";'
+}
+
+sub _inline_check_lazy {
+    my $self = shift;
+       return '' unless $self->is_lazy;
+    return '$_[0]->{$attr_name} = ($attr->has_default ? $attr->default($_[0]) : undef)'
+         . 'unless exists $_[0]->{$attr_name};';
+}
+
+
 sub _inline_store {
        my ($self, $instance, $value) = @_;
 
@@ -301,24 +313,16 @@ sub generate_accessor_method {
        my $inv = '$_[0]';
     my $code = 'sub { '
     . 'if (scalar(@_) == 2) {'
-        . ($attr->is_required ? 
-            'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";' 
-            : '')
-        . ($attr->should_coerce ? 
-            'my $val = $attr->type_constraint->coercion->coerce($_[1]);'
-            : '')
+        . $attr->_inline_check_required
+        . $attr->_inline_check_coercion
         . $attr->_inline_check_constraint($value_name)
                . $attr->_inline_store($inv, $value_name)
                . $attr->_inline_trigger($inv, $value_name)
     . ' }'
-    . ($attr->is_lazy ? 
-            '$_[0]->{$attr_name} = ($attr->has_default ? $attr->default($_[0]) : undef)'
-            . 'unless exists $_[0]->{$attr_name};'
-            : '')    
+    . $attr->_inline_check_lazy
     . 'return ' . $attr->_inline_auto_deref($attr->_inline_get($inv))
     . ' }';
     my $sub = eval $code;
-    warn "Could not create accessor for '$attr_name' because $@ \n code: $code" if $@;
     confess "Could not create accessor for '$attr_name' because $@ \n code: $code" if $@;
     return $sub;    
 }
@@ -328,12 +332,8 @@ sub generate_writer_method {
     my $value_name = $attr->should_coerce ? '$val' : '$_[1]';
        my $inv = '$_[0]';
     my $code = 'sub { '
-    . ($attr->is_required ? 
-        'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";' 
-        : '')
-    . ($attr->should_coerce ? 
-        'my $val = $attr->type_constraint->coercion->coerce($_[1]);'
-        : '')
+    . $attr->_inline_check_required
+    . $attr->_inline_check_coercion
        . $attr->_inline_check_constraint($value_name)
        . $attr->_inline_store($inv, $value_name)
        . $attr->_inline_trigger($inv, $value_name)
@@ -344,15 +344,12 @@ sub generate_writer_method {
 }
 
 sub generate_reader_method {
-    my $self = shift;
-    my $attr_name = $self->slots;
+    my $attr = shift;
+    my $attr_name = $attr->slots;
     my $code = 'sub {'
     . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
-    . ($self->is_lazy ? 
-            '$_[0]->{$attr_name} = ($self->has_default ? $self->default($_[0]) : undef)'
-            . 'unless exists $_[0]->{$attr_name};'
-            : '')
-    . 'return ' . $self->_inline_auto_deref( '$_[0]->{$attr_name}' ) . ';'
+    . $attr->_inline_check_lazy
+    . 'return ' . $attr->_inline_auto_deref( '$_[0]->{$attr_name}' ) . ';'
     . '}';
     my $sub = eval $code;
     confess "Could not create reader for '$attr_name' because $@ \n code: $code" if $@;
@@ -403,6 +400,8 @@ sub install_accessors {
     return;
 }
 
+# private methods to help delegation ...
+
 sub _canonicalize_handles {
     my $self    = shift;
     my $handles = $self->handles;
@@ -428,8 +427,7 @@ sub _canonicalize_handles {
 
 sub _find_delegate_metaclass {
     my $self = shift;
-    if ($self->has_isa_metadata) {
-        my $class = $self->isa_metadata;
+    if (my $class = $self->_isa_metadata) {
         # if the class does have 
         # a meta method, use it
         return $class->meta if $class->can('meta');
@@ -439,10 +437,10 @@ sub _find_delegate_metaclass {
         # our own metaclass
         return Moose::Meta::Class->initialize($class);
     }
-    elsif ($self->has_does_metadata) {
+    elsif (my $role = $self->_does_metadata) {
         # our role will always have 
         # a meta method
-        return $self->does_metadata->meta;
+        return $role->meta;
     }
     else {
         confess "Cannot find delegate metaclass for attribute " . $self->name;
@@ -497,8 +495,6 @@ will behave just as L<Class::MOP::Attribute> does.
 
 =item B<new>
 
-=item B<clone_and_inherit_options>
-
 =item B<initialize_instance_slot>
 
 =item B<generate_accessor_method>
@@ -518,6 +514,12 @@ creation and type coercion.
 
 =over 4
 
+=item B<clone_and_inherit_options>
+
+This is to support the C<has '+foo'> feature, it clones an attribute 
+from a superclass and allows a very specific set of changes to be made 
+to the attribute.
+
 =item B<has_type_constraint>
 
 Returns true if this meta-attribute has a type constraint.
@@ -585,6 +587,8 @@ to cpan-RT.
 
 Stevan Little E<lt>stevan@iinteractive.comE<gt>
 
+Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
+
 =head1 COPYRIGHT AND LICENSE
 
 Copyright 2006 by Infinity Interactive, Inc.