handle non-lazy default and builder when init_arg is undef
Matt S Trout [Fri, 25 Feb 2011 15:54:08 +0000 (15:54 +0000)]
We were skipping attributes entirely in new if there's no init_arg, but
this is not correct - given a non-lazy default or builder the attribute
should be initialized during new().

To handle this, I've re-ordered the checks slightly in Constructor.pm to
do the "is_simple" check first - an attribute with a default or builder
will fail that check, then we skip anyway if no init_arg and no default
or builder (yes, this is hardcoded-ish, but in that case we're definitely
not constructing anything so far as I can see). default and builder are
disregarded if lazy so as not to try and populate pure-lazy attributes.

Accessor also needed tweaking so it could handle the concept of a thing to
build without an init_arg to populate from. This only needs to happen in
one branch since the other branch is for "no default or builder" and thus
never reached.

Changes
lib/Method/Generate/Accessor.pm
lib/Method/Generate/Constructor.pm
t/accessor-default.t

diff --git a/Changes b/Changes
index f34fbce..d37e4cd 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,3 +1,4 @@
+  - handle non-lazy default and builder when init_arg is undef
   - add copyright and license info for downstream packagers
   - weak ref checking for Sub::Quote to avoid bugs on refaddr reuse
   - Switch composed role names to be a valid package name
index 7015ada..d4112bf 100644 (file)
@@ -275,12 +275,15 @@ sub _generate_populate_set {
   if (!$spec->{lazy} and
         ($spec->{default} or $spec->{builder})) {
     my $get_indent = ' ' x ($spec->{isa} ? 6 : 4);
+    my $get_default = $self->_generate_get_default(
+                        '$new', $_, $spec
+                      );
     my $get_value = 
-      "(\n${get_indent}  ${test}\n${get_indent}   ? ${source}\n${get_indent}   : "
-        .$self->_generate_get_default(
-          '$new', $_, $spec
-        )
-        ."\n${get_indent})";
+      defined($spec->{init_arg})
+        ? "(\n${get_indent}  ${test}\n${get_indent}   ? ${source}\n${get_indent}   : "
+            .$get_default
+            ."\n${get_indent})"
+        : $get_default;
     ($spec->{isa}
       ? "    {\n      my \$value = ".$get_value.";\n      "
         .$self->_generate_isa_check(
index c7138a1..5d459b6 100644 (file)
@@ -77,11 +77,14 @@ sub _assign_new {
   my $ag = $self->accessor_generator;
   NAME: foreach my $name (sort keys %$spec) {
     my $attr_spec = $spec->{$name};
-    next NAME unless defined(my $i = $attr_spec->{init_arg});
     unless ($ag->is_simple_attribute($name, $attr_spec)) {
-      $test{$name} = $i;
+      next NAME unless defined($attr_spec->{init_arg})
+                         or (($attr_spec->{default} or $attr_spec->{builder})
+                             and not $attr_spec->{lazy});
+      $test{$name} = $attr_spec->{init_arg};
       next NAME;
     }
+    next NAME unless defined(my $i = $attr_spec->{init_arg});
     push @init, $i;
     push @slots, $name;
   }
index 2cec9c0..1f3fbac 100644 (file)
@@ -13,6 +13,7 @@ use Test::More;
   has three => (is => 'ro', default => quote_sub q{ {} });
   has four => (is => 'ro', builder => '_build_four');
   sub _build_four { {} }
+  has five => (is => 'ro', init_arg => undef, default => sub { {} });
 }
 
 sub check {
@@ -31,4 +32,6 @@ check three => map Foo->new->{three}, 1..2;
 
 check four => map Foo->new->{four}, 1..2;
 
+check five => map Foo->new->{five}, 1..2;
+
 done_testing;