no-more-blessed-subs
[gitmo/Class-MOP.git] / lib / Class / MOP / Class.pm
index 17f36f3..332f832 100644 (file)
@@ -9,7 +9,7 @@ use Scalar::Util 'blessed', 'reftype', 'weaken';
 use Sub::Name    'subname';
 use B            'svref_2object';
 
-our $VERSION   = '0.18';
+our $VERSION   = '0.19';
 our $AUTHORITY = 'cpan:STEVAN';
 
 use base 'Class::MOP::Module';
@@ -20,15 +20,6 @@ use Class::MOP::Instance;
 
 sub meta { Class::MOP::Class->initialize(blessed($_[0]) || $_[0]) }
 
-# Class globals ...
-
-# NOTE:
-# we need a sufficiently annoying prefix
-# this should suffice for now, this is 
-# used in a couple of places below, so 
-# need to put it up here for now.
-my $ANON_CLASS_PREFIX = 'Class::MOP::Class::__ANON__::SERIAL::';
-
 # Creation
     
 sub initialize {
@@ -86,12 +77,23 @@ sub construct_class_instance {
         $meta = bless { 
             # inherited from Class::MOP::Package
             '$:package'             => $package_name, 
-            '%:namespace'           => \%{$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'             => (exists ${$package_name . '::'}{'VERSION'}   ? ${$package_name . '::VERSION'}   : undef),
-            '$:authority'           => (exists ${$package_name . '::'}{'AUTHORITY'} ? ${$package_name . '::AUTHORITY'} : undef),
-            # defined here ...
-            '%:attributes'          => {},
+            '$:version'             => \undef,
+            '$:authority'           => \undef,
+            # defined in Class::MOP::Class
+            
+            '%: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',
@@ -109,11 +111,12 @@ sub construct_class_instance {
     $meta->check_metaclass_compatability();
     
     Class::MOP::store_metaclass_by_name($package_name, $meta);
+    
     # NOTE:
     # we need to weaken any anon classes
     # so that they can call DESTROY properly
-    Class::MOP::weaken_metaclass($package_name)
-        if $package_name =~ /^$ANON_CLASS_PREFIX/;
+    Class::MOP::weaken_metaclass($package_name) if $meta->is_anon_class;
+    
     $meta;        
 } 
     
@@ -160,42 +163,68 @@ sub check_metaclass_compatability {
     # use case where it is not, write a test and 
     # I will change it.
     my $ANON_CLASS_SERIAL = 0;
+    
+    # NOTE:
+    # we need a sufficiently annoying prefix
+    # this should suffice for now, this is 
+    # used in a couple of places below, so 
+    # need to put it up here for now.
+    my $ANON_CLASS_PREFIX = 'Class::MOP::Class::__ANON__::SERIAL::';    
+
+    sub is_anon_class {
+        my $self = shift;
+        $self->name =~ /^$ANON_CLASS_PREFIX/ ? 1 : 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);
-    }
-}    
+        return $class->create($package_name, %options);
+    } 
 
-# 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};
+    # 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 . '::'};        
     }
-    delete ${'main::' . $ANON_CLASS_PREFIX}{$serial_id . '::'};        
+
 }
 
 # creating classes with MOP ...
 
 sub create {
-    my ($class, $package_name, $package_version, %options) = @_;
+    my $class        = shift;
+    my $package_name = shift;
+    
     (defined $package_name && $package_name)
         || confess "You must pass a package name";
+
+    (scalar @_ % 2 == 0)
+        || confess "You much pass all parameters as name => value pairs " . 
+                   "(I found an uneven number of params in \@_)";
+
+    my (%options) = @_;
+    
     my $code = "package $package_name;";
-    $code .= "\$$package_name\:\:VERSION = '$package_version';" 
-        if defined $package_version;
+    $code .= "\$$package_name\:\:VERSION = '" . $options{version} . "';" 
+        if exists $options{version};
+    $code .= "\$$package_name\:\:AUTHORITY = '" . $options{authority} . "';" 
+        if exists $options{authority};  
+              
     eval $code;
     confess "creation of $package_name failed : $@" if $@;    
+    
     my $meta = $class->initialize($package_name);
     
     $meta->add_method('meta' => sub { 
@@ -233,6 +262,22 @@ sub attribute_metaclass { $_[0]->{'$:attribute_metaclass'} }
 sub method_metaclass    { $_[0]->{'$:method_metaclass'}    }
 sub instance_metaclass  { $_[0]->{'$:instance_metaclass'}  }
 
+sub get_method_map {    
+    my $self = shift;
+    my $map  = $self->{'%:methods'}; 
+    
+    foreach my $symbol (grep { $self->has_package_symbol('&' . $_) } $self->list_all_package_symbols) {
+        next if exists $map->{$symbol} && 
+                $map->{$symbol}->body == $self->get_package_symbol('&' . $symbol);
+        
+        $map->{$symbol} = $self->method_metaclass->wrap(
+            $self->get_package_symbol('&' . $symbol)
+        );
+    }
+
+    return $map;
+}
+
 # Instance Construction & Cloning
 
 sub new_object {
@@ -333,15 +378,31 @@ sub add_method {
     (defined $method_name && $method_name)
         || confess "You must define a method name";
     # use reftype here to allow for blessed subs ...
-    ('CODE' eq (reftype($method) || ''))
-        || confess "Your code block must be a CODE reference";
-    my $full_method_name = ($self->name . '::' . $method_name);    
-
-    # FIXME:
-    # dont bless subs, its bad mkay
-    $method = $self->method_metaclass->wrap($method) unless blessed($method);
     
-    $self->add_package_symbol("&${method_name}" => subname $full_method_name => $method);
+    my $body;
+    
+    if (blessed($method)) {
+     
+        $body = $method->body;     
+     
+        ('CODE' eq (reftype($body) || ''))
+            || confess "Your code block must be a CODE reference";        
+        
+        $self->get_method_map->{$method_name} = $method;
+    }
+    else {
+        
+        $body = $method;
+        
+        ('CODE' eq (reftype($body) || ''))
+            || confess "Your code block must be a CODE reference";        
+        
+        $self->get_method_map->{$method_name} = $self->method_metaclass->wrap($body);        
+        
+    }
+    
+    my $full_method_name = ($self->name . '::' . $method_name);        
+    $self->add_package_symbol("&${method_name}" => subname $full_method_name => $body);
 }
 
 {
@@ -412,20 +473,30 @@ sub alias_method {
     my ($self, $method_name, $method) = @_;
     (defined $method_name && $method_name)
         || confess "You must define a method name";
-    # use reftype here to allow for blessed subs ...
-    ('CODE' eq (reftype($method) || ''))
-        || confess "Your code block must be a CODE reference";
 
-    # FIXME:
-    # dont bless subs, its bad mkay
-    $method = $self->method_metaclass->wrap($method) unless blessed($method);    
-        
-    $self->add_package_symbol("&${method_name}" => $method);
-}
+    my $body;
 
-sub find_method_by_name {
-    my ($self, $method_name) = @_;
-    return $self->name->can($method_name);
+    if (blessed($method)) {
+
+        $body = $method->body;     
+
+        ('CODE' eq (reftype($body) || ''))
+            || confess "Your code block must be a CODE reference";        
+
+        $self->get_method_map->{$method_name} = $method;
+    }
+    else {
+
+        $body = $method;
+
+        ('CODE' eq (reftype($body) || ''))
+            || confess "Your code block must be a CODE reference";        
+
+        $self->get_method_map->{$method_name} = $self->method_metaclass->wrap($body);        
+
+    }
+        
+    $self->add_package_symbol("&${method_name}" => $body);
 }
 
 sub has_method {
@@ -433,14 +504,13 @@ sub has_method {
     (defined $method_name && $method_name)
         || confess "You must define a method name";    
     
-    return 0 if !$self->has_package_symbol("&${method_name}");        
-    my $method = $self->get_package_symbol("&${method_name}");
-    return 0 if (svref_2object($method)->GV->STASH->NAME || '') ne $self->name &&
-                (svref_2object($method)->GV->NAME || '')        ne '__ANON__';      
-
-    # FIXME:
-    # dont bless subs, its bad mkay
-    $self->method_metaclass->wrap($method) unless blessed($method);
+    my $method_map = $self->get_method_map;
+    
+    return 0 unless exists $self->get_method_map->{$method_name};
+        
+    my $method = $method_map->{$method_name};
+    return 0 if ($method->package_name || '') ne $self->name &&
+                ($method->name         || '') ne '__ANON__'; 
     
     return 1;
 }
@@ -449,10 +519,10 @@ sub get_method {
     my ($self, $method_name) = @_;
     (defined $method_name && $method_name)
         || confess "You must define a method name";
-
+     
     return unless $self->has_method($method_name);
  
-    return $self->get_package_symbol("&${method_name}");
+    return $self->get_method_map->{$method_name};
 }
 
 sub remove_method {
@@ -465,12 +535,21 @@ sub remove_method {
     $self->remove_package_symbol("&${method_name}")
         if defined $removed_method;
         
+    delete $self->get_method_map->{$method_name}
+        if exists $self->get_method_map->{$method_name};        
+        
     return $removed_method;
 }
 
 sub get_method_list {
     my $self = shift;
-    grep { $self->has_method($_) } $self->list_all_package_symbols;
+    return grep { $self->has_method($_) } keys %{$self->get_method_map};
+}
+
+sub find_method_by_name {
+    my ($self, $method_name) = @_;
+    # FIXME
+    return $self->name->can($method_name);
 }
 
 sub compute_all_applicable_methods {
@@ -671,7 +750,8 @@ Class::MOP::Class - Class Meta Object
   
   # or use this to actually create classes ...
   
-  Class::MOP::Class->create('Bar' => '0.01' => (
+  Class::MOP::Class->create('Bar' => (
+      version      => '0.01',
       superclasses => [ 'Foo' ],
       attributes => [
           Class::MOP:::Attribute->new('$bar'),
@@ -708,21 +788,6 @@ 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
@@ -737,15 +802,17 @@ created any more than nessecary. Basically, they are singletons.
 
 =over 4
 
-=item B<create ($package_name, ?$package_version,
+=item B<create ($package_name, 
+                version      =E<gt> ?$version,                 
+                authority    =E<gt> ?$authority,                                 
                 superclasses =E<gt> ?@superclasses, 
                 methods      =E<gt> ?%methods, 
                 attributes   =E<gt> ?%attributes)>
 
 This returns a B<Class::MOP::Class> object, bringing the specified 
-C<$package_name> into existence and adding any of the 
-C<$package_version>, C<@superclasses>, C<%methods> and C<%attributes> 
-to it.
+C<$package_name> into existence and adding any of the C<$version>, 
+C<$authority>, C<@superclasses>, C<%methods> and C<%attributes> to 
+it.
 
 =item B<create_anon_class (superclasses =E<gt> ?@superclasses, 
                            methods      =E<gt> ?%methods, 
@@ -860,17 +927,15 @@ is too I<context-specific> to be part of the MOP.
 
 =head2 Informational 
 
-=over 4
+These are a few predicate methods for asking information about the class.
 
-=item B<name>
+=over 4
 
-This is a read-only attribute which returns the package name for the 
-given B<Class::MOP::Class> instance.
+=item B<is_anon_class>
 
-=item B<version>
+=item B<is_mutable>
 
-This is a read-only attribute which returns the C<$VERSION> of the 
-package for the given B<Class::MOP::Class> instance.
+=item B<is_immutable>
 
 =back
 
@@ -903,6 +968,8 @@ what B<Class::ISA::super_path> does, but we don't remove duplicate names.
 
 =over 4
 
+=item B<get_method_map>
+
 =item B<method_metaclass>
 
 =item B<add_method ($method_name, $method)>
@@ -1180,47 +1247,10 @@ It will return undef if nothing is found.
 
 =back
 
-=head2 Package Variables
-
-Since Perl's classes are built atop the Perl package system, it is 
-fairly common to use package scoped variables for things like static 
-class variables. The following methods are convience methods for 
-the creation and inspection of package scoped variables.
-
-=over 4
-
-=item B<add_package_symbol ($variable_name, ?$initial_value)>
-
-Given a C<$variable_name>, which must contain a leading sigil, this 
-method will create that variable within the package which houses the 
-class. It also takes an optional C<$initial_value>, which must be a 
-reference of the same type as the sigil of the C<$variable_name> 
-implies.
-
-=item B<get_package_symbol ($variable_name)>
-
-This will return a reference to the package variable in 
-C<$variable_name>. 
-
-=item B<has_package_symbol ($variable_name)>
-
-Returns true (C<1>) if there is a package variable defined for 
-C<$variable_name>, and false (C<0>) otherwise.
-
-=item B<remove_package_symbol ($variable_name)>
-
-This will attempt to remove the package variable at C<$variable_name>.
-
-=back
-
 =head2 Class closing
 
 =over 4
 
-=item B<is_mutable>
-
-=item B<is_immutable>
-
 =item B<make_immutable>
 
 =back