bump version and update changes
[gitmo/Class-MOP.git] / lib / Class / MOP / Class.pm
index 07a4d41..b197e5e 100644 (file)
@@ -11,7 +11,8 @@ use Class::MOP::Method::Wrapped;
 use Carp         'confess';
 use Scalar::Util 'blessed', 'weaken';
 
-our $VERSION   = '0.65';
+our $VERSION   = '0.74';
+$VERSION = eval $VERSION;
 our $AUTHORITY = 'cpan:STEVAN';
 
 use base 'Class::MOP::Module';
@@ -84,7 +85,7 @@ sub construct_class_instance {
     }
 
     # and check the metaclass compatibility
-    $meta->check_metaclass_compatability();  
+    $meta->check_metaclass_compatibility();  
 
     Class::MOP::store_metaclass_by_name($package_name, $meta);
 
@@ -139,7 +140,7 @@ sub update_package_cache_flag {
     $self->{'_package_cache_flag'} = Class::MOP::check_package_cache_flag($self->name);    
 }
 
-sub check_metaclass_compatability {
+sub check_metaclass_compatibility {
     my $self = shift;
 
     # this is always okay ...
@@ -166,14 +167,20 @@ sub check_metaclass_compatability {
                        $class_name . "->meta => (" . ($meta_type)     . ")";
         # NOTE:
         # we also need to check that instance metaclasses
-        # are compatabile in the same the class.
+        # are compatibile in the same the class.
         ($self->instance_metaclass->isa($meta->instance_metaclass))
-            || confess $self->name . "->meta => (" . ($self->instance_metaclass) . ")" .
+            || confess $self->name . "->meta->instance_metaclass => (" . ($self->instance_metaclass) . ")" .
                        " is not compatible with the " .
-                       $class_name . "->meta => (" . ($meta->instance_metaclass) . ")";
+                       $class_name . "->meta->instance_metaclass => (" . ($meta->instance_metaclass) . ")";
     }
 }
 
+# backwards compat for stevan's inability to spell ;)
+sub check_metaclass_compatability {
+    my $self = shift;
+    $self->check_metaclass_compatibility(@_);
+}
+
 ## ANON classes
 
 {
@@ -211,10 +218,18 @@ sub check_metaclass_compatability {
     sub DESTROY {
         my $self = shift;
 
-        return if Class::MOP::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/;
+        # Moose does a weird thing where it replaces the metaclass for
+        # class when fixing metaclass incompatibility. In that case,
+        # we don't want to clean out the namespace now. We can detect
+        # that because Moose will explicitly update the singleton
+        # cache in Class::MOP.
+        my $current_meta = Class::MOP::get_metaclass_by_name($self->name);
+        return if $current_meta ne $self;
+
         my ($serial_id) = ($self->name =~ /^$ANON_CLASS_PREFIX(\d+)/);
         no strict 'refs';
         foreach my $key (keys %{$ANON_CLASS_PREFIX . $serial_id}) {
@@ -235,9 +250,6 @@ sub create {
     my (%options) = @args;
     my $package_name = $options{package};
 
-    (defined $package_name && $package_name)
-        || confess "You must pass a package name";
-    
     (ref $options{superclasses} eq 'ARRAY')
         || confess "You must pass an ARRAY ref of superclasses"
             if exists $options{superclasses};
@@ -247,19 +259,21 @@ sub create {
             if exists $options{attributes};      
             
     (ref $options{methods} eq 'HASH')
-        || confess "You must pass an HASH ref of methods"
+        || confess "You must pass a HASH ref of methods"
             if exists $options{methods};                  
 
-    my $code = "package $package_name;";
-    $code .= "\$$package_name\:\:VERSION = '" . $options{version} . "';"
-        if exists $options{version};
-    $code .= "\$$package_name\:\:AUTHORITY = '" . $options{authority} . "';"
-        if exists $options{authority};
+    $class->SUPER::create(%options);
 
-    eval $code;
-    confess "creation of $package_name failed : $@" if $@;
-
-    my $meta = $class->initialize($package_name);
+    my (%initialize_options) = @args;
+    delete @initialize_options{qw(
+        package
+        superclasses
+        attributes
+        methods
+        version
+        authority
+    )};
+    my $meta = $class->initialize( $package_name => %initialize_options );
 
     # FIXME totally lame
     $meta->add_method('meta' => sub {
@@ -297,12 +311,12 @@ sub attribute_metaclass { $_[0]->{'attribute_metaclass'} }
 sub method_metaclass    { $_[0]->{'method_metaclass'}    }
 sub instance_metaclass  { $_[0]->{'instance_metaclass'}  }
 
-# FIXME:
-# this is a prime canidate for conversion to XS
 sub get_method_map {
     my $self = shift;
-    
-    my $current = Class::MOP::check_package_cache_flag($self->name);
+
+    my $class_name = $self->name;
+
+    my $current = Class::MOP::check_package_cache_flag($class_name);
 
     if (defined $self->{'_package_cache_flag'} && $self->{'_package_cache_flag'} == $current) {
         return $self->{'methods'} ||= {};
@@ -310,15 +324,14 @@ sub get_method_map {
 
     $self->{_package_cache_flag} = $current;
 
-    my $map  = $self->{'methods'} ||= {};
+    my $map = $self->{'methods'} ||= {};
 
-    my $class_name       = $self->name;
     my $method_metaclass = $self->method_metaclass;
 
-    my %all_code = $self->get_all_package_symbols('CODE');
+    my $all_code = $self->get_all_package_symbols('CODE');
 
-    foreach my $symbol (keys %all_code) {
-        my $code = $all_code{$symbol};
+    foreach my $symbol (keys %{ $all_code }) {
+        my $code = $all_code->{$symbol};
 
         next if exists  $map->{$symbol} &&
                 defined $map->{$symbol} &&
@@ -485,13 +498,22 @@ sub superclasses {
     if (@_) {
         my @supers = @_;
         @{$self->get_package_symbol($var_spec)} = @supers;
+
+        # NOTE:
+        # on 5.8 and below, we need to call
+        # a method to get Perl to detect
+        # a cycle in the class hierarchy
+        my $class = $self->name;
+        $class->isa($class);
+
         # NOTE:
         # we need to check the metaclass
         # compatibility 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->check_metaclass_compatibility();
         $self->update_meta_instance_dependencies();
     }
     @{$self->get_package_symbol($var_spec)};
@@ -590,15 +612,13 @@ sub class_precedence_list {
 sub wrap_method_body {
     my ( $self, %args ) = @_;
 
-    my $body = delete $args{body}; # delete is for compat
-
-    ('CODE' eq ref($body))
+    ('CODE' eq ref $args{body})
         || confess "Your code block must be a CODE reference";
 
-    $self->method_metaclass->wrap( $body => (
+    $self->method_metaclass->wrap(
         package_name => $self->name,
         %args,
-    ));
+    );
 }
 
 sub add_method {
@@ -609,11 +629,7 @@ sub add_method {
     my $body;
     if (blessed($method)) {
         $body = $method->body;
-        if ($method->package_name ne $self->name && 
-            $method->name         ne $method_name) {
-            warn "Hello there, got something for you." 
-                . " Method says " . $method->package_name . " " . $method->name
-                . " Class says " . $self->name . " " . $method_name;
+        if ($method->package_name ne $self->name) {
             $method = $method->clone(
                 package_name => $self->name,
                 name         => $method_name            
@@ -627,15 +643,16 @@ sub add_method {
 
     $method->attach_to_class($self);
 
-    $self->get_method_map->{$method_name} = $method;
+    # This used to call get_method_map, which meant we would build all
+    # the method objects for the class just because we added one
+    # method. This is hackier, but quicker too.
+    $self->{methods}{$method_name} = $method;
     
     my $full_method_name = ($self->name . '::' . $method_name);    
     $self->add_package_symbol(
         { sigil => '&', type => 'CODE', name => $method_name }, 
         Class::MOP::subname($full_method_name => $body)
     );
-
-    $self->update_package_cache_flag; # still valid, since we just added the method to the map, and if it was invalid before that then get_method_map updated it
 }
 
 {
@@ -709,17 +726,9 @@ sub add_method {
 }
 
 sub alias_method {
-    my ($self, $method_name, $method) = @_;
-    (defined $method_name && $method_name)
-        || confess "You must define a method name";
-
-    my $body = (blessed($method) ? $method->body : $method);
-    ('CODE' eq ref($body))
-        || confess "Your code block must be a CODE reference";
+    my $self = shift;
 
-    $self->add_package_symbol(
-        { sigil => '&', type => 'CODE', name => $method_name } => $body
-    );
+    $self->add_method(@_);
 }
 
 sub has_method {
@@ -727,7 +736,7 @@ sub has_method {
     (defined $method_name && $method_name)
         || confess "You must define a method name";
 
-    exists $self->get_method_map->{$method_name};
+    exists $self->{methods}{$method_name} || exists $self->get_method_map->{$method_name};
 }
 
 sub get_method {
@@ -741,7 +750,7 @@ sub get_method {
     # will just return undef for me now
     # return unless $self->has_method($method_name);
 
-    return $self->get_method_map->{$method_name};
+    return $self->{methods}{$method_name} || $self->get_method_map->{$method_name};
 }
 
 sub remove_method {
@@ -1112,7 +1121,12 @@ sub create_immutable_transformer {
                 my $original = shift;
                 confess "Cannot add package symbols to an immutable metaclass" 
                     unless (caller(2))[3] eq 'Class::MOP::Package::get_package_symbol'; 
-                goto $original->body;
+
+                # This is a workaround for a bug in 5.8.1 which thinks that
+                # goto $original->body
+                # is trying to go to a label
+                my $body = $original->body;
+                goto $body;
             },
         },
     });
@@ -1169,6 +1183,10 @@ manipulation of Perl 5 classes (and it can create them too). The
 best way to understand what this module can do, is to read the
 documentation for each of it's methods.
 
+=head1 INHERITANCE
+
+B<Class::MOP::Class> is a subclass of L<Class::MOP::Module>
+
 =head1 METHODS
 
 =head2 Self Introspection
@@ -1243,7 +1261,7 @@ to use C<construct_instance> once all the bootstrapping is done. This
 method is used internally by C<initialize> and should never be called
 from outside of that method really.
 
-=item B<check_metaclass_compatability>
+=item B<check_metaclass_compatibility>
 
 This method is called as the very last thing in the
 C<construct_class_instance> method. This will check that the
@@ -1337,7 +1355,7 @@ This method is used to construct an instance structure suitable for
 C<bless>-ing into your package of choice. It works in conjunction
 with the Attribute protocol to collect all applicable attributes.
 
-This will construct and instance using a HASH ref as storage
+This will construct an instance using a HASH ref as storage
 (currently only HASH references are supported). This will collect all
 the applicable attributes and layout out the fields in the HASH ref,
 it will then initialize them using either use the corresponding key
@@ -1453,10 +1471,20 @@ for more information on the method metaclasses.
 
 Wrap a code ref (C<$attrs{body>) with C<method_metaclass>.
 
-=item B<add_method ($method_name, $method, %attrs)>
+=item B<add_method ($method_name, $method)>
+
+This will take a C<$method_name> and CODE reference or meta method
+objectand install it into the class's package.
+
+You are strongly encouraged to pass a meta method object instead of a
+code reference. If you do so, that object gets stored as part of the
+class's method map, providing more useful information about the method
+for introspection.
 
-This will take a C<$method_name> and CODE reference to that
-C<$method> and install it into the class's package.
+When you provide a method object, this method will clone that object
+if the object's package name does not match the class name. This lets
+us track the original source of any methods added from other classes
+(notably Moose roles).
 
 B<NOTE>:
 This does absolutely nothing special to C<$method>
@@ -1464,16 +1492,6 @@ other than use B<Sub::Name> to make sure it is tagged with the
 correct name, and therefore show up correctly in stack traces and
 such.
 
-=item B<alias_method ($method_name, $method)>
-
-This will take a C<$method_name> and CODE reference to that
-C<$method> and alias the method into the class's package.
-
-B<NOTE>:
-Unlike C<add_method>, this will B<not> try to name the
-C<$method> using B<Sub::Name>, it only aliases the method in
-the class's package.
-
 =item B<has_method ($method_name)>
 
 This just provides a simple way to check if the class implements
@@ -1561,6 +1579,11 @@ This will return the first method to match a given C<$method_name> in
 the superclasses, this is basically equivalent to calling
 C<SUPER::$method_name>, but it can be dispatched at runtime.
 
+=item B<alias_method ($method_name, $method)>
+
+B<NOTE>: This method is now deprecated. Just use C<add_method>
+instead.
+
 =back
 
 =head2 Method Modifiers
@@ -1597,8 +1620,10 @@ the call tree might looks something like this:
     around 2
      around 1
       primary
-     after 1
-    after 2
+     around 1
+    around 2
+   after 1
+  after 2
 
 To see examples of using method modifiers, see the following examples
 included in the distribution; F<InstanceCountingClass>, F<Perl6Attribute>,