Add explicit check for odd number of args to new and give a useful warning
Dave Rolsky [Sun, 17 Oct 2010 15:16:23 +0000 (10:16 -0500)]
Changes
lib/Moose/Meta/Method/Constructor.pm
lib/Moose/Object.pm
t/010_basics/022_buildargs_warning.t [new file with mode: 0644]

diff --git a/Changes b/Changes
index 8c7cf41..6ea77d8 100644 (file)
--- a/Changes
+++ b/Changes
@@ -13,6 +13,10 @@ NEXT
     what arguments each native delegation method allows or requires. (Dave
     Rolsky)
 
+  * Passing an odd number of args to ->new() now gives a more useful warning
+    than Perl's builtin warning. Suggested by Sir Robert Burbridge. (Dave
+    Rolsky)
+
   [BUG FIXES]
 
   * A number of native trait methods which expected strings as arguments did
index e64e9db..fc043e7 100644 (file)
@@ -4,6 +4,7 @@ package Moose::Meta::Method::Constructor;
 use strict;
 use warnings;
 
+use Carp ();
 use Scalar::Util 'blessed', 'weaken', 'looks_like_number', 'refaddr';
 
 our $VERSION   = '1.15';
@@ -144,15 +145,34 @@ sub _generate_BUILDARGS {
 
     my $buildargs = $self->associated_metaclass->find_method_by_name("BUILDARGS");
 
-    if ( $args eq '@_' and ( !$buildargs or $buildargs->body == \&Moose::Object::BUILDARGS ) ) {
-        return join("\n",
-            'do {',
-            $self->_inline_throw_error('"Single parameters to new() must be a HASH ref"', 'data => $_[0]'),
-            '    if scalar @_ == 1 && !( defined $_[0] && ref $_[0] eq q{HASH} );',
-            '(scalar @_ == 1) ? {%{$_[0]}} : {@_};',
-            '}',
-        );
-    } else {
+    if ( $args eq '@_'
+        and ( !$buildargs or $buildargs->body == \&Moose::Object::BUILDARGS )
+        ) {
+
+        # This is basically a copy of Moose::Object::BUILDARGS wrapped in a do
+        # {} block.
+        return sprintf( <<'EOF', $self->_inline_throw_error( q{'Single parameters to new() must be a HASH ref'}, 'data => $_[0]' ) );
+do {
+    if ( scalar @_ == 1 ) {
+        unless ( defined $_[0] && ref $_[0] eq 'HASH' ) {
+            %s
+        }
+        return { %%{ $_[0] } };
+    }
+    elsif ( @_ %% 2 ) {
+        Carp::carp(
+            "The new() method for $class expects a hash reference or a key/value list."
+                . " You passed an odd number of arguments" );
+        return { @_, undef };
+    }
+    else {
+        return {@_};
+    }
+};
+EOF
+            ;
+    }
+    else {
         return $class . "->BUILDARGS($args)";
     }
 }
index b8f02c9..1896b06 100644 (file)
@@ -4,6 +4,7 @@ package Moose::Object;
 use strict;
 use warnings;
 
+use Carp ();
 use Devel::GlobalDestruction ();
 use MRO::Compat ();
 use Scalar::Util ();
@@ -35,6 +36,12 @@ sub BUILDARGS {
         }
         return { %{ $_[0] } };
     }
+    elsif ( @_ % 2 ) {
+        Carp::carp(
+            "The new() method for $class expects a hash reference or a key/value list."
+                . " You passed an odd number of arguments" );
+        return { @_, undef };
+    }
     else {
         return {@_};
     }
diff --git a/t/010_basics/022_buildargs_warning.t b/t/010_basics/022_buildargs_warning.t
new file mode 100644 (file)
index 0000000..f9cd94c
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Moose qw( with_immutable );
+
+use Test::Requires {
+    'Test::Output' => '0.01',
+};
+
+{
+    package Baz;
+    use Moose;
+}
+
+with_immutable {
+    stderr_like { Baz->new( x => 42, 'y' ) }
+    qr{\QThe new() method for Baz expects a hash reference or a key/value list. You passed an odd number of arguments at t/010_basics/022_buildargs_warning.t line \E\d+},
+        'warning when passing an odd number of args to new()';
+
+    stderr_unlike { Baz->new( x => 42, 'y' ) }
+    qr{\QOdd number of elements in anonymous hash},
+        'we suppress the standard warning from Perl for an odd number of elements in a hash';
+}
+'Baz';
+
+done_testing;