optimized
[gitmo/Class-MOP.git] / lib / Class / MOP / Class.pm
index 474783b..1dd30eb 100644 (file)
@@ -9,7 +9,7 @@ use Scalar::Util 'blessed', 'reftype';
 use Sub::Name    'subname';
 use B            'svref_2object';
 
-our $VERSION = '0.06';
+our $VERSION = '0.12';
 
 # Self-introspection 
 
@@ -17,12 +17,18 @@ 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.
     # After all, do package definitions even get reaped?
-    my %METAS;    
+    my %METAS;  
+    
+    # means of accessing all the metaclasses that have 
+    # been initialized thus far (for mugwumps obj browser)
+    sub get_all_metaclasses         {        %METAS }            
+    sub get_all_metaclass_instances { values %METAS } 
+    sub get_all_metaclass_names     { keys   %METAS }     
     
     sub initialize {
         my $class        = shift;
@@ -44,7 +50,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";  
-        return $METAS{$package_name} if exists $METAS{$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)
+        return $METAS{$package_name} 
+                       if exists $METAS{$package_name} && defined $METAS{$package_name};  
         $class = blessed($class) || $class;
         # now create the metaclass
         my $meta;
@@ -85,7 +97,7 @@ sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
                            $class_name . "->meta => (" . (blessed($meta)) . ")";
         }        
     }
