foo
[gitmo/Class-MOP.git] / lib / Class / MOP / Class.pm
index 85198f9..c66bb05 100644 (file)
@@ -5,7 +5,7 @@ use strict;
 use warnings;
 
 use Carp         'confess';
-use Scalar::Util 'blessed', 'reftype';
+use Scalar::Util 'blessed', 'reftype', 'weaken';
 use Sub::Name    'subname';
 use B            'svref_2object';
 
@@ -40,6 +40,34 @@ sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
         $class->construct_class_instance(':package' => $package_name, @_);
     }
     
+    sub reinitialize {
+        my $class        = shift;
+        my $package_name = shift;
+        (defined $package_name && $package_name && !blessed($package_name))
+            || confess "You must pass a package name and it cannot be blessed";    
+        $METAS{$package_name} = undef;
+        $class->construct_class_instance(':package' => $package_name, @_);
+    }   
+    
+    # NOTE:
+    # we need a sufficiently annoying prefix
+    # this should suffice for now
+    my $ANON_CLASS_PREFIX = 'Class::MOP::Class::__ANON__::SERIAL::';
+    
+    {
+        # NOTE:
+        # this should be sufficient, if you have a 
+        # use case where it is not, write a test and 
+        # I will change it.
+        my $ANON_CLASS_SERIAL = 0;
+
+        sub create_anon_class {
+            my ($class, %options) = @_;   
+            my $package_name = $ANON_CLASS_PREFIX . ++$ANON_CLASS_SERIAL;
+            return $class->create($package_name, '0.00', %options);
+        }
+    }     
+    
     # NOTE: (meta-circularity) 
     # this is a special form of &construct_instance 
     # (see below), which is used to construct class
@@ -76,11 +104,34 @@ sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
             # it is safe to use meta here because
             # class will always be a subclass of 
             # Class::MOP::Class, which defines meta
-            $meta = bless $class->meta->construct_instance(%options) => $class
+            $meta = $class->meta->construct_instance(%options)
         }
         # and check the metaclass compatibility
         $meta->check_metaclass_compatability();
         $METAS{$package_name} = $meta;
+        # NOTE:
+        # we need to weaken any anon classes
+        # so that they can call DESTROY properly
+        weaken($METAS{$package_name})
+            if $package_name =~ /^$ANON_CLASS_PREFIX/;
+        $meta;        
+    } 
+    
+    # NOTE:
+    # this will only get called for 
+    # anon-classes, all other calls 
+    # are assumed to occur during 
+    # global destruction and so don't
+    # really need to be handled explicitly
+    sub DESTROY {
+        my $self = shift;
+        return unless $self->name =~ /^$ANON_CLASS_PREFIX/;
+        my ($serial_id) = ($self->name =~ /^$ANON_CLASS_PREFIX(\d+)/);
+        no strict 'refs';     
+        foreach my $key (keys %{$ANON_CLASS_PREFIX . $serial_id}) {
+            delete ${$ANON_CLASS_PREFIX . $serial_id}{$key};
+        }
+        delete ${'main::' . $ANON_CLASS_PREFIX}{$serial_id . '::'};        
     }
     
     sub check_metaclass_compatability {
@@ -107,7 +158,7 @@ sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
                            " is not compatible with the " . 
                            $class_name . "->meta => (" . ($meta->instance_metaclass) . ")";                           
         }        
-    }
+    } 
 }
 
 sub create {
@@ -145,20 +196,6 @@ sub create {
     return $meta;
 }
 
-{
-    # NOTE:
-    # this should be sufficient, if you have a 
-    # use case where it is not, write a test and 
-    # I will change it.
-    my $ANON_CLASS_SERIAL = 0;
-    
-    sub create_anon_class {
-        my ($class, %options) = @_;   
-        my $package_name = 'Class::MOP::Class::__ANON__::SERIAL::' . ++$ANON_CLASS_SERIAL;
-        return $class->create($package_name, '0.00', %options);
-    }
-}
-
 ## Attribute readers
 
 # NOTE:
@@ -300,23 +337,22 @@ sub add_method {
         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))
+            # try to find the next method
+            $method = $self->find_next_method_by_name($method_name);
+            # die if it does not exist
+            (defined $method)
                 || 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')) {
+            # and now make sure to wrap it 
+            # even if it is already wrapped
+            # because we need a new sub ref
             $method = Class::MOP::Method::Wrapped->wrap($method);
-            $self->add_method($method_name => $method); 
-        }       
+        }
+        else {
+            # now make sure we wrap it properly 
+            $method = Class::MOP::Method::Wrapped->wrap($method)
+                unless $method->isa('Class::MOP::Method::Wrapped');  
+        }    
+        $self->add_method($method_name => $method);        
         return $method;
     };
 
@@ -765,11 +801,17 @@ This will create an anonymous class, it works much like C<create> but
 it does not need a C<$package_name>. Instead it will create a suitably 
 unique package name for you to stash things into.
 
-=item B<initialize ($package_name)>
+=item B<initialize ($package_name, %options)>
 
 This initializes and returns returns a B<Class::MOP::Class> object 
 for a given a C<$package_name>.
 
+=item B<reinitialize ($package_name, %options)>
+
+This removes the old metaclass, and creates a new one in it's place.
+Do B<not> use this unless you really know what you are doing, it could 
+very easily make a very large mess of your program. 
+
 =item B<construct_class_instance (%options)>
 
 This will construct an instance of B<Class::MOP::Class>, it is 
@@ -1223,4 +1265,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. 
 
-=cutchistian
+=cut