Applying Patch from Robert Norris again
Stevan Little [Fri, 14 Jul 2006 02:27:43 +0000 (02:27 +0000)]
lib/Class/C3.pm
t/31_next_method_skip.t

index a2f4740..988a8e9 100644 (file)
@@ -158,8 +158,10 @@ our $VERSION = '0.05';
 
 our %METHOD_CACHE;
 
-sub _find {
-    my $level = 2;
+sub method {
+    my $indirect = caller() =~ /^(?:next|maybe::next)$/;
+    my $level = $indirect ? 2 : 1;
+     
     my ($method_caller, $label, @label);
     while ($method_caller = (caller($level++))[3]) {
       @label = (split '::', $method_caller);
@@ -172,7 +174,7 @@ sub _find {
     my $self     = $_[0];
     my $class    = blessed($self) || $self;
     
-    return $METHOD_CACHE{"$class|$caller|$label"} ||= do {
+    my $method = $METHOD_CACHE{"$class|$caller|$label"} ||= do {
         
         my @MRO = Class::C3::calculateMRO($class);
         
@@ -189,15 +191,27 @@ sub _find {
             last if (defined ($found = *{$class . '::' . $label}{CODE}));
         }
         
-        die "No next::method '$label' found for $_[0]" if $_[1] && !$found;
-        
         $found;
     };
+
+    return $method if $indirect;
+
+    die "No next::method '$label' found for $self" if !$method;
+
+    goto &{$method};
 }
 
-sub method { goto &{_find($_[0], 1)} }
+sub can { method($_[0]) }
 
-sub can { return _find($_[0], 0) ? 1 : 0 }
+package  # hide me from PAUSE
+    maybe::next; 
+
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+
+sub method { (next::method($_[0]) || return)->(@_) }
 
 1;
 
@@ -406,6 +420,10 @@ You can use C<next::can> to see if C<next::method> will succeed before you call
 
   $self->next::method(@_) if $self->next::can; 
 
+Additionally, you can use C<maybe::next::method> as a shortcut to only call the next method if it exists. 
+The previous example could be simply written as:
+
+  $self->maybe::next::method(@_);
 
 There are some caveats about using C<next::method>, see below for those.
 
index 9bf2a2a..7af8035 100644 (file)
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 9;
+use Test::More tests => 11;
 
 BEGIN {
     use lib 'opt', '../opt', '..';    
@@ -42,8 +42,10 @@ This tests the classic diamond inheritence pattern.
     use c3;    
     use base 'Diamond_A';     
     sub foo { 'Diamond_C::foo' }   
-    sub buz { 'Diamond_C::buz' }         
-    sub woz { 'Diamond_C::woz' }  
+    sub buz { 'Diamond_C::buz' }     
+    
+    sub woz { 'Diamond_C::woz' }
+    sub maybe { 'Diamond_C::maybe' }         
 }
 {
     package Diamond_D;
@@ -52,9 +54,13 @@ This tests the classic diamond inheritence pattern.
     sub foo { 'Diamond_D::foo => ' . (shift)->next::method() } 
     sub bar { 'Diamond_D::bar => ' . (shift)->next::method() }   
     sub buz { 'Diamond_D::buz => ' . (shift)->baz() }  
-    sub fuz { 'Diamond_D::fuz => ' . (shift)->next::method() }
-    sub woz { 'Diamond_D::woz can => ' . (shift)->next::can() }
-    sub noz { 'Diamond_D::noz can => ' . (shift)->next::can() }               
+    sub fuz { 'Diamond_D::fuz => ' . (shift)->next::method() }  
+    
+    sub woz { 'Diamond_D::woz can => ' . ((shift)->next::can() ? 1 : 0) }
+    sub noz { 'Diamond_D::noz can => ' . ((shift)->next::can() ? 1 : 0) }
+
+    sub maybe { 'Diamond_D::maybe => ' . ((shift)->maybe::next::method() || 0) }
+    sub moybe { 'Diamond_D::moybe => ' . ((shift)->maybe::next::method() || 0) }             
 
 }
 
@@ -74,3 +80,6 @@ like($@, qr/^No next::method 'fuz' found for Diamond_D/, '... cannot re-dispatch
 
 is(Diamond_D->woz, 'Diamond_D::woz can => 1', '... can re-dispatch figured out correctly');
 is(Diamond_D->noz, 'Diamond_D::noz can => 0', '... cannot re-dispatch figured out correctly');
+
+is(Diamond_D->maybe, 'Diamond_D::maybe => Diamond_C::maybe', '... redispatched D to C when it exists');
+is(Diamond_D->moybe, 'Diamond_D::moybe => 0', '... quietly failed redispatch from D');