File Coverage

blib/lib/Anonymous/Object.pm
Criterion Covered Total %
statement 288 308 93.5
branch 134 134 100.0
condition 30 30 100.0
subroutine 45 47 95.7
pod 18 18 100.0
total 515 537 95.9


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