File Coverage

blib/lib/Anonymous/Object.pm
Criterion Covered Total %
statement 307 323 95.0
branch 135 138 97.8
condition 31 32 96.8
subroutine 46 48 95.8
pod 19 19 100.0
total 538 560 96.0


line stmt bran cond sub pod time code
1             package Anonymous::Object;
2 11     11   528729 use strict;
  11         116  
  11         241  
3 11     11   66 use warnings;
  11         17  
  11         263  
4 11     11   5266 use Data::Dumper;
  11         51244  
  11         783  
5             our $VERSION = 0.07;
6              
7             our $UNIQUE;
8             BEGIN {
9 11     11   35 $Data::Dumper::Deparse = 1;
10 11         20347 $UNIQUE = 0;
11             }
12              
13             sub new {
14 44 100   44 1 20675 my ( $cls, %args ) = ( shift(), scalar @_ == 1 ? %{ $_[0] } : @_ );
  39         139  
15 44         154 my $self = bless {}, $cls;
16 44         622 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         294 for my $accessor ( keys %accessors ) {
38             my $param = defined $args{$accessor}
39             ? $args{$accessor}
40 243 100       479 : $accessors{$accessor}->{default};
41             my $value
42             = $self->$accessor( $args{"build_$accessor"}
43 243 100       979 ? $args{"build_$accessor"}->( $self, $param )
44             : $param );
45 241 100       422 unless ( defined $value ) {
46 3         16 die "$accessor accessor is required";
47             }
48             }
49 41         283 return $self;
50             }
51              
52             sub clean {
53 23     23 1 41 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         129 });
59             }
60              
61             sub object_name {
62 44     44 1 719 my ($self, $value) = @_;
63 42 100       103 if ( defined $value ) {
64 41 100       79 if ( ref $value ) {
65 1         9 die qq{Str: invalid value $value for accessor object_name}
66             }
67 40         59 $self->{object_name} = $value;
68             }
69 41         73 return $self->{object_name};
70             }
71              
72             sub default {
73 95     97 1 567 my ( $self, $value ) = @_;
74 95 100       190 if ( defined $value ) {
75 47 100 100     118 if ( ( ref($value) || "" ) ne "HASH" ) {
76 1         7 die qq{HashRef: invalid value $value for accessor default};
77             }
78 46         86 $self->{default} = $value;
79             }
80 94         172 return $self->{default};
81             }
82              
83             sub meta {
84 318     318 1 1308 my ( $self, $value ) = @_;
85 318 100       447 if ( defined $value ) {
86 44 100 100     372 if ( ( ref($value) || "" ) ne "HASH" ) {
87 4         34 die qq{HashRef: invalid value $value for accessor meta};
88             }
89 40         81 $self->{meta} = $value;
90             }
91 314         740 return $self->{meta};
92             }
93              
94             sub types {
95 66     66 1 1031 my ( $self, $value ) = @_;
96 66 100       187 if ( defined $value ) {
97 42 100 100     112 if ( ( ref($value) || "" ) ne "HASH" ) {
98 2         13 die qq{HashRef: invalid value $value for accessor types};
99             }
100 40         61 $self->{types} = $value;
101             }
102 64         289 return $self->{types};
103             }
104              
105             sub type_library {
106 64     64 1 472 my ($self, $value) = @_;
107 64 100       113 if ( defined $value ) {
108 40 100       77 if ( ref $value ) {
109 1         8 die qq{Str: invalid value $value for accessor type_library}
110             }
111 39         65 $self->{type_library} = $value;
112             }
113 63         103 return $self->{type_library};
114             }
115              
116             sub type_map {
117 56     56 1 476 my ( $self, $value ) = @_;
118 56 100       137 if ( defined $value ) {
119 41 100 100     107 if ( ( ref($value) || "" ) ne "HASH" ) {
120 1         6 die qq{HashRef: invalid value $value for accessor type_map};
121             }
122 40         67 $self->{type_map} = $value;
123             }
124 55         160 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     5 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         2 $self = $self->clean();
135 1         5 $self->default({});
136 1         1 for my $key ( keys %{$hash} ) {
  1         3  
137 2         12 $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         3 my $build = $self->build;
147 1         2 for my $key ( keys %{$hash} ) {
  1         5  
148 2         5 my $meth = "set_${key}";
149 2         34 $build->$meth($hash->{$key});
150             }
151 1         16 return $build;
152             }
153              
154             sub hash_to_object {
155 8     8 1 824 my ( $self, $hash, %accessors ) = @_;
156 8 100 100     39 if ( ( ref($hash) || "" ) ne "HASH" ) {
157 3 100       8 $hash = defined $hash ? $hash : 'undef';
158 3         53 die
159             qq{HashRef: invalid value $hash for variable \$hash in method hash_to_object};
160             }
161 5         16 $self = $self->clean();
162 5         16 $self->default($hash);
163 5         5 for my $key ( keys %{$hash} ) {
  5         13  
164 30         61 $self->add_method(
165             {
166             name => $key,
167             %accessors
168             }
169             );
170             }
171 5         11 return $self->build;
172             }
173              
174             sub hash_to_nested_object {
175 17     17 1 776 my ( $self, $hash, %accessors ) = @_;
176 17 100 100     59 if ( ( ref($hash) || "" ) ne "HASH" ) {
177 2 100       4 $hash = defined $hash ? $hash : 'undef';
178 2         13 die
179             qq{HashRef: invalid value $hash for variable \$hash in method hash_to_object};
180             }
181 15         33 $self = $self->clean();
182 15         26 for my $key ( keys %{$hash} ) {
  15         41  
183 22         29 my $val = $hash->{$key};
184 22   100     52 my $ref = ref $val || "";
185 22 100       48 if ($ref eq 'HASH') {
    100          
186 8         23 $val = $self->hash_to_nested_object(
187             $val,
188             %accessors
189             );
190             } elsif ($ref eq 'ARRAY') {
191 4         16 $val = $self->array_to_nested_object(
192             $val,
193             %accessors
194             );
195             }
196             $self->add_method(
197             {
198 22         102 name => $key,
199             default => $val,
200             nested => 1,
201             %accessors
202             }
203             );
204             }
205 15         32 return $self->build;
206             }
207              
208             sub array_to_nested_object {
209 8     8 1 1148 my ( $self, $array, %accessors ) = @_;
210 8 100 100     40 if ( ( ref($array) || "" ) ne "ARRAY" ) {
211 2 100       6 $array = defined $array ? $array : 'undef';
212 2         13 die
213             qq{ArrayRef: invalid value $array for variable \$array in method array_to_object};
214             }
215 6         12 for (my $i = 0; $i < scalar @{$array}; $i++) {
  18         45  
216 12         20 my $val = $array->[$i];
217 12   100     29 my $ref = ref $val || "";
218 12 100       31 if ($ref eq 'HASH') {
    100          
219 5         22 $val = $self->hash_to_nested_object(
220             $val,
221             %accessors
222             );
223             } elsif ($ref eq 'ARRAY') {
224 1         4 $val = $self->array_to_nested_object(
225             $val,
226             %accessors
227             );
228             }
229 12         69 $array->[$i] = $val;
230             }
231 6         17 return $array;
232             }
233              
234             sub add_new {
235 28     28 1 1045 my ( $self, $new ) = @_;
236 28 100 100     101 if ( ( ref($new) || "" ) ne "HASH" ) {
237 4 100       9 $new = defined $new ? $new : 'undef';
238 4         32 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         112 map { sprintf q|%s => %s|, $_, $self->stringify_struct( $new->{$_} ) }
244 24         38 keys %{$new};
  24         68  
245              
246             }
247              
248             sub add_methods {
249 5     5 1 1852 my ( $self, $methods ) = @_;
250 5 100 100     28 if ( ( ref($methods) || "" ) ne "ARRAY" ) {
251 4 100       11 $methods = defined $methods ? $methods : 'undef';
252 4         27 die
253             qq{ArrayRef: invalid value $methods for variable \$methods in method add_methods};
254             }
255 1         3 for my $method ( @{$methods} ) {
  1         3  
256 1         4 $self->add_method($method);
257             }
258 1         3 return $self;
259             }
260              
261             sub add_method {
262 64     64 1 2194 my ( $self, $method ) = @_;
263 64 100 100     159 if ( ( ref($method) || "" ) ne "HASH" ) {
264 4 100       10 $method = defined $method ? $method : 'undef';
265 4         30 die
266             qq{HashRef: invalid value $method for variable \$method in method add_method};
267             }
268 60 100 100     202 if ( ( ! defined $method->{name} || ref($method->{name}) ) ) {
269 2 100       5 $method->{name} = defined $method->{name} ? $method->{name} : 'undef';
270 2         14 die
271             qq{Str: invalid value $method->{name} for variable \$method->{name} in method add_method};
272             }
273              
274 58         87 my $name = $method->{name};
275 58 100       92 if ( $method->{clearer} ) {
276 21         59 $self->meta->{ q|clear_| . $name }
277             = qq|return delete \$_[0]->{$name};|;
278             }
279 58 100       101 if ( $method->{predicate} ) {
280 1         5 $self->meta->{ q|has_| . $name }
281             = qq|return exists \$_[0]->{$name};|;
282             }
283 58 100       100 if ( $method->{get} ) {
284 1         2 $self->meta->{ q|get_| . $name }
285             = qq|return \$_[0]->{$name};|;
286             }
287 58 100       87 if ( $method->{set} ) {
288 14         19 my $set = q|my ($self, $val) = @_; |;
289             $method->{type} = $self->identify_type($method->{default})
290 14 100       81 if ($method->{autotype});
291 14 100       32 if ($method->{type}) {
292 11         28 $self->add_type($method->{type});
293 11         19 $set .= qq|$method->{type}\->(\$val); |;
294             }
295 14 100       33 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       38 | if ($method->{nested});
320 14         45 $set .= qq|
321             \$self->{$name} = \$val;
322             return \$self->{$name};
323             |;
324 14         24 $self->meta->{ q|set_| . $name } = $set;
325             }
326 58 100       93 if ( $method->{ref} ) {
327 1         2 $self->meta->{ q|ref_| . $name }
328             = qq|return ref \$_[0]->{$name};|;
329             }
330 58 100       101 if ( $method->{reftype} ) {
331 1         4 $self->meta->{ q|reftype_| . $name }
332             = qq|return Scalar::Util::reftype \$_[0]->{$name};|;
333             }
334 58 100       104 if ( exists $method->{default} ) {
335 24         36 $self->default->{ $name } = $method->{default};
336             }
337 58 100       179 unless ($method->{code}) {
338 55         114 $method->{code} = qq|return \$_[0]->{$name}|;
339             }
340 58         85 $self->meta->{ $name } = $method->{code};
341 58         125 return $self;
342             }
343              
344             sub build {
345 24     24 1 37 my ($self) = @_;
346              
347 24         94 $self->meta->{new} = $self->add_new( $self->default );
348              
349 24         92 my $class = sprintf q|%s::%s|, $self->{object_name}, $UNIQUE++;
350 24         35 my @methods;
351 24         37 for my $method ( keys %{ $self->meta } ) {
  24         39  
352             push @methods, sprintf q|sub %s { %s }|, $method,
353 128         185 $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         67 |, $class, $self->type_library, join(" ", keys %{$self->types}), join( "\n", @methods) );
  24         80  
363            
364 24 100   13   1685 eval $c;
  19 100   16   159  
  18 100   15   844  
  17 100   12   423  
  33 100   11   8224  
  46 100   11   575770  
  12 100   4   796  
  9 100   5   53  
  10 100   5   51  
  15 100   5   136  
  15     10   64  
  13     18   163  
  12     3   467  
  6     15   25  
  8     14   94  
  10     8   75  
  10     7   36  
  7     7   13  
  7     6   230  
  3     5   105  
  2     23   4  
  2     35   10  
  2     0   4  
  7     0   15  
  4     1   25  
  3         12  
  3         10  
  3         84  
  1         1  
  1         7  
  1         4  
  2         5  
  2         6  
  2         6  
  2         7  
  2         73  
  1         9  
  1         13  
  2         10  
  3         8  
  3         28  
  3         10  
  3         18  
  2         66  
  1         2  
  1         5  
  1         3  
  2         5  
  2         6  
  2         6  
  2         5  
  2         64  
  1         3  
  2         21  
  12         19  
  13         43  
  13         30  
  13         60  
  2         4  
  2         100  
  1         3  
  1         6  
  4         9  
  5         12  
  5         21  
  6         15  
  13         32  
  5         81  
  12         23  
  10         20  
  10         25  
  11         25  
  11         39  
  1         3  
  1         5  
  1         43  
  1         2  
  3         6  
  3         7  
  3         7  
  3         10  
  7         13  
  2         7  
  9         20  
  31         40  
  31         58  
  31         52  
  31         71  
  5         7  
  14         20  
  14         32  
  19         28  
  6         7  
  6         9  
  6         11  
  6         7  
  16         25  
  6         16  
  31         63  
  2         4  
  2         10  
  2         15  
  2         12  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         6  
  3         6  
  3         10  
  3         10  
  3         14  
  0         0  
  0         0  
  0         0  
  0         0  
  1         1  
  1         3  
  1         4  
  1         3  
  2         12  
  1         3  
  3         9  
365 24 100       85 if ($@) {
366 1         4 die $@;
367             }
368 23         531 return $class->new;
369             }
370              
371             sub stringify_struct {
372 66     66 1 485 my ( $self, $struct ) = @_;
373 77 100       359 return 'undefined' unless defined $struct;
374 62 100       187 $struct = ref $struct ? Dumper $struct : "'$struct'";
375 62         4644 $struct =~ s/\$VAR1 = //;
376 58         5276 $struct =~ s/\s*\n*\s*package Module\:\:Generate\;|use warnings\;|use strict\;//g;
377 57         150 $struct =~ s/{\s*\n*/{/;
378 58         121 $struct =~ s/;$//;
379 58         312 return $struct;
380             }
381              
382             sub add_type {
383 16     30 1 425 my ($self, $value) = @_;
384 17 100 100     102 if ( ! defined $value || ref $value ) {
385 6 100       48 $value = defined $value ? $value : 'undef';
386 4         21 die qq{Str: invalid value $value for method push_type};
387             }
388 19         59 $self->{types}->{$value}++;
389             }
390              
391             sub identify_type {
392 25     18 1 548 my ($self, $value) = @_;
393 35         331 my $type_map = $self->type_map;
394 19         45 my $ref = ref $value;
395             return $type_map->{default}
396 20 100       57 if (! defined $value);
397             return $type_map->{$ref} ? $type_map->{$ref} : $type_map->{REF}
398 18 100       75 if ($ref);
    100          
399 20 100       1391 return $type_map->{NUM} if $value =~ m/\d+\.\d+/;
400 17 100       570 return $type_map->{INT} if $value =~ m/\d+/;
401 13 100       128 return $type_map->{STRING} if $value =~ m/\w+/;
402 9         97 return $type_map->{default};
403             }
404              
405              
406             1;
407              
408             __END__