X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FClass%2FMOP%2FInstance.pm;h=16dd6e0ea7ebcd6060bf70370dccc99c056c0b16;hb=88bb3cf1a0e38a011d04bc7eff7f2e59a0c304e1;hp=20539cd10a3a21cbb7330af3d7847b3825b943f2;hpb=58287a97d2fbdcd7271a6f4b860cb553ce4b2d26;p=gitmo%2FClass-MOP.git diff --git a/lib/Class/MOP/Instance.pm b/lib/Class/MOP/Instance.pm index 20539cd..16dd6e0 100644 --- a/lib/Class/MOP/Instance.pm +++ b/lib/Class/MOP/Instance.pm @@ -4,137 +4,211 @@ package Class::MOP::Instance; use strict; use warnings; -use Carp 'confess'; -use Scalar::Util 'blessed', 'reftype', 'weaken'; +use Scalar::Util 'weaken', 'blessed'; -our $VERSION = '0.01'; +our $VERSION = '0.78'; +$VERSION = eval $VERSION; +our $AUTHORITY = 'cpan:STEVAN'; -sub meta { - require Class::MOP::Class; - Class::MOP::Class->initialize(blessed($_[0]) || $_[0]); +use base 'Class::MOP::Object'; + +sub BUILDARGS { + my ($class, @args) = @_; + + if ( @args == 1 ) { + unshift @args, "associated_metaclass"; + } elsif ( @args >= 2 && blessed($args[0]) && $args[0]->isa("Class::MOP::Class") ) { + # compat mode + my ( $meta, @attrs ) = @args; + @args = ( associated_metaclass => $meta, attributes => \@attrs ); + } + + my %options = @args; + # FIXME lazy_build + $options{slots} ||= [ map { $_->slots } @{ $options{attributes} || [] } ]; + $options{slot_hash} = { map { $_ => undef } @{ $options{slots} } }; # FIXME lazy_build + + return \%options; +} + +sub new { + my $class = shift; + my $options = $class->BUILDARGS(@_); + + # FIXME replace with a proper constructor + my $instance = $class->_new(%$options); + + # FIXME weak_ref => 1, + weaken($instance->{'associated_metaclass'}); + + return $instance; } -sub new { - my ( $class, $meta ) = @_; +sub _new { + my ( $class, %options ) = @_; bless { - meta => $meta, - instance_layout => {} - } => $class; + # NOTE: + # I am not sure that it makes + # sense to pass in the meta + # The ideal would be to just + # pass in the class name, but + # that is placing too much of + # an assumption on bless(), + # which is *probably* a safe + # assumption,.. but you can + # never tell <:) + 'associated_metaclass' => $options{associated_metaclass}, + 'attributes' => $options{attributes}, + 'slots' => $options{slots}, + 'slot_hash' => $options{slot_hash}, + } => $class; } +sub _class_name { $_[0]->{_class_name} ||= $_[0]->associated_metaclass->name } + +sub associated_metaclass { $_[0]{'associated_metaclass'} } + sub create_instance { - my ( $self, $class ) = @_; - - # rely on autovivification - $self->bless_instance_structure( {}, $class ); + my $self = shift; + bless {}, $self->_class_name; } +# for compatibility sub bless_instance_structure { - my ( $self, $instance_structure, $class ) = @_; - $class ||= $self->{meta}->name; - bless $instance_structure, $class; + my ($self, $instance_structure) = @_; + bless $instance_structure, $self->_class_name; } -sub get_all_parents { - my $self = shift; - my @parents = $self->{meta}->class_precedence_list; - shift @parents; # shift off ourselves - return map { $_->get_meta_instance } map { $_->meta || () } @parents; +sub clone_instance { + my ($self, $instance) = @_; + bless { %$instance }, $self->_class_name; } # operations on meta instance -sub add_slot { - my ($self, $slot_name ) = @_; - confess "The slot '$slot_name' already exists" - if 0 && $self->has_slot_recursively( $slot_name ); - $self->{instance_layout}->{$slot_name} = undef; -} - -sub has_slot { - my ($self, $slot_name) = @_; - exists $self->{instance_layout}->{$slot_name} ? 1 : 0; +sub get_all_slots { + my $self = shift; + return @{$self->{'slots'}}; } -sub has_slot_recursively { - my ( $self, $slot_name ) = @_; - return 1 if $self->has_slot($slot_name); - $_->has_slot_recursively($slot_name) && return 1 for $self->get_all_parents; - return 0; +sub get_all_attributes { + my $self = shift; + return @{$self->{attributes}}; } -sub remove_slot { - my ( $self, $slot_name ) = @_; - # NOTE: - # this does not search recursively cause - # that is not the domain of this meta-instance - # it is specific to this class ... - confess "The slot '$slot_name' does not exist (maybe it's inherited?)" - if 0 && $self->has_slot( $slot_name ); - delete $self->{instance_layout}->{$slot_name}; +sub is_valid_slot { + my ($self, $slot_name) = @_; + exists $self->{'slot_hash'}->{$slot_name}; } - # operations on created instances sub get_slot_value { my ($self, $instance, $slot_name) = @_; - return $instance->{$slot_name}; + $instance->{$slot_name}; } -# can be called only after initialize_slot_value sub set_slot_value { my ($self, $instance, $slot_name, $value) = @_; - $slot_name or confess "must provide slot name"; $instance->{$slot_name} = $value; } -# convenience method -# non autovivifying stores will have this as { initialize_slot unless slot_initlized; set_slot_value } -sub set_slot_value_with_init { - my ( $self, $instance, $slot_name, $value ) = @_; - $self->set_slot_value( $instance, $slot_name, $value ); +sub initialize_slot { + my ($self, $instance, $slot_name) = @_; + return; } -sub initialize_slot { +sub deinitialize_slot { my ( $self, $instance, $slot_name ) = @_; + delete $instance->{$slot_name}; +} + +sub initialize_all_slots { + my ($self, $instance) = @_; + foreach my $slot_name ($self->get_all_slots) { + $self->initialize_slot($instance, $slot_name); + } +} + +sub deinitialize_all_slots { + my ($self, $instance) = @_; + foreach my $slot_name ($self->get_all_slots) { + $self->deinitialize_slot($instance, $slot_name); + } +} + +sub is_slot_initialized { + my ($self, $instance, $slot_name, $value) = @_; + exists $instance->{$slot_name}; } -sub slot_initialized { +sub weaken_slot_value { my ($self, $instance, $slot_name) = @_; - exists $instance->{$slot_name} ? 1 : 0; + weaken $instance->{$slot_name}; +} + +sub strengthen_slot_value { + my ($self, $instance, $slot_name) = @_; + $self->set_slot_value($instance, $slot_name, $self->get_slot_value($instance, $slot_name)); +} + +sub rebless_instance_structure { + my ($self, $instance, $metaclass) = @_; + + # we use $_[1] here because of t/306_rebless_overload.t regressions on 5.8.8 + bless $_[1], $metaclass->name; } +sub is_dependent_on_superclasses { + return; # for meta instances that require updates on inherited slot changes +} # inlinable operation snippets +sub is_inlinable { 1 } + +sub inline_create_instance { + my ($self, $class_variable) = @_; + 'bless {} => ' . $class_variable; +} + +sub inline_slot_access { + my ($self, $instance, $slot_name) = @_; + sprintf q[%s->{"%s"}], $instance, quotemeta($slot_name); +} + sub inline_get_slot_value { my ($self, $instance, $slot_name) = @_; - sprintf "%s->{%s}", $instance, $slot_name; + $self->inline_slot_access($instance, $slot_name); } sub inline_set_slot_value { my ($self, $instance, $slot_name, $value) = @_; - $self->_inline_slot_lvalue . " = $value", + $self->inline_slot_access($instance, $slot_name) . " = $value", } -sub inline_set_slot_value_with_init { - my ( $self, $instance, $slot_name, $value) = @_; - $self->inline_set_slot_value( $instance, $slot_name, $value ) . ";"; +sub inline_initialize_slot { + my ($self, $instance, $slot_name) = @_; + return ''; } -sub inline_initialize_slot { - return ""; +sub inline_deinitialize_slot { + my ($self, $instance, $slot_name) = @_; + "delete " . $self->inline_slot_access($instance, $slot_name); +} +sub inline_is_slot_initialized { + my ($self, $instance, $slot_name) = @_; + "exists " . $self->inline_slot_access($instance, $slot_name); } -sub inline_slot_initialized { +sub inline_weaken_slot_value { my ($self, $instance, $slot_name) = @_; - "exists " . $self->inline_get_slot_value; + sprintf "Scalar::Util::weaken( %s )", $self->inline_slot_access($instance, $slot_name); } -sub _inline_slot_lvalue { +sub inline_strengthen_slot_value { my ($self, $instance, $slot_name) = @_; - $self->inline_slot_value; + $self->inline_set_slot_value($instance, $slot_name, $self->inline_slot_access($instance, $slot_name)); } 1; @@ -143,53 +217,170 @@ __END__ =pod -=head1 NAME +=head1 NAME Class::MOP::Instance - Instance Meta Object -=head1 SYNOPSIS - =head1 DESCRIPTION +The meta instance API controls the creation of object instances, and +the storage of attribute values in those instances. + +Using this API directly in your own code violates encapsulation, and +we recommend that you use the appropriate APIs in L +and L instead. Those APIs in turn call the +methods in this class as appropriate. + +This class also participates in generating inlined code by providing +snippets of code to access an object instance. + =head1 METHODS +=head2 Object construction + =over 4 -=item B +=item B<< Class::MOP::Instance->new(%options) >> -=item B +This method creates a new meta-instance object. -=item B +It accepts the following keys in C<%options>: -=item B +=over 8 -=item B +=item * associated_metaclass -=item B +The L object for which instances will be created. -=item B +=item * attributes -=item B +An array reference of L objects. These are the +attributes which can be stored in each instance. -=item B +=back -=item B +=back -=item B +=head2 Creating and altering instances -=item B +=over 4 + +=item B<< $metainstance->create_instance >> + +This method returns a reference blessed into the associated +metaclass's class. + +The default is to use a hash reference. Subclasses can override this. + +=item B<< $metainstance->clone_instance($instance) >> + +Given an instance, this method creates a new object by making +I clone of the original. + +=back + +=head2 Introspection + +=over 4 + +=item B<< $metainstance->associated_metaclass >> + +This returns the L object associated with the +meta-instance object. -=item B +=item B<< $metainstance->get_all_slots >> -=item B +This returns a list of slot names stored in object instances. In +almost all cases, slot names correspond directly attribute names. -=item B +=item B<< $metainstance->is_valid_slot($slot_name) >> -=item B +This will return true if C<$slot_name> is a valid slot name. -=item B +=item B<< $metainstance->get_all_attributes >> -=item B +This returns a list of attributes corresponding to the attributes +passed to the constructor. + +=back + +=head2 Operations on Instance Structures + +It's important to understand that the meta-instance object is a +different entity from the actual instances it creates. For this +reason, any operations on the C<$instance_structure> always require +that the object instance be passed to the method. + +=over 4 + +=item B<< $metainstance->get_slot_value($instance_structure, $slot_name) >> + +=item B<< $metainstance->set_slot_value($instance_structure, $slot_name, $value) >> + +=item B<< $metainstance->initialize_slot($instance_structure, $slot_name) >> + +=item B<< $metainstance->deinitialize_slot($instance_structure, $slot_name) >> + +=item B<< $metainstance->initialize_all_slots($instance_structure) >> + +=item B<< $metainstance->deinitialize_all_slots($instance_structure) >> + +=item B<< $metainstance->is_slot_initialized($instance_structure, $slot_name) >> + +=item B<< $metainstance->weaken_slot_value($instance_structure, $slot_name) >> + +=item B<< $metainstance->strengthen_slot_value($instance_structure, $slot_name) >> + +=item B<< $metainstance->rebless_instance_structure($instance_structure, $new_metaclass) >> + +The exact details of what each method does should be fairly obvious +from the method name. + +=back + +=head2 Inlinable Instance Operations + +=over 4 + +=item B<< $metainstance->is_inlinable >> + +This is a boolean that indicates whether or not slot access operations +can be inlined. By default it is true, but subclasses can override +this. + +=item B<< $metainstance->inline_create_instance($class_variable) >> + +This method expects a string that, I, will become a +class name. This would literally be something like C<'$class'>, not an +actual class name. + +It returns a snippet of code that creates a new object for the +class. This is something like C< bless {}, $class_name >. + +=item B<< $metainstance->inline_slot_access($instance_variable, $slot_name) >> + +=item B<< $metainstance->inline_get_slot_value($instance_variable, $slot_name) >> + +=item B<< $metainstance->inline_set_slot_value($instance_variable, $slot_name, $value) >> + +=item B<< $metainstance->inline_initialize_slot($instance_variable, $slot_name) >> + +=item B<< $metainstance->inline_deinitialize_slot($instance_variable, $slot_name) >> + +=item B<< $metainstance->inline_is_slot_initialized($instance_variable, $slot_name) >> + +=item B<< $metainstance->inline_weaken_slot_value($instance_variable, $slot_name) >> + +=item B<< $metainstance->inline_strengthen_slot_value($instance_variable, $slot_name) >> + +These methods all expect two arguments. The first is the name of a +variable, than when inlined, will represent the object +instance. Typically this will be a literal string like C<'$_[0]'>. + +The second argument is a slot name. + +The method returns a snippet of code that, when inlined, performs some +operation on the instance. =back @@ -197,24 +388,30 @@ Class::MOP::Instance - Instance Meta Object =over 4 -=item B +=item B<< Class::MOP::Instance->meta >> -This will return a B instance which is related -to this class. +This will return a L instance for this class. + +It should also be noted that L will actually bootstrap +this module by installing a number of attribute meta-objects into its +metaclass. =back -=head1 AUTHOR +=head1 AUTHORS + +Yuval Kogman Enothingmuch@woobling.comE Stevan Little Estevan@iinteractive.comE =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Infinity Interactive, Inc. +Copyright 2006-2009 by Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself. +it under the same terms as Perl itself. + +=cut -=cut \ No newline at end of file