refactor constructor generation and test more complex cases
Matt S Trout [Mon, 8 Nov 2010 04:53:12 +0000 (04:53 +0000)]
lib/Method/Generate/Accessor.pm
lib/Method/Generate/Constructor.pm
t/accessor-mixed.t [new file with mode: 0644]
t/method-generate-constructor.t

index f922f9c..79f2e91 100644 (file)
@@ -25,6 +25,11 @@ sub generate_method {
   ;
 }
 
+sub is_simple_attribute {
+  my ($self, $name, $spec) = @_;
+  return !grep $spec->{$_}, qw(lazy default builder isa trigger);
+}
+
 sub _generate_get {
   my ($self, $name, $spec) = @_;
   my $simple = $self->_generate_simple_get('$_[0]', $name);
@@ -136,6 +141,68 @@ sub _generate_call_code {
   return "${cap_name}->(${values})";
 }
 
+sub generate_populate_set {
+  my $self = shift;
+  local $self->{captures} = {};
+  my $code = $self->_generate_populate_set(@_);
+  return ($code, $self->{captures});
+}
+
+sub _generate_populate_set {
+  my ($self, $me, $name, $spec, $source, $test) = @_;
+  if (!$spec->{lazy} and
+        ($spec->{default} or $spec->{builder})) {
+    my $get_indent = ' ' x ($spec->{isa} ? 6 : 4);
+    my $get_value = 
+      "(\n${get_indent}  ${test}\n${get_indent}   ? ${source}\n${get_indent}   : "
+        .$self->_generate_get_default(
+          '$new', $_, $spec
+        )
+        ."\n${get_indent})";
+    ($spec->{isa}
+      ? "    {\n      my \$value = ".$get_value.";\n      "
+       .$self->_generate_isa_check(
+         $name, '$value', $spec->{isa}
+       ).";\n"
+       .'      '.$self->_generate_simple_set($me, $name, '$value').";\n"
+       ."    }\n"
+      : '    '.$self->_generate_simple_set($me, $name, $get_value).";\n"
+    )
+    .($spec->{trigger}
+      ? '    '
+       .$self->_generate_trigger(
+         $name, $me, $self->_generate_simple_get($me, $name),
+         $spec->{trigger}
+       )." if ${test};\n"
+      : ''
+    );
+  } else {
+    "    if (${test}) {\n"
+      .($spec->{isa}
+        ? "      "
+         .$self->_generate_isa_check(
+           $name, $source, $spec->{isa}
+         ).";\n"
+        : ""
+      )
+      ."      ".$self->_generate_simple_set($me, $name, $source).";\n"
+      .($spec->{trigger}
+       ? "      "
+         .$self->_generate_trigger(
+           $name, $me, $self->_generate_simple_get($me, $name),
+           $spec->{trigger}
+         ).";\n"
+       : ""
+      )
+      ."    }\n";
+  }
+}
+
+sub generate_multi_set {
+  my ($self, $me, $to_set, $from) = @_;
+  "\@{${me}}{qw(${\join ' ', @$to_set})} = $from";
+}
+
 sub generate_simple_set {
   my $self = shift;
   local $self->{captures} = {};
index 0064eac..0ecf401 100644 (file)
@@ -40,10 +40,8 @@ sub generate_method {
   my $body = '    my $class = shift;'."\n";
   $body .= $self->_generate_args;
   $body .= $self->_check_required($spec);
-  $body .= $self->_check_isa($spec);
   $body .= '    my $new = bless({}, $class);'."\n";
   $body .= $self->_assign_new($spec);
-  $body .= $self->_fire_triggers($spec);
   if ($into->can('BUILD')) {
     require Method::Generate::BuildAll;
     $body .= Method::Generate::BuildAll->new->buildall_body_for(
@@ -57,6 +55,12 @@ sub generate_method {
   ;
 }
 
+sub _cap_call {
+  my ($self, $code, $captures) = @_;
+  @{$self->{captures}}{keys %$captures} = values %$captures if $captures;
+  $code;
+}
+
 sub _generate_args {
   my ($self) = @_;
   q{    my $args = ref($_[0]) eq 'HASH' ? $_[0] : { @_ };}."\n";
@@ -65,10 +69,11 @@ sub _generate_args {
 sub _assign_new {
   my ($self, $spec) = @_;
   my (@init, @slots, %test);
+  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});
-    if ($attr_spec->{lazy} or $attr_spec->{default} or $attr_spec->{builder}) {
+    unless ($ag->is_simple_attribute($name, $attr_spec)) {
       $test{$name} = $i;
       next NAME;
     }
@@ -78,40 +83,18 @@ sub _assign_new {
   return '' unless @init or %test;
   join '', (
     @init
-      ? '    @{$new}{qw('.join(' ',@slots).')}'
-        .' = @{$args}{qw('.join(' ',@init).')};'."\n"
+      ? '    '.$self->_cap_call($ag->generate_multi_set(
+         '$new', [ @slots ], '@{$args}{qw('.join(' ',@init).')}'
+       )).";\n"
       : ''
   ), map {
     my $arg_key = perlstring($test{$_});
-    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;
+    $self->_cap_call($ag->generate_populate_set(
+      '$new', $_, $attr_spec, $source, $test
+    ));
   } sort keys %test;
 }
 
diff --git a/t/accessor-mixed.t b/t/accessor-mixed.t
new file mode 100644 (file)
index 0000000..83bc7bc
--- /dev/null
@@ -0,0 +1,50 @@
+use strictures 1;
+use Test::More;
+
+my @result;
+
+{
+  package Foo;
+
+  use Class::Tiny;
+
+  my @isa = (isa => sub { push @result, 'isa', $_[0] });
+  my @trigger = (trigger => sub { push @result, 'trigger', $_[1] });
+  sub _mkdefault {
+    my $val = shift;
+    (default => sub { push @result, 'default', $val; $val; })
+  }
+
+  has a1 => (
+    is => 'ro', @isa
+  );
+  has a2 => (
+    is => 'ro', @isa, @trigger
+  );
+  has a3 => (
+    is => 'ro', @isa, @trigger
+  );
+  has a4 => (
+    is => 'ro', @trigger, _mkdefault('a4')
+  );
+  has a5 => (
+    is => 'ro', @trigger, _mkdefault('a5')
+  );
+  has a6 => (
+    is => 'ro', @isa, @trigger, _mkdefault('a6')
+  );
+  has a7 => (
+    is => 'ro', @isa, @trigger, _mkdefault('a7')
+  );
+}
+
+my $foo = Foo->new(a1 => 'a1', a2 => 'a2', a4 => 'a4', a6 => 'a6');
+
+is_deeply(
+  \@result,
+  [ qw(isa a1 isa a2 trigger a2 trigger a4 default a5 isa a6 trigger a6
+    default a7 isa a7) ],
+  'Stuff fired in expected order'
+);
+
+done_testing;
index 96d604c..db10020 100644 (file)
@@ -3,8 +3,11 @@ use Test::More;
 use Test::Fatal;
 
 use Method::Generate::Constructor;
+use Method::Generate::Accessor;
 
-my $gen = Method::Generate::Constructor->new;
+my $gen = Method::Generate::Constructor->new(
+  accessor_generator => Method::Generate::Accessor->new
+);
 
 $gen->generate_method('Foo', 'new', {
   one => { },