foo
[gitmo/Class-MOP.git] / lib / Class / MOP / Class.pm
index 9c154e3..1b4580f 100644 (file)
@@ -9,7 +9,9 @@ use Scalar::Util 'blessed', 'reftype';
 use Sub::Name    'subname';
 use B            'svref_2object';
 
-our $VERSION = '0.12';
+our $VERSION = '0.14';
+
+use Class::MOP::Instance;
 
 # Self-introspection 
 
@@ -17,7 +19,7 @@ sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
 
 # Creation
 
-#{
+{
     # Metaclasses are singletons, so we cache them here.
     # there is no need to worry about destruction though
     # because they should die only when the program dies.
@@ -50,13 +52,13 @@ sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
         my $package_name = $options{':package'};
         (defined $package_name && $package_name)
             || confess "You must pass a package name";  
-               # NOTE:
-               # return the metaclass if we have it cached, 
-               # and it is still defined (it has not been 
-               # reaped by DESTROY yet, which can happen 
-               # annoyingly enough during global destruction)
+        # NOTE:
+        # return the metaclass if we have it cached, 
+        # and it is still defined (it has not been 
+        # reaped by DESTROY yet, which can happen 
+        # annoyingly enough during global destruction)
         return $METAS{$package_name} 
-                       if exists $METAS{$package_name} && defined $METAS{$package_name};  
+            if exists $METAS{$package_name} && defined $METAS{$package_name};  
         $class = blessed($class) || $class;
         # now create the metaclass
         my $meta;
@@ -65,7 +67,8 @@ sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
                 '$:package'             => $package_name, 
                 '%:attributes'          => {},
                 '$:attribute_metaclass' => $options{':attribute_metaclass'} || 'Class::MOP::Attribute',
-                '$:method_metaclass'    => $options{':method_metaclass'}    || 'Class::MOP::Method',                
+                '$:method_metaclass'    => $options{':method_metaclass'}    || 'Class::MOP::Method',
+                '$:instance_metaclass'  => $options{':instance_metaclass'}  || 'Class::MOP::Instance',    
             } => $class;
         }
         else {
@@ -84,7 +87,8 @@ sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
         my $self = shift;
 
         # this is always okay ...
-        return if blessed($self) eq 'Class::MOP::Class';
+        return if blessed($self)            eq 'Class::MOP::Class'   && 
+                  $self->instance_metaclass eq 'Class::MOP::Instance';
 
         my @class_list = $self->class_precedence_list;
         shift @class_list; # shift off $self->name
@@ -95,9 +99,16 @@ sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
                 || confess $self->name . "->meta => (" . (blessed($self)) . ")" . 
                            " is not compatible with the " . 
                            $class_name . "->meta => (" . (blessed($meta)) . ")";
+            # NOTE:
+            # we also need to check that instance metaclasses
+            # are compatabile in the same the class.
+            ($self->instance_metaclass->isa($meta->instance_metaclass))
+                || confess $self->name . "->meta => (" . ($self->instance_metaclass) . ")" . 
+                           " is not compatible with the " . 
+                           $class_name . "->meta => (" . ($meta->instance_metaclass) . ")";                           
         }        
