rewrite nonMoo detection
Matt S Trout [Sat, 8 Jan 2011 05:41:04 +0000 (05:41 +0000)]
Changes
lib/Moo.pm
lib/Moo/Role.pm
lib/Sub/Defer.pm
t/buildall.t

diff --git a/Changes b/Changes
index 1a1a22c..51ad7f0 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,3 +1,6 @@
+  - Fix bug where nonMoo is mistakenly detected given a Moo superclass
+    with no attributes (and hence no own constructor)
+
 0.9.4 Mon Dec 13 2010
   - Automatic detection on non-Moo superclasses
 
index 01d9830..2c02cf2 100644 (file)
@@ -48,22 +48,31 @@ sub import {
 }
 
 sub _constructor_maker_for {
-  my ($class, $target) = @_;
+  my ($class, $target, $select_super) = @_;
   return unless $MAKERS{$target};
   $MAKERS{$target}{constructor} ||= do {
     require Method::Generate::Constructor;
-    my $con;
+    require Sub::Defer;
+    my ($moo_constructor, $con);
 
-    # using the -last- entry in @ISA means that classes created by
-    # Role::Tiny as N roles + superclass will still get the attributes
-    # from the superclass via the ->register_attribute_specs call later
-
-    if (my $super = do { no strict 'refs'; ${"${target}::ISA"}[-1] }) {
-      $con = $MAKERS{$super}{constructor} if $MAKERS{$super};
-    }
-    my $moo_constructor = !!$con || do {
+    if ($select_super && $MAKERS{$select_super}) {
+      $moo_constructor = 1;
+      $con = $MAKERS{$select_super}{constructor};
+    } else {
       my $t_new = $target->can('new');
-      $t_new and $t_new == Moo::Object->can('new');
+      if ($t_new) {
+        if ($t_new == Moo::Object->can('new')) {
+          $moo_constructor = 1;
+        } elsif (my $defer_target = (Sub::Defer::defer_info($t_new)||[])->[0]) {
+          my ($pkg) = ($defer_target =~ /^(.*)::[^:]+$/);
+          if ($MAKERS{$pkg}) {
+            $moo_constructor = 1;
+            $con = $MAKERS{$pkg}{constructor};
+          }
+        }
+      } else {
+        $moo_constructor = 1; # no other constructor, make a Moo one
+      }
     };
     require Moo::_mro unless $moo_constructor;
     Method::Generate::Constructor
index a38677a..a8c2083 100644 (file)
@@ -46,7 +46,7 @@ sub create_class_with_roles {
   }
 
   $me->_handle_constructor(
-    $new_name, { map %{$INFO{$_}{attributes}||{}}, @roles }
+    $new_name, { map %{$INFO{$_}{attributes}||{}}, @roles }, $superclass
   );
 
   return $new_name;
@@ -58,14 +58,14 @@ sub _install_single_modifier {
 }
 
 sub _handle_constructor {
-  my ($me, $to, $attr_info) = @_;
+  my ($me, $to, $attr_info, $superclass) = @_;
   return unless $attr_info && keys %$attr_info;
   if ($INFO{$to}) {
     @{$INFO{$to}{attributes}||={}}{keys %$attr_info} = values %$attr_info;
   } else {
     # only fiddle with the constructor if the target is a Moo class
     if ($INC{"Moo.pm"}
-        and my $con = Moo->_constructor_maker_for($to)) {
+        and my $con = Moo->_constructor_maker_for($to, $superclass)) {
       $con->register_attribute_specs(%$attr_info);
     }
   }
index 0aa0068..4f2db1f 100644 (file)
@@ -18,9 +18,15 @@ sub undefer_sub {
     no warnings 'redefine';
     *{_getglob($target)} = $made;
   }
+  push @{$DEFERRED{$made} = $DEFERRED{$deferred}}, $made;
   return $made;
 }
 
+sub defer_info {
+  my ($deferred) = @_;
+  $DEFERRED{$deferred||''};
+}
+
 sub defer_sub {
   my ($target, $maker) = @_;
   my $undeferred;
index 9129cc6..a6e64d9 100644 (file)
@@ -18,6 +18,21 @@ my @ran;
   sub BUILD { push @ran, $_[0]->foo, $_[1]->{bar} }
 }
 
+{
+  package Odd1;
+  use Moo;
+  has 'odd1' => (is => 'ro');
+  sub BUILD { push @ran, 'Odd1' }
+  package Odd2;
+  use Moo;
+  extends 'Odd1';
+  package Odd3;
+  use Moo;
+  extends 'Odd2';
+  has 'odd3' => (is => 'ro');
+  sub BUILD { push @ran, 'Odd3' }
+}
+
 my $o = Quux->new;
 
 is(ref($o), 'Quux', 'object returned');
@@ -30,4 +45,11 @@ $o = Fleem->new(foo => 'Fleem1', bar => 'Fleem2');
 is(ref($o), 'Fleem', 'object with inline constructor returned');
 is_deeply(\@ran, [ qw(Foo Bar Quux Fleem1 Fleem2) ], 'BUILDs ran in order');
 
+@ran = ();
+
+$o = Odd3->new(odd1 => 1, odd3 => 3);
+
+is(ref($o), 'Odd3', 'Odd3 object constructed');
+is_deeply(\@ran, [ qw(Odd1 Odd3) ], 'BUILDs ran in order');
+
 done_testing;