support for default at construction time
Matt S Trout [Sun, 7 Nov 2010 21:29:03 +0000 (21:29 +0000)]
lib/Method/Generate/Accessor.pm
lib/Method/Generate/Constructor.pm
t/accessor-default.t

index 6a0326b..f922f9c 100644 (file)
@@ -30,8 +30,8 @@ sub _generate_get {
   my $simple = $self->_generate_simple_get('$_[0]', $name);
   my ($lazy, $default, $builder) = @{$spec}{qw(lazy default builder)};
   return $simple unless $lazy and ($default or $builder);
-  'do { '.$self->_generate_default(
-    '$_[0]', $name, $default, $builder,
+  'do { '.$self->_generate_use_default(
+    '$_[0]', $name, $spec,
     $self->_generate_simple_has('$_[0]', $name),
   ).'; '.$simple.' }';
 }
@@ -41,17 +41,27 @@ sub _generate_simple_has {
   "exists ${me}->{${\perlstring $name}}";
 }
 
-sub _generate_default {
-  my ($self, $me, $name, $default, $builder, $test) = @_;
+sub generate_get_default {
+  my $self = shift;
+  local $self->{captures} = {};
+  my $code = $self->_generate_get_default(@_);
+  return ($code, $self->{captures});
+}
+
+sub _generate_use_default {
+  my ($self, $me, $name, $spec, $test) = @_;
   $self->_generate_simple_set(
-    $me, $name, (
-      $default
-        ? $self->_generate_call_code($name, 'default', $me, $default)
-        : "${me}->${builder}"
-    )
+    $me, $name, $self->_generate_get_default($me, $name, $spec)
   ).' unless '.$test;
 }
 
+sub _generate_get_default {
+  my ($self, $me, $name, $spec) = @_;
+  $spec->{default}
+    ? $self->_generate_call_code($name, 'default', $me, $spec->{default})
+    : "${me}->${\$spec->{builder}}"
+}
+
 sub generate_simple_get {
   shift->_generate_simple_get(@_);
 }
@@ -126,6 +136,13 @@ sub _generate_call_code {
   return "${cap_name}->(${values})";
 }
 
+sub generate_simple_set {
+  my $self = shift;
+  local $self->{captures} = {};
+  my $code = $self->_generate_simple_set(@_);
+  return ($code, $self->{captures});
+}
+
 sub _generate_simple_set {
   my ($self, $me, $name, $value) = @_;
   my $name_str = perlstring $name;
index de3111f..d62545d 100644 (file)
@@ -62,14 +62,14 @@ sub _assign_new {
   NAME: foreach my $name (sort keys %$spec) {
     my $attr_spec = $spec->{$name};
     next NAME unless defined(my $i = $attr_spec->{init_arg});
-    if ($attr_spec->{lazy}) {
+    if ($attr_spec->{lazy} or $attr_spec->{default} or $attr_spec->{builder}) {
       $test{$name} = $i;
       next NAME;
     }
     push @init, $i;
     push @slots, $name;
   }
-  return '' unless @init;
+  return '' unless @init or %test;
   join '', (
     @init
       ? '    @{$new}{qw('.join(' ',@slots).')}'
@@ -77,8 +77,35 @@ sub _assign_new {
       : ''
   ), map {
     my $arg_key = perlstring($test{$_});
-    "    \$new->{${\perlstring($_)}} = \$args->{$arg_key}\n"
-    ."      if exists \$args->{$arg_key};\n"
+    my $ag = $self->accessor_generator;
+    my $test = "exists \$args->{$arg_key}";
+    my $source = "\$args->{$arg_key}";
+    my $attr_spec = $spec->{$_};
+    my ($code, $add_captures);
+    if (!$attr_spec->{lazy} and
+          ($attr_spec->{default} or $attr_spec->{builder})) {
+      my $get_captures;
+      ($code, $add_captures) = $ag->generate_simple_set(
+        '$new', $_,
+        "(\n      ${test}\n        ? ${source}\n        : "
+          .do {
+            (my $get, $get_captures) = $ag->generate_get_default(
+              '$new', $_, $attr_spec
+            );
+            $get;
+          }
+          ."\n    )"
+      );
+      @{$add_captures}{keys %$get_captures} = values %$get_captures;
+      $code .= ";\n";
+    } else {
+      ($code, $add_captures) = $ag->generate_simple_set(
+        '$new', $_, "\$args->{$arg_key}"
+      );
+      $code .= " if ${test};\n";
+    }
+    @{$self->{captures}}{keys %$add_captures} = values %$add_captures;
+    '    '.$code;
   } sort keys %test;
 }
 
@@ -108,7 +135,12 @@ sub _check_isa {
       $name, "\$args->{${init_str}}", $isa
     );
     @{$captures}{keys %$add_captures} = values %$add_captures;
-    $check .= "    ${code} if exists \$args->{${init_str}};\n";
+    $check .= "    ${code}".(
+      (not($spec->{lazy}) and ($spec->{default} or $spec->{builder})
+        ? ";\n"
+        : "if exists \$args->{${init_str}};\n"
+      )
+    );
   }
   return $check;
 }
index af6e448..80786d0 100644 (file)
@@ -11,7 +11,7 @@ use Test::More;
   has two => (is => 'ro', lazy => 1, builder => '_build_two');
   sub _build_two { {} }
   has three => (is => 'ro', default => quote_sub q{ {} });
-  has four => (is => 'ro', default => '_build_four');
+  has four => (is => 'ro', builder => '_build_four');
   sub _build_four { {} }
 }
 
@@ -27,4 +27,8 @@ check one => map Foo->new->one, 1..2;
 
 check two => map Foo->new->two, 1..2;
 
+check three => map Foo->new->{three}, 1..2;
+
+check four => map Foo->new->{four}, 1..2;
+
 done_testing;