File Coverage

blib/lib/Anonymous/Object.pm
Criterion Covered Total %
statement 305 323 94.4
branch 135 138 97.8
condition 31 32 96.8
subroutine 46 48 95.8
pod 19 19 100.0
total 536 560 95.7


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