File Coverage

blib/lib/Object/InsideOut/Dump.pm
Criterion Covered Total %
statement 93 105 88.5
branch 39 54 72.2
condition 7 17 41.1
subroutine 5 6 83.3
pod n/a
total 144 182 79.1


line stmt bran cond sub pod time code
1             package Object::InsideOut; {
2              
3 6     6   53 use strict;
  6         12  
  6         207  
4 6     6   32 use warnings;
  6         12  
  6         187  
5 6     6   29 no warnings 'redefine';
  6         11  
  6         8677  
6              
7             # Installs object dumper and loader methods
8             sub dump
9             {
10             my ($GBL, $call, @args) = @_;
11             push(@{$$GBL{'export'}}, 'dump');
12             $$GBL{'init'} = 1;
13              
14             *Object::InsideOut::dump = sub
15             {
16 23     23   7644 my $self = shift;
17              
18 23         48 my $d_flds = $$GBL{'dump'}{'fld'};
19              
20             # Extract field info from any :InitArgs hashes
21 23         39 while (my $pkg = shift(@{$$GBL{'dump'}{'args'}})) {
  31         109  
22 8         17 my $p_args = $$GBL{'args'}{$pkg};
23 8         15 foreach my $name (keys(%{$p_args})) {
  8         21  
24 28         46 my $val = $$p_args{$name};
25 28 100       76 next if (ref($val) ne 'HASH');
26 15 100       38 if (my $field = $$val{'_F'}) {
27 14   100     52 $$d_flds{$pkg} ||= {};
28 14 50       38 if (add_dump_field('InitArgs', $name, $field, $$d_flds{$pkg}) eq 'conflict') {
29 0         0 OIO::Code->die(
30             'message' => 'Cannot dump object',
31             'Info' => "In class '$pkg', '$name' refers to two different fields set by 'InitArgs' and '$$d_flds{$pkg}{$name}{'src'}'");
32             }
33             }
34             }
35             }
36              
37             # Must call ->dump() as an object method
38 23 50       103 if (! Scalar::Util::blessed($self)) {
39 0         0 OIO::Method->die('message' => q/'dump' called as a class method/);
40             }
41              
42             # Gather data from the object's class tree
43 23         53 my %dump;
44 23         74 my $fld_refs = $$GBL{'fld'}{'ref'};
45 23         46 my $dumpers = $$GBL{'dump'}{'dumper'};
46 23         40 my $weak = $$GBL{'fld'}{'weak'};
47 23         33 foreach my $pkg (@{$$GBL{'tree'}{'td'}{ref($self)}}) {
  23         60  
48             # Try to use a class-supplied dumper
49 51 100       153 if (my $dumper = $$dumpers{$pkg}) {
    100          
50 4         19 local $SIG{'__DIE__'} = 'OIO::trap';
51 4         18 $dump{$pkg} = $self->$dumper();
52              
53             } elsif ($$fld_refs{$pkg}) {
54             # Dump the data ourselves from all known class fields
55 43         71 my @fields = @{$$fld_refs{$pkg}};
  43         139  
56              
57             # Fields for which we have names
58 43         63 foreach my $name (keys(%{$$d_flds{$pkg}})) {
  43         110  
59 71         123 my $field = $$d_flds{$pkg}{$name}{'fld'};
60 71 100       156 if (ref($field) eq 'HASH') {
61 33 100       74 if (exists($$field{$$self})) {
62 24         59 $dump{$pkg}{$name} = $$field{$$self};
63             }
64             } else {
65 38 100       112 if (defined($$field[$$self])) {
66 32         74 $dump{$pkg}{$name} = $$field[$$self];
67             }
68             }
69 71 50 66     183 if ($$weak{$field} && exists($dump{$pkg}{$name})) {
70 1         4 Scalar::Util::weaken($dump{$pkg}{$name});
71             }
72 71         114 @fields = grep { $_ != $field } @fields;
  118         321  
73             }
74              
75             # Fields for which names are not known
76 43         97 foreach my $field (@fields) {
77 5 100       16 if (ref($field) eq 'HASH') {
78 2 50       9 if (exists($$field{$$self})) {
79 2         5 $dump{$pkg}{$field} = $$field{$$self};
80             }
81             } else {
82 3 50       17 if (defined($$field[$$self])) {
83 3         11 $dump{$pkg}{$field} = $$field[$$self];
84             }
85             }
86 5 0 33     19 if ($$weak{$field} && exists($dump{$pkg}{$field})) {
87 0         0 Scalar::Util::weaken($dump{$pkg}{$field});
88             }
89             }
90             }
91             }
92              
93             # Package up the object's class and its data
94 23         101 my $output = [ ref($self), \%dump ];
95              
96             # Create a string version of dumped data if arg is true
97 23 100       95 if ($_[0]) {
98 8         2660 require Data::Dumper;
99 8         28102 local $Data::Dumper::Indent = 1;
100 8         29 $output = Data::Dumper::Dumper($output);
101 8         670 chomp($output);
102 8         73 $output =~ s/^\$VAR1 = //; # Remove leading '$VAR1 = '
103 8         43 $output =~ s/;$//s; # Remove trailing semi-colon
104             }
105              
106             # Done - send back the dumped data
107 23         468 return ($output);
108             };
109              
110              
111             *Object::InsideOut::pump = sub
112             {
113 9     9   7257 my $input = shift;
114              
115             # Check usage
116 9 50       28 if ($input) {
117 9 100       37 if ($input eq 'Object::InsideOut') {
    50          
118 5         9 $input = shift; # Called as a class method
119              
120             } elsif (Scalar::Util::blessed($input)) {
121 0         0 OIO::Method->die('message' => q/'pump' called as an object method/);
122             }
123             }
124              
125             # Must have an arg
126 9 50       24 if (! $input) {
127 0         0 OIO::Args->die('message' => 'Missing argument to pump()');
128             }
129              
130             # Convert string input to array ref, if needed
131 9 100       25 if (! ref($input)) {
132 1         2 my @errs;
133 1     0   10 local $SIG{'__WARN__'} = sub { push(@errs, @_); };
  0         0  
134              
135 1         3 my $array_ref;
136 1         97 eval "\$array_ref = $input";
137              
138 1 50 33     10 if ($@ || @errs) {
139 0   0     0 my ($err) = split(/ at /, $@ || join(" | ", @errs));
140 0         0 OIO::Args->die(
141             'message' => 'Failure converting dump string back to hash ref',
142             'Error' => $err,
143             'Arg' => $input);
144             }
145              
146 1         7 $input = $array_ref;
147             }
148              
149             # Check input
150 9 50       36 if (ref($input) ne 'ARRAY') {
151 0         0 OIO::Args->die('message' => 'Argument to pump() is not an array ref');
152             }
153              
154             # Extract class name and object data
155 9         14 my ($class, $dump) = @{$input};
  9         26  
156 9 50 33     59 if (! defined($class) || ref($dump) ne 'HASH') {
157 0         0 OIO::Args->die('message' => 'Argument to pump() is invalid');
158             }
159              
160             # Create a new 'bare' object
161 9         31 my $self = _obj($class);
162              
163             # Store object data
164 9         14 foreach my $pkg (keys(%{$dump})) {
  9         34  
165 14 50       47 if (! exists($$GBL{'tree'}{'td'}{$pkg})) {
166 0         0 OIO::Args->die('message' => "Unknown class: $pkg");
167             }
168 14         26 my $data = $$dump{$pkg};
169              
170             # Try to use a class-supplied pumper
171 14 100       37 if (my $pumper = $$GBL{'dump'}{'pumper'}{$pkg}) {
172 2         7 local $SIG{'__DIE__'} = 'OIO::trap';
173 2         9 $self->$pumper($data);
174              
175             } else {
176             # Pump in the data ourselves
177 12         34 foreach my $fld_name (keys(%{$data})) {
  12         29  
178 20         35 my $value = $$data{$fld_name};
179 20 100       56 if (my $field = $$GBL{'dump'}{'fld'}{$pkg}{$fld_name}{'fld'}) {
180 18         48 $self->set($field, $value);
181             } else {
182 2 50       15 if ($fld_name =~ /^(?:HASH|ARRAY)/) {
183 2         35 OIO::Args->die(
184             'message' => "Unnamed field encounted in class '$pkg'",
185             'Arg' => "$fld_name => $value");
186             } else {
187 0         0 OIO::Args->die(
188             'message' => "Unknown field name for class '$pkg': $fld_name");
189             }
190             }
191             }
192             }
193             }
194              
195             # Done - return the object
196 7         1392 return ($self);
197             };
198              
199              
200             # Do the original call
201             @_ = @args;
202             goto &$call;
203             }
204              
205             } # End of package's lexical scope
206              
207              
208             # Ensure correct versioning
209             ($Object::InsideOut::VERSION eq '4.05')
210             or die("Version mismatch\n");
211              
212             # EOF