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   26 use strict;
  6         7  
  6         171  
4 6     6   19 use warnings;
  6         7  
  6         150  
5 6     6   15 no warnings 'redefine';
  6         6  
  6         5884  
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   2722 my $self = shift;
17              
18 23         34 my $d_flds = $$GBL{'dump'}{'fld'};
19              
20             # Extract field info from any :InitArgs hashes
21 23         19 while (my $pkg = shift(@{$$GBL{'dump'}{'args'}})) {
  31         87  
22 8         9 my $p_args = $$GBL{'args'}{$pkg};
23 8         8 foreach my $name (keys(%{$p_args})) {
  8         18  
24 28         26 my $val = $$p_args{$name};
25 28 100       51 next if (ref($val) ne 'HASH');
26 15 100       26 if (my $field = $$val{'_F'}) {
27 14   100     34 $$d_flds{$pkg} ||= {};
28 14 50       34 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       73 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         24 my %dump;
44 23         25 my $fld_refs = $$GBL{'fld'}{'ref'};
45 23         23 my $dumpers = $$GBL{'dump'}{'dumper'};
46 23         26 my $weak = $$GBL{'fld'}{'weak'};
47 23         14 foreach my $pkg (@{$$GBL{'tree'}{'td'}{ref($self)}}) {
  23         50  
48             # Try to use a class-supplied dumper
49 51 100       133 if (my $dumper = $$dumpers{$pkg}) {
    100          
50 4         11 local $SIG{'__DIE__'} = 'OIO::trap';
51 4         11 $dump{$pkg} = $self->$dumper();
52              
53             } elsif ($$fld_refs{$pkg}) {
54             # Dump the data ourselves from all known class fields
55 43         29 my @fields = @{$$fld_refs{$pkg}};
  43         66  
56              
57             # Fields for which we have names
58 43         34 foreach my $name (keys(%{$$d_flds{$pkg}})) {
  43         71  
59 71         64 my $field = $$d_flds{$pkg}{$name}{'fld'};
60 71 100       87 if (ref($field) eq 'HASH') {
61 33 100       56 if (exists($$field{$$self})) {
62 24         50 $dump{$pkg}{$name} = $$field{$$self};
63             }
64             } else {
65 38 100       59 if (defined($$field[$$self])) {
66 32         61 $dump{$pkg}{$name} = $$field[$$self];
67             }
68             }
69 71 50 66     122 if ($$weak{$field} && exists($dump{$pkg}{$name})) {
70 1         2 Scalar::Util::weaken($dump{$pkg}{$name});
71             }
72 71         53 @fields = grep { $_ != $field } @fields;
  118         205  
73             }
74              
75             # Fields for which names are not known
76 43         62 foreach my $field (@fields) {
77 5 100       9 if (ref($field) eq 'HASH') {
78 2 50       5 if (exists($$field{$$self})) {
79 2         3 $dump{$pkg}{$field} = $$field{$$self};
80             }
81             } else {
82 3 50       7 if (defined($$field[$$self])) {
83 3         7 $dump{$pkg}{$field} = $$field[$$self];
84             }
85             }
86 5 0 33     14 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         59 my $output = [ ref($self), \%dump ];
95              
96             # Create a string version of dumped data if arg is true
97 23 100       40 if ($_[0]) {
98 8         2233 require Data::Dumper;
99 8         17220 local $Data::Dumper::Indent = 1;
100 8         21 $output = Data::Dumper::Dumper($output);
101 8         536 chomp($output);
102 8         79 $output =~ s/^\$VAR1 = //; # Remove leading '$VAR1 = '
103 8         26 $output =~ s/;$//s; # Remove trailing semi-colon
104             }
105              
106             # Done - send back the dumped data
107 23         292 return ($output);
108             };
109              
110              
111             *Object::InsideOut::pump = sub
112             {
113 9     9   4376 my $input = shift;
114              
115             # Check usage
116 9 50       25 if ($input) {
117 9 100       29 if ($input eq 'Object::InsideOut') {
    50          
118 5         5 $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       18 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       24 if (! ref($input)) {
132 1         3 my @errs;
133 1     0   5 local $SIG{'__WARN__'} = sub { push(@errs, @_); };
  0         0  
134              
135 1         2 my $array_ref;
136 1         63 eval "\$array_ref = $input";
137              
138 1 50 33     8 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         4 $input = $array_ref;
147             }
148              
149             # Check input
150 9 50       18 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         8 my ($class, $dump) = @{$input};
  9         15  
156 9 50 33     44 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         27 my $self = _obj($class);
162              
163             # Store object data
164 9         12 foreach my $pkg (keys(%{$dump})) {
  9         23  
165 13 50       557 if (! exists($$GBL{'tree'}{'td'}{$pkg})) {
166 0         0 OIO::Args->die('message' => "Unknown class: $pkg");
167             }
168 13         15 my $data = $$dump{$pkg};
169              
170             # Try to use a class-supplied pumper
171 13 100       52 if (my $pumper = $$GBL{'dump'}{'pumper'}{$pkg}) {
172 2         5 local $SIG{'__DIE__'} = 'OIO::trap';
173 2         6 $self->$pumper($data);
174              
175             } else {
176             # Pump in the data ourselves
177 11         9 foreach my $fld_name (keys(%{$data})) {
  11         28  
178 18         20 my $value = $$data{$fld_name};
179 18 100       38 if (my $field = $$GBL{'dump'}{'fld'}{$pkg}{$fld_name}{'fld'}) {
180 16         41 $self->set($field, $value);
181             } else {
182 2 50       13 if ($fld_name =~ /^(?:HASH|ARRAY)/) {
183 2         34 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         18 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.03')
210             or die("Version mismatch\n");
211              
212             # EOF