File Coverage

blib/lib/Class/Lego/Constructor.pm
Criterion Covered Total %
statement 77 82 93.9
branch 18 24 75.0
condition 9 19 47.3
subroutine 14 14 100.0
pod 0 4 0.0
total 118 143 82.5


line stmt bran cond sub pod time code
1              
2             package Class::Lego::Constructor;
3              
4 3     3   133353 use 5.006;
  3         14  
  3         129  
5 3     3   16 use strict;
  3         7  
  3         119  
6 3     3   16 use warnings;
  3         10  
  3         161  
7              
8             our $VERSION = '0.004';
9              
10 3     3   3510 use Scalar::Defer 0.13 ();
  3         57021  
  3         607  
11              
12             sub mk_constructor0 {
13 1     1 0 29 my $self = shift;
14 1         3 my $params = shift;
15              
16 1   33     8 my $class = ref $self || $self;
17 1         11 my @defaults = $self->_arrange_defaults0($params);
18 1         9 my $sub = $self->make_constructor(@defaults);
19 1         4 my $subname = $class . '::' . 'new';
20              
21 3     3   32 no strict 'refs';
  3         7  
  3         359  
22 1         2 *{$subname} = $sub;
  1         5  
23             }
24              
25             sub mk_constructor1 {
26 2     2 0 1677 my $self = shift;
27 2         5 my $params = shift;
28              
29 2   33     15 my $class = ref $self || $self;
30 2         18 my @defaults = $self->_arrange_defaults1($params);
31 2         18 my $sub = $self->make_constructor(@defaults);
32 2         7 my $subname = $class . '::' . 'new';
33              
34 3     3   16 no strict 'refs';
  3         14  
  3         178  
35 2         4 *{$subname} = $sub;
  2         14  
36             }
37              
38 3     3   2969 use SUPER;
  3         8653  
  3         2375  
39              
40             # turn the arguments of mk_constructor0 into
41             # two maps, one for immediate default values
42             # 'field' => 'value'
43             # and other for deferred defaults
44             # 'field' => 'deferred value'
45             sub _arrange_defaults0 {
46 1     1   2 my $self = shift;
47 1   50     5 my $params = shift || {};
48              
49 1         2 my (%deferred, %values);
50 1         7 while ( my ($k, $v) = each %$params ) {
51 4 50 66     68 if ( Scalar::Defer::is_deferred($v) ) { # already deferred
    100          
52 0         0 $deferred{$k} = $v;
53             } elsif ( ref $v && ref $v eq 'CODE' ) { # defer sub
54 2         22 $deferred{$k} = &Scalar::Defer::defer($v);
55             } else { # immediate value
56 2         24 $values{$k} = $v;
57             }
58             }
59 1         4 return (\%values, \%deferred);
60              
61             }
62              
63             # turn the arguments of mk_constructor1 into
64             # two maps, one for immediate default values
65             # 'field' => 'value'
66             # and other for deferred defaults
67             # 'field' => 'deferred value'
68             sub _arrange_defaults1 {
69 2     2   5 my $self = shift;
70 2   50     9 my $params = shift || {};
71              
72 2         2 my (%deferred, %values);
73 2         12 while ( my ($k, $v) = each %$params ) {
74 10 50       68 if ( ref $v ne 'HASH' ) {
75 0         0 die "all entries must be hash refs: $k => $v"; # FIXME croak
76             }
77 10 100       34 if ( exists $v->{default} ) {
    50          
78 4 50       14 if ( exists $v->{default_value} ) {
79 0         0 die "at entry $k, 'default' takes precedence over 'default_value'"; # FIXME croak
80             }
81              
82 4         8 my $default = $v->{default};
83 4 50 66     13 if ( Scalar::Defer::is_deferred($default) ) { # already deferred
    100          
84 0         0 $deferred{$k} = $default;
85             } elsif ( ref $default && ref $default eq 'CODE' ) { # defer sub
86 2         21 $deferred{$k} = &Scalar::Defer::defer($default);
87             } else { # immediate value
88 2         26 $values{$k} = $default;
89             }
90             } elsif ( exists $v->{default_value} ) {
91             # immediate value
92 6         61 $values{$k} = $v->{default_value};
93             } else {
94 0         0 die "entry $k has no 'default' or 'default_value'"; # FIXME croak
95             }
96             }
97 2         30 return (\%values, \%deferred);
98              
99             }
100              
101             sub make_constructor {
102 3     3 0 6 my $self = shift;
103 3         5 my $default_values = shift;
104 3         6 my $deferred_defaults = shift;
105              
106             # return a closure
107             return sub {
108 13     13   21180 my $self = shift;
109 13         17 my $fields = shift;
110 13 100       20 my %f = %{ $fields || {} };
  13         87  
111 13         51 while ( my ($k, $v) = each %$default_values ) {
112 30 100       85 if ( !exists $f{$k} ) {
113 26         95 $f{$k} = $v;
114             }
115             }
116 13         46 while ( my ($k, $v) = each %$deferred_defaults ) {
117 24 100       472 if ( !exists $f{$k} ) {
118 20         60 $f{$k} = Scalar::Defer::force($v);
119             }
120             }
121 13         492 return $self->super('new')->( $self, \%f );
122              
123 3         21 };
124             }
125              
126             # fallback constructor, from Class::Accessor
127             sub new {
128 13     13 0 1131 my($proto, $fields) = @_;
129 13   33     111 my($class) = ref $proto || $proto;
130              
131 13 50       33 $fields = {} unless defined $fields;
132              
133             # make a copy of $fields.
134 13         84 bless {%$fields}, $class;
135             }
136              
137             1;