is_pristine
[gitmo/Class-MOP.git] / lib / Class / MOP / Class.pm
index da66d76..d409156 100644 (file)
@@ -37,15 +37,6 @@ sub initialize {
         || $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";
-    Class::MOP::remove_metaclass_by_name($package_name);
-    $class->construct_class_instance('package' => $package_name, @_);
-}
-
 # NOTE: (meta-circularity)
 # this is a special form of &construct_instance
 # (see below), which is used to construct class
@@ -54,8 +45,8 @@ sub reinitialize {
 # normal &construct_instance.
 sub construct_class_instance {
     my $class        = shift;
-    my %options      = @_;
-    my $package_name = $options{'package'};
+    my $options      = @_ == 1 ? $_[0] : {@_};
+    my $package_name = $options->{package};
     (defined $package_name && $package_name)
         || confess "You must pass a package name";
     # NOTE:
@@ -72,57 +63,24 @@ sub construct_class_instance {
     # we need to deal with the possibility
     # of class immutability here, and then
     # get the name of the class appropriately
-    $class = (blessed($class)
+    $class = (ref($class)
                     ? ($class->is_immutable
                         ? $class->get_mutable_metaclass_name()
-                        : blessed($class))
+                        : ref($class))
                     : $class);
 
     # now create the metaclass
     my $meta;
     if ($class eq 'Class::MOP::Class') {
         no strict 'refs';
-        $meta = bless {
-            # inherited from Class::MOP::Package
-            'package'             => $package_name,
-
-            # NOTE:
-            # since the following attributes will
-            # actually be loaded from the symbol
-            # table, and actually bypass the instance
-            # entirely, we can just leave these things
-            # listed here for reference, because they
-            # should not actually have a value associated
-            # with the slot.
-            'namespace'           => \undef,
-            # inherited from Class::MOP::Module
-            'version'             => \undef,
-            'authority'           => \undef,
-            # defined in Class::MOP::Class
-            'superclasses'        => \undef,
-
-            'methods'             => {},
-            'attributes'          => {},
-            'attribute_metaclass' => $options{'attribute_metaclass'} || 'Class::MOP::Attribute',
-            'method_metaclass'    => $options{'method_metaclass'}    || 'Class::MOP::Method',
-            'instance_metaclass'  => $options{'instance_metaclass'}  || 'Class::MOP::Instance',
-            
-            ## uber-private variables
-            # NOTE:
-            # this starts out as undef so that 
-            # we can tell the first time the 
-            # methods are fetched
-            # - SL
-            '_package_cache_flag'       => undef,  
-            '_meta_instance'            => undef,          
-        } => $class;
+        $meta = $class->_new($options)
     }
     else {
         # NOTE:
         # it is safe to use meta here because
         # class will always be a subclass of
         # Class::MOP::Class, which defines meta
-        $meta = $class->meta->construct_instance(%options)
+        $meta = $class->meta->construct_instance($options)
     }
 
     # and check the metaclass compatibility
@@ -138,6 +96,37 @@ sub construct_class_instance {
     $meta;
 }
 
+sub _new {
+    my $class = shift;
+    my $options = @_ == 1 ? $_[0] : {@_};
+
+    bless {
+        # inherited from Class::MOP::Package
+        'package'             => $options->{package},
+
+        # NOTE:
+        # since the following attributes will
+        # actually be loaded from the symbol
+        # table, and actually bypass the instance
+        # entirely, we can just leave these things
+        # listed here for reference, because they
+        # should not actually have a value associated
+        # with the slot.
+        'namespace'           => \undef,
+        # inherited from Class::MOP::Module
+        'version'             => \undef,
+        'authority'           => \undef,
+        # defined in Class::MOP::Class
+        'superclasses'        => \undef,
+
+        'methods'             => {},
+        'attributes'          => {},
+        'attribute_metaclass' => $options->{'attribute_metaclass'} || 'Class::MOP::Attribute',
+        'method_metaclass'    => $options->{'method_metaclass'}    || 'Class::MOP::Method',
+        'instance_metaclass'  => $options->{'instance_metaclass'}  || 'Class::MOP::Instance',
+    }, $class;
+}
+
 sub reset_package_cache_flag  { (shift)->{'_package_cache_flag'} = undef } 
 sub update_package_cache_flag {
     my $self = shift;
@@ -154,7 +143,7 @@ sub check_metaclass_compatability {
     my $self = shift;
 
     # this is always okay ...
-    return if blessed($self)            eq 'Class::MOP::Class'   &&
+    return if ref($self)                eq 'Class::MOP::Class'   &&
               $self->instance_metaclass eq 'Class::MOP::Instance';
 
     my @class_list = $self->linearized_isa;
@@ -169,10 +158,10 @@ sub check_metaclass_compatability {
         # get the name of the class appropriately
         my $meta_type = ($meta->is_immutable
                             ? $meta->get_mutable_metaclass_name()
-                            : blessed($meta));
+                            : ref($meta));
 
         ($self->isa($meta_type))
-            || confess $self->name . "->meta => (" . (blessed($self)) . ")" .
+            || confess $self->name . "->meta => (" . (ref($self)) . ")" .
                        " is not compatible with the " .
                        $class_name . "->meta => (" . ($meta_type)     . ")";
         # NOTE:
@@ -204,7 +193,7 @@ sub check_metaclass_compatability {
     sub is_anon_class {
         my $self = shift;
         no warnings 'uninitialized';
-        $self->name =~ /^$ANON_CLASS_PREFIX/ ? 1 : 0;
+        $self->name =~ /^$ANON_CLASS_PREFIX/;
     }
 
     sub create_anon_class {
@@ -213,15 +202,6 @@ sub check_metaclass_compatability {
         return $class->create($package_name, %options);
     }
 
-    BEGIN {
-        local $@;
-        eval {
-            require Devel::GlobalDestruction;
-            Devel::GlobalDestruction->import("in_global_destruction");
-            1;
-        } or *in_global_destruction = sub () { '' };
-    }
-
     # NOTE:
     # this will only get called for
     # anon-classes, all other calls
@@ -231,7 +211,7 @@ sub check_metaclass_compatability {
     sub DESTROY {
         my $self = shift;
 
-        return if in_global_destruction; # it'll happen soon anyway and this just makes things more complicated
+        return if Class::MOP::in_global_destruction; # it'll happen soon anyway and this just makes things more complicated
 
         no warnings 'uninitialized';
         return unless $self->name =~ /^$ANON_CLASS_PREFIX/;
@@ -283,7 +263,7 @@ sub create {
 
     # FIXME totally lame
     $meta->add_method('meta' => sub {
-        $class->initialize(blessed($_[0]) || $_[0]);
+        $class->initialize(ref($_[0]) || $_[0]);
     });
 
     $meta->superclasses(@{$options{superclasses}})
@@ -325,12 +305,12 @@ sub get_method_map {
     my $current = Class::MOP::check_package_cache_flag($self->name);
 
     if (defined $self->{'_package_cache_flag'} && $self->{'_package_cache_flag'} == $current) {
-        return $self->{'methods'};
+        return $self->{'methods'} ||= {};
     }
 
     $self->{_package_cache_flag} = $current;
 
-    my $map  = $self->{'methods'};
+    my $map  = $self->{'methods'} ||= {};
 
     my $class_name       = $self->name;
     my $method_metaclass = $self->method_metaclass;
@@ -385,11 +365,12 @@ sub new_object {
 }
 
 sub construct_instance {
-    my ($class, %params) = @_;
+    my $class = shift;
+    my $params = @_ == 1 ? $_[0] : {@_};
     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($meta_instance, $instance, \%params);
+        $attr->initialize_instance_slot($meta_instance, $instance, $params);
     }
     # NOTE:
     # this will only work for a HASH instance type
@@ -430,7 +411,7 @@ sub clone_object {
     my $class    = shift;
     my $instance = shift;
     (blessed($instance) && $instance->isa($class->name))
-        || confess "You must pass an instance of the metaclass (" . $class->name . "), not ($instance)";
+        || confess "You must pass an instance of the metaclass (" . (ref $class ? $class->name : $class) . "), not ($instance)";
 
     # NOTE:
     # we need to protect the integrity of the
@@ -466,7 +447,7 @@ sub rebless_instance {
         $old_metaclass = $instance->meta;
     }
     else {
-        $old_metaclass = $self->initialize(blessed($instance));
+        $old_metaclass = $self->initialize(ref($instance));
     }
 
     my $meta_instance = $self->get_meta_instance();
@@ -946,7 +927,7 @@ sub has_attribute {
     my ($self, $attribute_name) = @_;
     (defined $attribute_name && $attribute_name)
         || confess "You must define an attribute name";
-    exists $self->get_attribute_map->{$attribute_name} ? 1 : 0;
+    exists $self->get_attribute_map->{$attribute_name};
 }
 
 sub get_attribute {
@@ -999,6 +980,25 @@ sub find_attribute_by_name {
     return;
 }
 
+# check if we can reinitialize
+sub is_pristine {
+    my $self = shift;
+
+    # if any local attr is defined
+    return if $self->get_attribute_list;
+
+    # or any non-declared methods
+    if ( my @methods = values %{ $self->get_method_map } ) {
+        my $metaclass = $self->method_metaclass;
+        foreach my $method ( @methods ) {
+            return if $method->isa("Class::MOP::Method::Generated");
+            # FIXME do we need to enforce this too? return unless $method->isa($metaclass);
+        }
+    }
+
+    return 1;
+}
+
 ## Class closing
 
 sub is_mutable   { 1 }
@@ -1040,7 +1040,7 @@ sub is_immutable { 0 }
     sub get_immutable_transformer {
         my $self = shift;
         if( $self->is_mutable ){
-            my $class = blessed $self || $self;
+            my $class = ref $self || $self;
             return $IMMUTABLE_TRANSFORMERS{$class} ||= $self->create_immutable_transformer;
         }
         confess "unable to find transformer for immutable class"
@@ -1227,12 +1227,6 @@ as we use a special reserved slot (C<__MOP__>) to store this.
 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
@@ -1400,6 +1394,11 @@ This returns true if the class is still mutable.
 
 This returns true if the class has been made immutable.
 
+=item B<is_pristine>
+
+Checks whether the class has any data that will be lost if C<reinitialize> is
+called.
+
 =back
 
 =head2 Inheritance Relationships