our %METHOD_CACHE;
-sub method {
- my $level = 1;
+sub _find {
+ my $level = 2;
my ($method_caller, $label, @label);
while ($method_caller = (caller($level++))[3]) {
@label = (split '::', $method_caller);
my $self = $_[0];
my $class = blessed($self) || $self;
- goto &{ $METHOD_CACHE{"$class|$caller|$label"} ||= do {
-
- my @MRO = Class::C3::calculateMRO($class);
-
- my $current;
- while ($current = shift @MRO) {
- last if $caller eq $current;
- }
-
- no strict 'refs';
- my $found;
- foreach my $class (@MRO) {
- next if (defined $Class::C3::MRO{$class} &&
- defined $Class::C3::MRO{$class}{methods}{$label});
- last if (defined ($found = *{$class . '::' . $label}{CODE}));
- }
+ return $METHOD_CACHE{"$class|$caller|$label"} ||= do {
+
+ my @MRO = Class::C3::calculateMRO($class);
+
+ my $current;
+ while ($current = shift @MRO) {
+ last if $caller eq $current;
+ }
+
+ no strict 'refs';
+ my $found;
+ foreach my $class (@MRO) {
+ next if (defined $Class::C3::MRO{$class} &&
+ defined $Class::C3::MRO{$class}{methods}{$label});
+ last if (defined ($found = *{$class . '::' . $label}{CODE}));
+ }
+
+ die "No next::method '$label' found for $_[0]" if $_[1] && !$found;
+
+ $found;
+ };
+}
- die "No next::method '$label' found for $self" unless $found;
+sub method { goto &{_find($_[0], 1)} }
- $found;
- } };
-}
+sub can { return _find($_[0], 0) ? 1 : 0 }
1;
The next thing to keep in mind is that you will need to pass all arguments to C<next::method> it can
not automatically use the current C<@_>.
+If C<next::method> cannot find a next method to re-dispatch the call to, it will throw an exception.
+You can use C<next::can> to see if C<next::method> will succeed before you call it like so:
+
+ $self->next::method(@_) if $self->next::can;
+
+
There are some caveats about using C<next::method>, see below for those.
=head1 CAVEATS
use strict;
use warnings;
-use Test::More tests => 7;
+use Test::More tests => 9;
BEGIN {
use lib 'opt', '../opt', '..';
use c3;
use base 'Diamond_A';
sub foo { 'Diamond_C::foo' }
- sub buz { 'Diamond_C::buz' }
+ sub buz { 'Diamond_C::buz' }
+ sub woz { 'Diamond_C::woz' }
}
{
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 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() }
}
eval { Diamond_D->fuz };
like($@, qr/^No next::method 'fuz' found for Diamond_D/, '... cannot re-dispatch to a method which is not there');
-
+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');