From: Stevan Little Date: Fri, 14 Jul 2006 02:27:43 +0000 (+0000) Subject: Applying Patch from Robert Norris again X-Git-Tag: 0.13~4 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=fa91a1c74155c2a85026814dfeb203a2bf1b6124;p=gitmo%2FClass-C3.git Applying Patch from Robert Norris again --- diff --git a/lib/Class/C3.pm b/lib/Class/C3.pm index a2f4740..988a8e9 100644 --- a/lib/Class/C3.pm +++ b/lib/Class/C3.pm @@ -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 to see if C will succeed before you call $self->next::method(@_) if $self->next::can; +Additionally, you can use C 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, see below for those. diff --git a/t/31_next_method_skip.t b/t/31_next_method_skip.t index 9bf2a2a..7af8035 100644 --- a/t/31_next_method_skip.t +++ b/t/31_next_method_skip.t @@ -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');