X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FClass%2FMOP%2FAttribute.pm;h=ce8e0cc33c929fd373aa21ffff541ec065664d51;hb=e76b01fb041ccbdd1ece1c070a6ccfb74ca1611e;hp=1b721d785bbdf532c88477f0dca60fb629a284f2;hpb=d14f6cbe014dcba2caeca6d19a056074356ae534;p=gitmo%2FClass-MOP.git diff --git a/lib/Class/MOP/Attribute.pm b/lib/Class/MOP/Attribute.pm index 1b721d7..ce8e0cc 100644 --- a/lib/Class/MOP/Attribute.pm +++ b/lib/Class/MOP/Attribute.pm @@ -9,7 +9,7 @@ use Class::MOP::Method::Accessor; use Carp 'confess'; use Scalar::Util 'blessed', 'reftype', 'weaken'; -our $VERSION = '0.20'; +our $VERSION = '0.23'; our $AUTHORITY = 'cpan:STEVAN'; use base 'Class::MOP::Object'; @@ -49,22 +49,29 @@ sub new { "wrap then in a CODE reference (ex: sub { [] } and not [])") if exists $options{default} && ref $options{default}; } + if( $options{required} and not( defined($options{builder}) || defined($options{init_arg}) || exists $options{default} ) ) { + confess("A required attribute must have either 'init_arg', 'builder', or 'default'"); + } bless { '$!name' => $name, '$!accessor' => $options{accessor}, '$!reader' => $options{reader}, - '$!writer' => $options{writer}, - '$!predicate' => $options{predicate}, - '$!clearer' => $options{clearer}, - '$!builder' => $options{builder}, - '$!init_arg' => $options{init_arg}, - '$!default' => $options{default}, + '$!writer' => $options{writer}, + '$!predicate' => $options{predicate}, + '$!clearer' => $options{clearer}, + '$!builder' => $options{builder}, + '$!init_arg' => $options{init_arg}, + '$!default' => $options{default}, + '$!initializer' => $options{initializer}, # keep a weakened link to the # class we are associated with '$!associated_class' => undef, # and a list of the methods # associated with this attr '@!associated_methods' => [], + # NOTE: + # protect this from silliness + init_arg => '!............( DO NOT DO THIS )............!', } => $class; } @@ -88,15 +95,27 @@ sub initialize_instance_slot { # if nothing was in the %params, we can use the # attribute's default value (if it has one) - if(exists $params->{$init_arg}){ - $meta_instance->set_slot_value($instance, $self->name, $params->{$init_arg}); + if(defined $init_arg and exists $params->{$init_arg}){ + $self->_set_initial_slot_value( + $meta_instance, + $instance, + $params->{$init_arg}, + ); } elsif (defined $self->{'$!default'}) { - $meta_instance->set_slot_value($instance, $self->name, $self->default($instance)); + $self->_set_initial_slot_value( + $meta_instance, + $instance, + $self->default($instance), + ); } elsif (defined( my $builder = $self->{'$!builder'})) { if ($builder = $instance->can($builder)) { - $meta_instance->set_slot_value($instance, $self->name, $instance->$builder); + $self->_set_initial_slot_value( + $meta_instance, + $instance, + $instance->$builder, + ); } else { confess(blessed($instance)." does not support builder method '". $self->{'$!builder'} ."' for attribute '" . $self->name . "'"); @@ -104,6 +123,24 @@ sub initialize_instance_slot { } } +sub _set_initial_slot_value { + my ($self, $meta_instance, $instance, $value) = @_; + + my $slot_name = $self->name; + + return $meta_instance->set_slot_value($instance, $slot_name, $value) + unless $self->has_initializer; + + my $callback = sub { + $meta_instance->set_slot_value($instance, $slot_name, $_[0]); + }; + + my $initializer = $self->initializer; + + # most things will just want to set a value, so make it first arg + $instance->$initializer($value, $callback, $self); +} + # NOTE: # the next bunch of methods will get bootstrapped # away in the Class::MOP bootstrapping section @@ -113,22 +150,24 @@ sub name { $_[0]->{'$!name'} } sub associated_class { $_[0]->{'$!associated_class'} } sub associated_methods { $_[0]->{'@!associated_methods'} } -sub has_accessor { defined($_[0]->{'$!accessor'}) ? 1 : 0 } -sub has_reader { defined($_[0]->{'$!reader'}) ? 1 : 0 } -sub has_writer { defined($_[0]->{'$!writer'}) ? 1 : 0 } -sub has_predicate { defined($_[0]->{'$!predicate'}) ? 1 : 0 } -sub has_clearer { defined($_[0]->{'$!clearer'}) ? 1 : 0 } -sub has_builder { defined($_[0]->{'$!builder'}) ? 1 : 0 } -sub has_init_arg { defined($_[0]->{'$!init_arg'}) ? 1 : 0 } -sub has_default { defined($_[0]->{'$!default'}) ? 1 : 0 } - -sub accessor { $_[0]->{'$!accessor'} } -sub reader { $_[0]->{'$!reader'} } -sub writer { $_[0]->{'$!writer'} } -sub predicate { $_[0]->{'$!predicate'} } -sub clearer { $_[0]->{'$!clearer'} } -sub builder { $_[0]->{'$!builder'} } -sub init_arg { $_[0]->{'$!init_arg'} } +sub has_accessor { defined($_[0]->{'$!accessor'}) ? 1 : 0 } +sub has_reader { defined($_[0]->{'$!reader'}) ? 1 : 0 } +sub has_writer { defined($_[0]->{'$!writer'}) ? 1 : 0 } +sub has_predicate { defined($_[0]->{'$!predicate'}) ? 1 : 0 } +sub has_clearer { defined($_[0]->{'$!clearer'}) ? 1 : 0 } +sub has_builder { defined($_[0]->{'$!builder'}) ? 1 : 0 } +sub has_init_arg { defined($_[0]->{'$!init_arg'}) ? 1 : 0 } +sub has_default { defined($_[0]->{'$!default'}) ? 1 : 0 } +sub has_initializer { defined($_[0]->{'$!initializer'}) ? 1 : 0 } + +sub accessor { $_[0]->{'$!accessor'} } +sub reader { $_[0]->{'$!reader'} } +sub writer { $_[0]->{'$!writer'} } +sub predicate { $_[0]->{'$!predicate'} } +sub clearer { $_[0]->{'$!clearer'} } +sub builder { $_[0]->{'$!builder'} } +sub init_arg { $_[0]->{'$!init_arg'} } +sub initializer { $_[0]->{'$!initializer'} } # end bootstrapped away method section. # (all methods below here are kept intact) @@ -216,6 +255,15 @@ sub associate_method { ## Slot management +sub set_initial_value { + my ($self, $instance, $value) = @_; + $self->_set_initial_slot_value( + Class::MOP::Class->initialize(blessed($instance))->get_meta_instance, + $instance, + $value + ); +} + sub set_value { my ($self, $instance, $value) = @_; @@ -398,12 +446,6 @@ value of C<-foo>, then the following code will Just Work. In an init_arg is not assigned, it will automatically use the value of C<$name>. -=item I - -The value of this key is the default value which -C will initialize the -attribute to. - =item I The value of this key is the name of the method that will be @@ -411,6 +453,12 @@ called to obtain the value used to initialize the attribute. This should be a method in the class associated with the attribute, not a method in the attribute class itself. +=item I + +The value of this key is the default value which +C will initialize the +attribute to. + B If the value is a simple scalar (string or number), then it can be just passed as is. However, if you wish to initialize it with @@ -495,9 +543,19 @@ C value to the attribute. =item I -This is a basic test to see if the value of the attribute is not -C. It will return true (C<1>) if the attribute's value is -defined, and false (C<0>) otherwise. +This is a basic test to see if any value has been set for the +attribute. It will return true (C<1>) if the attribute has been set +to any value (even C), and false (C<0>) otherwise. + +B +The predicate will return true even when you set an attribute's +value to C. This behaviour has changed as of version 0.43. In +older versions, the predicate (erroneously) checked for attribute +value definedness, instead of presence as it is now. + +If you really want to get rid of the value, you have to define and +use a I (see below). + =item I @@ -527,6 +585,11 @@ know what you are doing. Set the value without going through the accessor. Note that this may be done to even attributes with just read only accessors. +=item B + +This method sets the value without going through the accessor -- but it is only +called when the instance data is first initialized. + =item B Return the value without going through the accessor. Note that this may be done @@ -534,7 +597,7 @@ even to attributes with just write only accessors. =item B -Returns a boolean indicating if the item in the C<$instance> has a value in it. +Return a boolean indicating if the item in the C<$instance> has a value in it. This is basically what the default C method calls. =item B @@ -564,19 +627,23 @@ passed into C. I think they are pretty much self-explanitory. =item B +=item B + =item B =item B =item B -As noted in the documentation for C above, if the I -value is a CODE reference, this accessor will pass a single additional -argument C<$instance> into it and return the value. +Return the default value for the attribute. + +If you pass in an C<$instance> argument to this accessor and the +I is a CODE reference, then the CODE reference will be +executed with the C<$instance> as its argument. =item B -Returns a list of slots required by the attribute. This is usually +Return a list of slots required by the attribute. This is usually just one, which is the name of the attribute. =item B @@ -616,6 +683,8 @@ These are all basic predicate methods for the values passed into C. =item B +=item B + =item B =item B @@ -724,7 +793,7 @@ Stevan Little Estevan@iinteractive.comE =head1 COPYRIGHT AND LICENSE -Copyright 2006, 2007 by Infinity Interactive, Inc. +Copyright 2006-2008 by Infinity Interactive, Inc. L