-}
+#}
 
 sub create {
     my ($class, $package_name, $package_version, %options) = @_;
@@ -122,6 +134,20 @@ 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:
@@ -151,14 +177,7 @@ sub construct_instance {
     my ($class, %params) = @_;
     my $instance = {};
     foreach my $attr ($class->compute_all_applicable_attributes()) {
-        my $init_arg = $attr->init_arg();
-        # try to fetch the init arg from the %params ...
-        my $val;        
-        $val = $params{$init_arg} if exists $params{$init_arg};
-        # if nothing was in the %params, we can use the 
-        # attribute's default value (if it has one)
-        $val ||= $attr->default($instance) if $attr->has_default();            
-        $instance->{$attr->name} = $val;
+        $attr->initialize_instance_slot($class, $instance, \%params);
     }
     return $instance;
 }
@@ -198,11 +217,12 @@ sub version {
 
 sub superclasses {
     my $self = shift;
+    no strict 'refs';
     if (@_) {
         my @supers = @_;
-        @{$self->get_package_variable('@ISA')} = @supers;
+        @{$self->name . '::ISA'} = @supers;
     }
-    @{$self->get_package_variable('@ISA')};        
+    @{$self->name . '::ISA'};
 }
 
 sub class_precedence_list {
@@ -213,11 +233,11 @@ sub class_precedence_list {
     # up otherwise. Yes, it's an ugly hack, better 
     # suggestions are welcome.
     { $self->name->isa('This is a test for circular inheritance') }
-    # ... and no back to our regularly scheduled program
+    # ... and now back to our regularly scheduled program
     (
         $self->name, 
         map { 
-            $self->initialize($_)->class_precedence_list()
+            ($METAS{$_} || $self->initialize($_))->class_precedence_list()
         } $self->superclasses()
     );   
 }
@@ -270,30 +290,39 @@ sub add_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 $full_method_modifier_name = ($self->name . '::' . $method_name . ':before');    
+               || confess "You must pass in a method name";    
                my $method = $fetch_and_prepare_method->($self, $method_name);
-               $method->add_before_modifier(subname $full_method_modifier_name => $method_modifier);
+               $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 $full_method_modifier_name = ($self->name . '::' . $method_name . ':after');     
+               || confess "You must pass in a method name";    
                my $method = $fetch_and_prepare_method->($self, $method_name);
-               $method->add_after_modifier(subname $full_method_modifier_name => $method_modifier);
+               $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 $full_method_modifier_name = ($self->name . '::' . $method_name . ':around');    
                my $method = $fetch_and_prepare_method->($self, $method_name);
-               $method->add_around_modifier(subname $full_method_modifier_name => $method_modifier);
+               $method->add_around_modifier(subname ':around' => $method_modifier);
        }       
 
+    # NOTE: 
+    # the methods above used to be named like this:
+    #    ${pkg}::${method}:(before|after|around)
+    # but this proved problematic when using one modifier
+    # to wrap multiple methods (something which is likely
+    # to happen pretty regularly IMO). So instead of naming
+    # it like this, I have chosen to just name them purely 
+    # with their modifier names, like so:
+    #    :(before|after|around)
+    # The fact is that in a stack trace, it will be fairly 
+    # evident from the context what method they are attached
+    # to, and so don't need the fully qualified name.
 }
 
 sub alias_method {
@@ -460,8 +489,12 @@ sub get_attribute {
     my ($self, $attribute_name) = @_;
     (defined $attribute_name && $attribute_name)
         || confess "You must define an attribute name";
-    return $self->get_attribute_map->{$attribute_name} 
-        if $self->has_attribute($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; 
 } 
 
@@ -479,7 +512,12 @@ sub remove_attribute {
 
 sub get_attribute_list {
     my $self = shift;
-    keys %{$self->get_attribute_map};
+    # 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'}};
 } 
 
 sub compute_all_applicable_attributes {
@@ -494,7 +532,7 @@ sub compute_all_applicable_attributes {
         next if $seen_class{$class};
         $seen_class{$class}++;
         # fetch the meta-class ...
-        my $meta = $self->initialize($class);
+        my $meta = ($METAS{$class} || $self->initialize($class));
         foreach my $attr_name ($meta->get_attribute_list()) { 
             next if exists $seen_attr{$attr_name};
             $seen_attr{$attr_name}++;
@@ -504,6 +542,24 @@ sub compute_all_applicable_attributes {
     return @attrs;    
 }
 
+sub find_attribute_by_name {
+    my ($self, $attr_name) = @_;
+    # 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;
+    foreach my $class ($self->class_precedence_list()) {
+        next if $seen_class{$class};
+        $seen_class{$class}++;
+        # fetch the meta-class ...
+        my $meta = $self->initialize($class);
+        return $meta->get_attribute($attr_name)
+            if $meta->has_attribute($attr_name);
+    }
+    return;
+}
+
 # Class attributes
 
 sub add_package_variable {
@@ -517,8 +573,17 @@ sub add_package_variable {
         *{$self->name . '::' . $name} = $initial_value;
     }
     else {
-        eval $sigil . $self->name . '::' . $name;
-        confess "Could not create package variable ($variable) because : $@" if $@;
+        my $e;
+        {        
+            # NOTE:
+            # We HAVE to localize $@ or all 
+            # hell breaks loose. It is not 
+            # good, believe me, not good.
+            local $@;
+            eval $sigil . $self->name . '::' . $name;
+            $e = $@ if $@;            
+        }
+        confess "Could not create package variable ($variable) because : $e" if $e;
     }
 }
 
@@ -536,10 +601,17 @@ sub get_package_variable {
     (defined $variable && $variable =~ /^[\$\@\%]/)
         || confess "variable name does not have a sigil";
     my ($sigil, $name) = ($variable =~ /^(.)(.*)$/); 
-    no strict 'refs';
-    # try to fetch it first,.. see what happens
-    my $ref = eval '\\' . $sigil . $self->name . '::' . $name;
-    confess "Could not get the package variable ($variable) because : $@" if $@;    
+    my ($ref, $e);
+    {
+        # NOTE:
+        # We HAVE to localize $@ or all 
+        # hell breaks loose. It is not 
+        # good, believe me, not good.
+        local $@;        
+        $ref = eval '\\' . $sigil . $self->name . '::' . $name;
+        $e = $@ if $@;
+    }
+    confess "Could not get the package variable ($variable) because : $e" if $e;    
     # if we didn't die, then we can return it
        return $ref;
 }
@@ -565,6 +637,9 @@ Class::MOP::Class - Class Meta Object
 
 =head1 SYNOPSIS
 
+  # assuming that class Foo 
+  # has been defined, you can
+  
   # use this for introspection ...
   
   # add a method to Foo ...
@@ -616,6 +691,21 @@ bootstrap this module by installing a number of attribute meta-objects
 into it's metaclass. This will allow this class to reap all the benifits 
 of the MOP when subclassing it. 
 
+=item B<get_all_metaclasses>
+
+This will return an hash of all the metaclass instances that have 
+been cached by B<Class::MOP::Class> keyed by the package name. 
+
+=item B<get_all_metaclass_instances>
+
+This will return an array of all the metaclass instances that have 
+been cached by B<Class::MOP::Class>.
+
+=item B<get_all_metaclass_names>
+
+This will return an array of all the metaclass names that have 
+been cached by B<Class::MOP::Class>.
+
 =back
 
 =head2 Class construction
@@ -640,6 +730,14 @@ C<$package_name> into existence and adding any of the
 C<$package_version>, C<@superclasses>, C<%methods> and C<%attributes> 
 to it.
 
+=item B<create_anon_class (superclasses =E<gt> ?@superclasses, 
+                           methods      =E<gt> ?%methods, 
+                           attributes   =E<gt> ?%attributes)>
+
+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)>
 
 This initializes and returns returns a B<Class::MOP::Class> object 
@@ -1040,6 +1138,12 @@ HASH reference like C<compute_all_applicable_methods> because all
 that same information is discoverable through the attribute 
 meta-object itself.
 
+=item B<find_attribute_by_name ($attr_name)>
+
+This method will traverse the inheritance heirachy and find the 
+first attribute whose name matches C<$attr_name>, then return it. 
+It will return undef if nothing is found.
+
 =back
 
 =head2 Package Variables