File Coverage

blib/lib/Anonymous/Object.pm
Criterion Covered Total %
statement 299 322 92.8
branch 135 138 97.8
condition 31 32 96.8
subroutine 46 48 95.8
pod 19 20 95.0
total 530 560 94.6


line stmt bran cond sub pod time code
1             package Anonymous::Object;
2 11     11   940742 use strict;
  11         20  
  11         411  
3 11     11   50 use warnings;
  11         34  
  11         587  
4 11     11   3984 use Loo qw/dDump/;
  11         6989  
  11         863  
5             our $VERSION = 1.02;
6              
7             our $UNIQUE;
8             BEGIN {
9 11     11   22513 $UNIQUE = 0;
10             }
11              
12             sub new {
13 44 100   44 1 1196456 my ( $cls, %args ) = ( shift(), scalar @_ == 1 ? %{ $_[0] } : @_ );
  39         226  
14 44         103 my $self = bless {}, $cls;
15 44         507 my %accessors = (
16             object_name => { default => 'Anonymous::Object' },
17             meta => { default => {}, },
18             default => { default => {}, },
19             types => { default => {}, },
20             type_library => { default => 'Types::Standard' },
21             type_map => {
22             default => {
23             HASH => 'HashRef',
24             ARRAY => 'ArrayRef',
25             STRING => 'Str',
26             SCALAR => 'ScalarRef',
27             REF => 'Ref',
28             CODE => 'CodeRef',
29             GLOB => 'GlobRef',
30             NUM => 'Num',
31             INT => 'Int',
32             default => 'Any'
33             }
34             }
35             );
36 44         164 for my $accessor ( keys %accessors ) {
37             my $param = defined $args{$accessor}
38             ? $args{$accessor}
39 250 100       498 : $accessors{$accessor}->{default};
40             my $value
41             = $self->$accessor( $args{"build_$accessor"}
42 250 100       673 ? $args{"build_$accessor"}->( $self, $param )
43             : $param );
44 248 100       614 unless ( defined $value ) {
45 3         47 die "$accessor accessor is required";
46             }
47             }
48 41         278 return $self;
49             }
50              
51             sub clean {
52 23     23 1 78 my $class = ref $_[0];
53             return $class->new({
54             object_name => $_[0]->{object_name},
55             type_library => $_[0]->{type_library},
56             type_map => $_[0]->{type_map}
57 23         134 });
58             }
59              
60             sub object_name {
61 45     45 1 636 my ($self, $value) = @_;
62 45 100       212 if ( defined $value ) {
63 42 100       73 if ( ref $value ) {
64 1         10 die qq{Str: invalid value $value for accessor object_name}
65             }
66 41         79 $self->{object_name} = $value;
67             }
68 42         88 return $self->{object_name};
69             }
70              
71             sub default {
72 97     99 1 718 my ( $self, $value ) = @_;
73 97 100       183 if ( defined $value ) {
74 49 100 100     133 if ( ( ref($value) || "" ) ne "HASH" ) {
75 1         8 die qq{HashRef: invalid value $value for accessor default};
76             }
77 48         107 $self->{default} = $value;
78             }
79 96         193 return $self->{default};
80             }
81              
82             sub meta {
83 319     319 1 1496 my ( $self, $value ) = @_;
84 319 100       467 if ( defined $value ) {
85 45 100 100     140 if ( ( ref($value) || "" ) ne "HASH" ) {
86 4         41 die qq{HashRef: invalid value $value for accessor meta};
87             }
88 41         74 $self->{meta} = $value;
89             }
90 315         708 return $self->{meta};
91             }
92              
93             sub types {
94 67     67 1 1080 my ( $self, $value ) = @_;
95 67 100       148 if ( defined $value ) {
96 43 100 100     144 if ( ( ref($value) || "" ) ne "HASH" ) {
97 2         14 die qq{HashRef: invalid value $value for accessor types};
98             }
99 41         87 $self->{types} = $value;
100             }
101 65         273 return $self->{types};
102             }
103              
104             sub type_library {
105 66     66 1 538 my ($self, $value) = @_;
106 66 100       121 if ( defined $value ) {
107 42 100       93 if ( ref $value ) {
108 1         9 die qq{Str: invalid value $value for accessor type_library}
109             }
110 41         92 $self->{type_library} = $value;
111             }
112 65         117 return $self->{type_library};
113             }
114              
115             sub type_map {
116 56     56 1 549 my ( $self, $value ) = @_;
117 56 100       122 if ( defined $value ) {
118 41 100 100     130 if ( ( ref($value) || "" ) ne "HASH" ) {
119 1         8 die qq{HashRef: invalid value $value for accessor type_map};
120             }
121 40         76 $self->{type_map} = $value;
122             }
123 55         135 return $self->{type_map};
124             }
125              
126             sub hash_to_object_context {
127 1     1 1 2 my ( $self, $hash, %accessors ) = @_;
128 1 50 50     3 if ( ( ref($hash) || "" ) ne "HASH" ) {
129 0 0       0 $hash = defined $hash ? $hash : 'undef';
130 0         0 die
131             qq{HashRef: invalid value $hash for variable \$hash in method hash_to_object_context};
132             }
133 1         3 $self = $self->clean();
134 1         3 $self->default({});
135 1         1 for my $key ( keys %{$hash} ) {
  1         3  
136 2         8 $self->add_method(
137             {
138             name => $key,
139             set => 1,
140             code => qq|return shift->{$key}->(splice \@_, 1)|,
141             %accessors
142             }
143             );
144             }
145 1         4 my $build = $self->build;
146 1         3 for my $key ( keys %{$hash} ) {
  1         5  
147 2         6 my $meth = "set_${key}";
148 2         62 $build->$meth($hash->{$key});
149             }
150 1         21 return $build;
151             }
152              
153             sub hash_to_object {
154 8     8 1 1060 my ( $self, $hash, %accessors ) = @_;
155 8 100 100     39 if ( ( ref($hash) || "" ) ne "HASH" ) {
156 3 100       9 $hash = defined $hash ? $hash : 'undef';
157 3         24 die
158             qq{HashRef: invalid value $hash for variable \$hash in method hash_to_object};
159             }
160 5         19 $self = $self->clean();
161 5         16 $self->default($hash);
162 5         6 for my $key ( keys %{$hash} ) {
  5         13  
163 30         54 $self->add_method(
164             {
165             name => $key,
166             %accessors
167             }
168             );
169             }
170 5         12 return $self->build;
171             }
172              
173             sub hash_to_nested_object {
174 17     17 1 939 my ( $self, $hash, %accessors ) = @_;
175 17 100 100     76 if ( ( ref($hash) || "" ) ne "HASH" ) {
176 2 100       5 $hash = defined $hash ? $hash : 'undef';
177 2         14 die
178             qq{HashRef: invalid value $hash for variable \$hash in method hash_to_object};
179             }
180 15         40 $self = $self->clean();
181 15         31 for my $key ( keys %{$hash} ) {
  15         41  
182 22         40 my $val = $hash->{$key};
183 22   100     68 my $ref = ref $val || "";
184 22 100       50 if ($ref eq 'HASH') {
    100          
185 8         40 $val = $self->hash_to_nested_object(
186             $val,
187             %accessors
188             );
189             } elsif ($ref eq 'ARRAY') {
190 4         14 $val = $self->array_to_nested_object(
191             $val,
192             %accessors
193             );
194             }
195             $self->add_method(
196             {
197 22         121 name => $key,
198             default => $val,
199             nested => 1,
200             %accessors
201             }
202             );
203             }
204 15         34 return $self->build;
205             }
206              
207             sub array_to_nested_object {
208 8     8 1 1433 my ( $self, $array, %accessors ) = @_;
209 8 100 100     42 if ( ( ref($array) || "" ) ne "ARRAY" ) {
210 2 100       5 $array = defined $array ? $array : 'undef';
211 2         14 die
212             qq{ArrayRef: invalid value $array for variable \$array in method array_to_object};
213             }
214 6         13 for (my $i = 0; $i < scalar @{$array}; $i++) {
  18         38  
215 12         28 my $val = $array->[$i];
216 12   100     32 my $ref = ref $val || "";
217 12 100       32 if ($ref eq 'HASH') {
    100          
218 5         31 $val = $self->hash_to_nested_object(
219             $val,
220             %accessors
221             );
222             } elsif ($ref eq 'ARRAY') {
223 1         6 $val = $self->array_to_nested_object(
224             $val,
225             %accessors
226             );
227             }
228 12         31 $array->[$i] = $val;
229             }
230 6         27 return $array;
231             }
232              
233             sub add_new {
234 28     28 1 1157 my ( $self, $new ) = @_;
235 28 100 100     138 if ( ( ref($new) || "" ) ne "HASH" ) {
236 4 100       10 $new = defined $new ? $new : 'undef';
237 4         30 die
238             qq{HashRef: invalid value $new for variable \$new in method add_new};
239             }
240              
241             return sprintf q|return bless { %s }, __PACKAGE__;|, join q|,|,
242 55         110 map { sprintf q|%s => %s|, $_, $self->stringify_struct( $new->{$_} ) }
243 24         36 keys %{$new};
  24         91  
244              
245             }
246              
247             sub add_methods {
248 5     5 1 2495 my ( $self, $methods ) = @_;
249 5 100 100     39 if ( ( ref($methods) || "" ) ne "ARRAY" ) {
250 4 100       17 $methods = defined $methods ? $methods : 'undef';
251 4         43 die
252             qq{ArrayRef: invalid value $methods for variable \$methods in method add_methods};
253             }
254 1         3 for my $method ( @{$methods} ) {
  1         4  
255 1         4 $self->add_method($method);
256             }
257 1         7 return $self;
258             }
259              
260             sub add_method {
261 64     64 1 2773 my ( $self, $method ) = @_;
262 64 100 100     181 if ( ( ref($method) || "" ) ne "HASH" ) {
263 4 100       11 $method = defined $method ? $method : 'undef';
264 4         32 die
265             qq{HashRef: invalid value $method for variable \$method in method add_method};
266             }
267 60 100 100     203 if ( ( ! defined $method->{name} || ref($method->{name}) ) ) {
268 2 100       6 $method->{name} = defined $method->{name} ? $method->{name} : 'undef';
269 2         16 die
270             qq{Str: invalid value $method->{name} for variable \$method->{name} in method add_method};
271             }
272              
273 58         76 my $name = $method->{name};
274 58 100       110 if ( $method->{clearer} ) {
275 21         42 $self->meta->{ q|clear_| . $name }
276             = qq|return delete \$_[0]->{$name};|;
277             }
278 58 100       112 if ( $method->{predicate} ) {
279 1         3 $self->meta->{ q|has_| . $name }
280             = qq|return exists \$_[0]->{$name};|;
281             }
282 58 100       92 if ( $method->{get} ) {
283 1         8 $self->meta->{ q|get_| . $name }
284             = qq|return \$_[0]->{$name};|;
285             }
286 58 100       100 if ( $method->{set} ) {
287 14         18 my $set = q|my ($self, $val) = @_; |;
288             $method->{type} = $self->identify_type($method->{default})
289 14 100       60 if ($method->{autotype});
290 14 100       28 if ($method->{type}) {
291 11         29 $self->add_type($method->{type});
292 11         42 $set .= qq|$method->{type}\->(\$val); |;
293             }
294 14 100       35 my $merge = $method->{merge} ? '|| $first' : '';
295             $set .= qq|
296             if (defined \$self->{$name}) {
297             my \$recurse;
298             \$recurse = sub {
299             my (\$first, \$second) = \@_;
300             my \$fref = Scalar::Util::reftype(\$first) \|\| "";
301             my \$sref = Scalar::Util::reftype(\$second) \|\| "";
302             if (\$fref eq 'ARRAY' && \$sref eq 'ARRAY') {
303             for (my \$i = 0; \$i < scalar \@{ \$first }; \$i++) {
304             my (\$f, \$s) = (\$first->[0], \$second->[0]);
305             \$second->[\$i] = \$recurse->(\$first->[\$i], \$second->[\$i]);
306             }
307             } elsif (\$fref eq 'HASH' && \$sref eq 'HASH') {
308             my \@keys = (keys \%{ \$first }, keys \%{ \$second });
309             for my \$key ( \@keys ) {
310             \$second->{\$key} = \$recurse->(\$first->{\$key}, \$second->{\$key});
311             }
312             \$second = bless \$second, ref \$first;
313             }
314             return \$second${merge};
315             };
316             \$val = \$recurse->(\$self->{$name}, \$val);
317             }
318 14 100       30 | if ($method->{nested});
319 14         22 $set .= qq|
320             \$self->{$name} = \$val;
321             return \$self->{$name};
322             |;
323 14         41 $self->meta->{ q|set_| . $name } = $set;
324             }
325 58 100       113 if ( $method->{ref} ) {
326 1         3 $self->meta->{ q|ref_| . $name }
327             = qq|return ref \$_[0]->{$name};|;
328             }
329 58 100       107 if ( $method->{reftype} ) {
330 1         2 $self->meta->{ q|reftype_| . $name }
331             = qq|return Scalar::Util::reftype \$_[0]->{$name};|;
332             }
333 58 100       106 if ( exists $method->{default} ) {
334 24         52 $self->default->{ $name } = $method->{default};
335             }
336 58 100       94 unless ($method->{code}) {
337 55         87 $method->{code} = qq|return \$_[0]->{$name}|;
338             }
339 58         85 $self->meta->{ $name } = $method->{code};
340 58         151 return $self;
341             }
342              
343             sub build {
344 24     24 1 38 my ($self) = @_;
345              
346 24         52 $self->meta->{new} = $self->add_new( $self->default );
347              
348 24         66 my $class = sprintf q|%s::%s|, $self->{object_name}, $UNIQUE++;
349 24         30 my @methods;
350 24         32 for my $method ( keys %{ $self->meta } ) {
  24         37  
351             push @methods, sprintf q|sub %s { %s }|, $method,
352 128         179 $self->meta->{$method};
353             }
354             my $c = sprintf(
355             q|
356             package %s;
357             use Scalar::Util qw//;
358             use %s qw/%s/;
359             %s
360             1;
361 24         60 |, $class, $self->type_library, join(" ", keys %{$self->types}), join( "\n", @methods) );
  24         59  
362            
363 24 100   14   2185 eval $c;
  22 100   16   236  
  23 100   14   1409  
  18 100   13   302  
  16 100   11   4634  
  34 100   10   778779  
  46 100   5   1739  
  9 100   4   61  
  9 100   7   19  
  15 100   11   164  
  13     18   45  
  12     3   169  
  10     15   508  
  7     14   49  
  5     8   25  
  6     7   58  
  7     7   32  
  7     6   19  
  7     5   310  
  3     23   177  
  2     35   4  
  2     0   19  
  2     0   8  
  3     1   8  
  5         29  
  33         44  
  33         79  
  33         164  
  32         89  
  6         13  
  15         21  
  16         28  
  21         36  
  8         14  
  8         16  
  8         93  
  7         10  
  17         50  
  7         14  
  33         63  
  5         11  
  5         14  
  5         19  
  5         124  
  1         2  
  1         10  
  1         4  
  2         5  
  3         11  
  3         8  
  3         9  
  3         64  
  3         7  
  2         8  
  4         16  
  3         8  
  3         21  
  3         9  
  3         14  
  2         64  
  1         2  
  1         4  
  1         4  
  2         3  
  2         7  
  3         11  
  3         11  
  2         69  
  1         2  
  2         8  
  2         9  
  3         11  
  3         15  
  2         15  
  1         15  
  1         74  
  1         3  
  1         9  
  1         4  
  1         5  
  1         5  
  0         0  
  0         0  
  0         0  
  1         22  
  11         13  
  11         19  
  11         35  
  11         34  
  0         0  
  0         0  
  0         0  
  0         0  
  3         4  
  3         16  
  3         7  
  3         4  
  10         15  
  3         9  
  11         23  
  1         2  
  1         6  
  1         15  
  1         8  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         3  
  4         8  
  4         10  
  4         9  
  4         27  
  0         0  
  0         0  
  0         0  
  0         0  
  1         1  
  1         3  
  1         3  
  1         2  
  3         20  
  1         3  
  4         11  
364 24 100       79 if ($@) {
365 1         6 die $@;
366             }
367 23         532 return $class->new;
368             }
369              
370             sub stringify_struct {
371 71     71 1 729 my ( $self, $struct ) = @_;
372 82 100       465 return 'undefined' unless defined $struct;
373 67 100       195 $struct = ref $struct ? aDump($struct) : "'$struct'";
374 67         334 return $struct;
375             }
376              
377             sub add_type {
378 16     30 1 588 my ($self, $value) = @_;
379 15 100 100     70 if ( ! defined $value || ref $value ) {
380 5 100       18 $value = defined $value ? $value : 'undef';
381 5         44 die qq{Str: invalid value $value for method push_type};
382             }
383 15         40 $self->{types}->{$value}++;
384             }
385              
386             sub identify_type {
387 19     18 1 545 my ($self, $value) = @_;
388 19         46 my $type_map = $self->type_map;
389 17         32 my $ref = ref $value;
390             return $type_map->{default}
391 26 100       99 if (! defined $value);
392             return $type_map->{$ref} ? $type_map->{$ref} : $type_map->{REF}
393 25 100       225 if ($ref);
    100          
394 33 100       437 return $type_map->{NUM} if $value =~ m/\d+\.\d+/;
395 14 100       48 return $type_map->{INT} if $value =~ m/\d+/;
396 14 100       65 return $type_map->{STRING} if $value =~ m/\w+/;
397 9         33 return $type_map->{default};
398             }
399              
400             sub aDump {
401 43     29 0 2344 my $loo = Loo->new([$_[0]]);
402 39         322 $loo->Terse(1)->Deparse(1)->Colour(0)->Indent(0);
403 38         1441 return $loo->Dump;
404             }
405              
406             1;
407              
408             __END__