-    }
-#}
+    } 
+}
 
 sub create {
     my ($class, $package_name, $package_version, %options) = @_;
@@ -158,6 +169,7 @@ sub name                { $_[0]->{'$:package'}             }
 sub get_attribute_map   { $_[0]->{'%:attributes'}          }
 sub attribute_metaclass { $_[0]->{'$:attribute_metaclass'} }
 sub method_metaclass    { $_[0]->{'$:method_metaclass'}    }
+sub instance_metaclass  { $_[0]->{'$:instance_metaclass'}  }
 
 # Instance Construction & Cloning
 
@@ -170,18 +182,27 @@ sub new_object {
     # which will deal with the singletons
     return $class->construct_class_instance(@_)
         if $class->name->isa('Class::MOP::Class');
-    bless $class->construct_instance(@_) => $class->name;
+    return $class->construct_instance(@_);
 }
 
 sub construct_instance {
     my ($class, %params) = @_;
-    my $instance = {};
+    my $meta_instance = $class->get_meta_instance();
+    my $instance = $meta_instance->create_instance();
     foreach my $attr ($class->compute_all_applicable_attributes()) {
-        $attr->initialize_instance_slot($class, $instance, \%params);
+        $attr->initialize_instance_slot($meta_instance, $instance, \%params);
     }
     return $instance;
 }
 
+sub get_meta_instance {
+    my $class = shift;
+    return $class->instance_metaclass->new(
+        $class, 
+        $class->compute_all_applicable_attributes()
+    );
+}
+
 sub clone_object {
     my $class    = shift;
     my $instance = shift; 
@@ -192,14 +213,19 @@ sub clone_object {
     # Class::MOP::Class singletons here, they 
     # should not be cloned.
     return $instance if $instance->isa('Class::MOP::Class');   
-    bless $class->clone_instance($instance, @_) => blessed($instance);
+    $class->clone_instance($instance, @_);
 }
 
 sub clone_instance {
     my ($class, $instance, %params) = @_;
     (blessed($instance))
         || confess "You can only clone instances, \$self is not a blessed instance";
-    my $clone = { %$instance, %params }; 
+    my $meta_instance = $class->get_meta_instance();
+    my $clone = $meta_instance->clone_instance($instance);        
+    foreach my $key (%params) {
+        next unless $meta_instance->is_valid_slot($key);
+        $meta_instance->set_slot_value($clone, $key, $params{$key});
+    }
     return $clone;    
 }
 
@@ -221,6 +247,13 @@ sub superclasses {
     if (@_) {
         my @supers = @_;
         @{$self->name . '::ISA'} = @supers;
+        # NOTE:
+        # we need to check the metaclass 
+        # compatability here so that we can 
+        # be sure that the superclass is 
+        # not potentially creating an issues 
+        # we don't know about
+        $self->check_metaclass_compatability();
     }
     @{$self->name . '::ISA'};
 }
@@ -232,16 +265,12 @@ sub class_precedence_list {
     # This will do nothing if all is well, and blow
     # up otherwise. Yes, it's an ugly hack, better 
     # suggestions are welcome.
-    { $self->name->isa('This is a test for circular inheritance') }
+    { ($self->name || return)->isa('This is a test for circular inheritance') }
     # ... and now back to our regularly scheduled program
     (
         $self->name, 
         map { 
-            # OPTIMIZATION NOTE:
-            # we grab the metaclass from the %METAS 
-            # hash here to save the initialize() call
-            # if we can, but it is not always possible            
-            ($METAS{$_} || $self->initialize($_))->class_precedence_list()
+            $self->initialize($_)->class_precedence_list()
         } $self->superclasses()
     );   
 }
@@ -257,63 +286,63 @@ sub add_method {
         || confess "Your code block must be a CODE reference";
     my $full_method_name = ($self->name . '::' . $method_name);    
 
-       $method = $self->method_metaclass->wrap($method) unless blessed($method);
-       
+    $method = $self->method_metaclass->wrap($method) unless blessed($method);
+    
     no strict 'refs';
     no warnings 'redefine';
     *{$full_method_name} = subname $full_method_name => $method;
 }
 
 {
-       my $fetch_and_prepare_method = sub {
-               my ($self, $method_name) = @_;
-               # fetch it locally
-               my $method = $self->get_method($method_name);
-               # if we dont have local ...
-               unless ($method) {
-                       # make sure this method even exists ...
-                       ($self->find_next_method_by_name($method_name))
-                               || confess "The method '$method_name' is not found in the inherience hierarchy for this class";
-                       # if so, then create a local which just 
-                       # calls the next applicable method ...                          
-                       $self->add_method($method_name => sub {
-                               $self->find_next_method_by_name($method_name)->(@_);
-                       });
-                       $method = $self->get_method($method_name);
-               }
-               
-               # now make sure we wrap it properly 
-               # (if it isnt already)
-               unless ($method->isa('Class::MOP::Method::Wrapped')) {
-                       $method = Class::MOP::Method::Wrapped->wrap($method);
-                       $self->add_method($method_name => $method);     
-               }               
-               return $method;
-       };
-
-       sub add_before_method_modifier {
-               my ($self, $method_name, $method_modifier) = @_;
-           (defined $method_name && $method_name)
-               || confess "You must pass in a method name";    
-               my $method = $fetch_and_prepare_method->($self, $method_name);
-               $method->add_before_modifier(subname ':before' => $method_modifier);
-       }
-
-       sub add_after_method_modifier {
-               my ($self, $method_name, $method_modifier) = @_;
-           (defined $method_name && $method_name)
-               || confess "You must pass in a method name";    
-               my $method = $fetch_and_prepare_method->($self, $method_name);
-               $method->add_after_modifier(subname ':after' => $method_modifier);
-       }
-       
-       sub add_around_method_modifier {
-               my ($self, $method_name, $method_modifier) = @_;
-           (defined $method_name && $method_name)
-               || confess "You must pass in a method name";
-               my $method = $fetch_and_prepare_method->($self, $method_name);
-               $method->add_around_modifier(subname ':around' => $method_modifier);
-       }       
+    my $fetch_and_prepare_method = sub {
+        my ($self, $method_name) = @_;
+        # fetch it locally
+        my $method = $self->get_method($method_name);
+        # if we dont have local ...
+        unless ($method) {
+            # make sure this method even exists ...
+            ($self->find_next_method_by_name($method_name))
+                || confess "The method '$method_name' is not found in the inherience hierarchy for this class";
+            # if so, then create a local which just 
+            # calls the next applicable method ...              
+            $self->add_method($method_name => sub {
+                $self->find_next_method_by_name($method_name)->(@_);
+            });
+            $method = $self->get_method($method_name);
+        }
+        
+        # now make sure we wrap it properly 
+        # (if it isnt already)
+        unless ($method->isa('Class::MOP::Method::Wrapped')) {
+            $method = Class::MOP::Method::Wrapped->wrap($method);
+            $self->add_method($method_name => $method); 
+        }       
+        return $method;
+    };
+
+    sub add_before_method_modifier {
+        my ($self, $method_name, $method_modifier) = @_;
+        (defined $method_name && $method_name)
+            || confess "You must pass in a method name";    
+        my $method = $fetch_and_prepare_method->($self, $method_name);
+        $method->add_before_modifier(subname ':before' => $method_modifier);
+    }
+
+    sub add_after_method_modifier {
+        my ($self, $method_name, $method_modifier) = @_;
+        (defined $method_name && $method_name)
+            || confess "You must pass in a method name";    
+        my $method = $fetch_and_prepare_method->($self, $method_name);
+        $method->add_after_modifier(subname ':after' => $method_modifier);
+    }
+    
+    sub add_around_method_modifier {
+        my ($self, $method_name, $method_modifier) = @_;
+        (defined $method_name && $method_name)
+            || confess "You must pass in a method name";
+        my $method = $fetch_and_prepare_method->($self, $method_name);
+        $method->add_around_modifier(subname ':around' => $method_modifier);
+    }   
 
     # NOTE: 
     # the methods above used to be named like this:
@@ -338,7 +367,7 @@ sub alias_method {
         || confess "Your code block must be a CODE reference";
     my $full_method_name = ($self->name . '::' . $method_name);
 
-       $method = $self->method_metaclass->wrap($method) unless blessed($method);    
+    $method = $self->method_metaclass->wrap($method) unless blessed($method);    
         
     no strict 'refs';
     no warnings 'redefine';
@@ -354,13 +383,13 @@ sub has_method {
     
     no strict 'refs';
     return 0 if !defined(&{$sub_name});        
-       my $method = \&{$sub_name};
+    my $method = \&{$sub_name};
     return 0 if (svref_2object($method)->GV->STASH->NAME || '') ne $self->name &&
-                (svref_2object($method)->GV->NAME || '')        ne '__ANON__';         
-       
-       # at this point we are relatively sure 
-       # it is our method, so we bless/wrap it 
-       $self->method_metaclass->wrap($method) unless blessed($method);
+                (svref_2object($method)->GV->NAME || '')        ne '__ANON__';      
+    
+    # at this point we are relatively sure 
+    # it is our method, so we bless/wrap it 
+    $self->method_metaclass->wrap($method) unless blessed($method);
     return 1;
 }
 
@@ -369,7 +398,7 @@ sub get_method {
     (defined $method_name && $method_name)
         || confess "You must define a method name";
 
-       return unless $self->has_method($method_name);
+    return unless $self->has_method($method_name);
 
     no strict 'refs';    
     return \&{$self->name . '::' . $method_name};
@@ -392,7 +421,7 @@ sub remove_method {
 sub get_method_list {
     my $self = shift;
     no strict 'refs';
-    grep { $self->has_method($_) } %{$self->name . '::'};
+    grep { $self->has_method($_) } keys %{$self->name . '::'};
 }
 
 sub compute_all_applicable_methods {
@@ -448,23 +477,23 @@ sub find_all_methods_by_name {
 sub find_next_method_by_name {
     my ($self, $method_name) = @_;
     (defined $method_name && $method_name)
-        || confess "You must define a method name to find";    
+        || confess "You must define a method name to find"; 
     # keep a record of what we have seen
     # here, this will handle all the 
     # inheritence issues because we are 
     # using the &class_precedence_list
     my %seen_class;
-       my @cpl = $self->class_precedence_list();
-       shift @cpl; # discard ourselves
+    my @cpl = $self->class_precedence_list();
+    shift @cpl; # discard ourselves
     foreach my $class (@cpl) {
         next if $seen_class{$class};
         $seen_class{$class}++;
         # fetch the meta-class ...
         my $meta = $self->initialize($class);
-               return $meta->get_method($method_name) 
-                       if $meta->has_method($method_name);
+        return $meta->get_method($method_name) 
+            if $meta->has_method($method_name);
     }
-       return;
+    return;
 }
 
 ## Attributes
@@ -478,8 +507,11 @@ sub add_attribute {
     ($attribute->isa('Class::MOP::Attribute'))
         || confess "Your attribute must be an instance of Class::MOP::Attribute (or a subclass)";    
     $attribute->attach_to_class($self);
-    $attribute->install_accessors();        
+    $attribute->install_accessors();
     $self->get_attribute_map->{$attribute->name} = $attribute;
+
+       # FIXME
+       # in theory we have to tell everyone the slot structure may have changed
 }
 
 sub has_attribute {
@@ -493,12 +525,8 @@ sub get_attribute {
     my ($self, $attribute_name) = @_;
     (defined $attribute_name && $attribute_name)
         || confess "You must define an attribute name";
-    # OPTIMIZATION NOTE:
-    # we used to say `if $self->has_attribute($attribute_name)` 
-    # here, but since get_attribute is called so often, we 
-    # eliminate the function call here
-    return $self->{'%:attributes'}->{$attribute_name} 
-        if exists $self->{'%:attributes'}->{$attribute_name};   
+    return $self->get_attribute_map->{$attribute_name} 
+        if $self->has_attribute($attribute_name);   
     return; 
 } 
 
@@ -509,19 +537,14 @@ sub remove_attribute {
     my $removed_attribute = $self->get_attribute_map->{$attribute_name};    
     return unless defined $removed_attribute;
     delete $self->get_attribute_map->{$attribute_name};        
-    $removed_attribute->remove_accessors();        
-    $removed_attribute->detach_from_class();    
+    $removed_attribute->remove_accessors(); 
+    $removed_attribute->detach_from_class();
     return $removed_attribute;
 } 
 
 sub get_attribute_list {
     my $self = shift;
-    # OPTIMIZATION NOTE:
-    # We don't use get_attribute_map here because 
-    # we ask for the attribute list quite often 
-    # in compute_all_applicable_attributes, so 
-    # eliminating the function call helps 
-    keys %{$self->{'%:attributes'}};
+    keys %{$self->get_attribute_map};
 } 
 
 sub compute_all_applicable_attributes {
@@ -536,10 +559,7 @@ sub compute_all_applicable_attributes {
         next if $seen_class{$class};
         $seen_class{$class}++;
         # fetch the meta-class ...
-        # OPTIMIZATION NOTE:
-        # we grab the metaclass from the %METAS 
-        # hash here to save the initialize() call
-        my $meta = $METAS{$class};
+        my $meta = $self->initialize($class);
         foreach my $attr_name ($meta->get_attribute_list()) { 
             next if exists $seen_attr{$attr_name};
             $seen_attr{$attr_name}++;
@@ -620,7 +640,7 @@ sub get_package_variable {
     }
     confess "Could not get the package variable ($variable) because : $e" if $e;    
     # if we didn't die, then we can return it
-       return $ref;
+    return $ref;
 }
 
 sub remove_package_variable {
@@ -775,6 +795,10 @@ to use them or not.
 
 =over 4
 
+=item B<instance_metaclass>
+
+=item B<get_meta_instance>
+
 =item B<new_object (%params)>
 
 This is a convience method for creating a new object of the class, and 
@@ -1199,4 +1223,4 @@ L<http://www.iinteractive.com>
 This library is free software; you can redistribute it and/or modify
 it under the same terms as Perl itself. 
 
-=cut
\ No newline at end of file
+=cutchistian