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);
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);
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;
$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.
use strict;
use warnings;
-use Test::More tests => 9;
+use Test::More tests => 11;
BEGIN {
use lib 'opt', '../opt', '..';
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;
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) }
}
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');