use Alg::C3 0.05 persistent merge cache parameter, VERSION/Changes for 0.13
[gitmo/Class-C3.git] / lib / Class / C3.pm
index bd72a00..73e8228 100644 (file)
@@ -7,7 +7,7 @@ use warnings;
 use Scalar::Util 'blessed';
 use Algorithm::C3;
 
-our $VERSION = '0.12';
+our $VERSION = '0.13';
 
 # this is our global stash of both 
 # MRO's and method dispatch tables
@@ -67,15 +67,16 @@ sub reinitialize {
 ## functions for applying C3 to classes
 
 sub _calculate_method_dispatch_tables {
+    my %merge_cache;
     foreach my $class (keys %MRO) {
-        _calculate_method_dispatch_table($class);
+        _calculate_method_dispatch_table($class, \%merge_cache);
     }
 }
 
 sub _calculate_method_dispatch_table {
-    my $class = shift;
+    my ($class, $merge_cache) = @_;
     no strict 'refs';
-    my @MRO = calculateMRO($class);
+    my @MRO = calculateMRO($class, $merge_cache);
     $MRO{$class} = { MRO => \@MRO };
     my $has_overload_fallback = 0;
     my %methods;
@@ -139,11 +140,11 @@ sub _remove_method_dispatch_table {
 ## functions for calculating C3 MRO
 
 sub calculateMRO {
-    my ($class) = @_;
+    my ($class, $merge_cache) = @_;
     return Algorithm::C3::merge($class, sub { 
         no strict 'refs'; 
         @{$_[0] . '::ISA'};
-    });
+    }, $merge_cache);
 }
 
 package  # hide me from PAUSE
@@ -159,7 +160,9 @@ our $VERSION = '0.05';
 our %METHOD_CACHE;
 
 sub method {
-    my $level = 1;
+    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,28 +175,44 @@ sub method {
     my $self     = $_[0];
     my $class    = blessed($self) || $self;
     
-    goto &{ $METHOD_CACHE{"$class|$caller|$label"} ||= do {
+    my $method = $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}));
+        }
+        
+        $found;
+    };
+
+    return $method if $indirect;
 
-      my @MRO = Class::C3::calculateMRO($class);
+    die "No next::method '$label' found for $self" if !$method;
 
-      my $current;
-      while ($current = shift @MRO) {
-          last if $caller eq $current;
-      }
+    goto &{$method};
+}
 
-      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}));
-      }
+sub can { method($_[0]) }
 
-      die "No next::method '$label' found for $self" unless $found;
+package  # hide me from PAUSE
+    maybe::next; 
 
-      $found;
-    } };
-}
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+
+sub method { (next::method($_[0]) || return)->(@_) }
 
 1;
 
@@ -397,6 +416,16 @@ that you cannot dispatch to a method of a different name (this is how C<NEXT::>
 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; 
+
+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.
 
 =head1 CAVEATS
@@ -524,6 +553,9 @@ and finding many bugs and providing fixes.
 =item Thanks to Justin Guenther for making C<next::method> more robust by handling 
 calls inside C<eval> and anon-subs.
 
+=item Thanks to Robert Norris for adding support for C<next::can> and 
+C<maybe::next::method>.
+
 =back
 
 =head1 AUTHOR
@@ -539,4 +571,4 @@ L<http://www.iinteractive.com>
 This library is free software; you can redistribute it and/or modify
 it under the same terms as Perl itself. 
 
-=cut
\ No newline at end of file
+=cut