File Coverage

blib/lib/Object/InsideOut/Util.pm
Criterion Covered Total %
statement 71 174 40.8
branch 21 100 21.0
condition 6 24 25.0
subroutine 11 14 78.5
pod 0 7 0.0
total 109 319 34.1


line stmt bran cond sub pod time code
1             package Object::InsideOut::Util; {
2              
3             require 5.006;
4              
5 53     53   401 use strict;
  53         113  
  53         3272  
6 53     53   310 use warnings;
  53         99  
  53         1768  
7 53     53   267 use Config;
  53         99  
  53         3906  
8              
9             our $VERSION = '4.05';
10             $VERSION = eval $VERSION;
11              
12 53     53   25644 use Object::InsideOut::Metadata 4.05;
  53         1221  
  53         295  
13              
14             ### Module Initialization ###
15              
16             BEGIN {
17             # 1. Install our own 'no-op' version of Internals::SvREADONLY for Perl < 5.8
18 53 50   53   1467 if (! Internals->can('SvREADONLY')) {
19 0         0 *Internals::SvREADONLY = sub (\$;$) { return; };
  0         0  
20             }
21              
22             # Import 'share' and 'bless' if threads::shared
23 53 0 33     4641 if ($Config::Config{useithreads} && $threads::shared::threads_shared) {
24 0         0 import threads::shared;
25             }
26             }
27              
28              
29             # 2. Export requested subroutines
30             sub import
31             {
32 54     54   154 my $class = shift; # Not used
33              
34             # Exportable subroutines
35 54         121 my %EXPORT_OK;
36 54         202 @EXPORT_OK{qw(create_object hash_re is_it make_shared shared_copy)} = undef;
37              
38             # Handle entries in the import list
39 54         139 my $caller = caller();
40 54         90 my %meta;
41 54         249 while (my $sym = shift) {
42 213 50       680 if (exists($EXPORT_OK{lc($sym)})) {
43             # Export subroutine name
44 53     53   371 no strict 'refs';
  53         99  
  53         85057  
45 213         307 *{$caller.'::'.$sym} = \&{lc($sym)};
  213         979  
  213         496  
46 213         907 $meta{$sym}{'hidden'} = 1;
47             } else {
48 0         0 OIO::Code->die(
49             'message' => "Symbol '$sym' is not exported by Object::InsideOut::Util",
50             'Info' => 'Exportable symbols: ' . join(' ', keys(%EXPORT_OK)),
51             'ignore_package' => 'Object::InsideOut::Util');
52             }
53             }
54 54 50       232 if (%meta) {
55 54         216 add_meta($caller, \%meta);
56             }
57             }
58              
59              
60             ### Subroutines ###
61              
62             # Returns a blessed (optional), readonly (Perl 5.8) anonymous scalar reference
63             # containing either:
64             # the value returned by a user-specified subroutine; or
65             # a user-supplied scalar
66             sub create_object
67             {
68 233     233 0 560 my ($class, $id) = @_;
69              
70             # Create the object from an anonymous scalar reference
71 233         477 my $obj = \do{ my $scalar; };
  233         525  
72              
73             # Set the scalar equal to ...
74 233 50       709 if (my $ref_type = ref($id)) {
75 233 50       586 if ($ref_type eq 'CODE') {
76             # ... the value returned by the user-specified subroutine
77 233         2018 local $SIG{__DIE__} = 'OIO::trap';
78 233         919 $$obj = $id->($class);
79             } else {
80             # Complain if something other than code ref
81 0         0 OIO::Args->die(
82             'message' => q/2nd argument to create_object() is not a code ref or scalar/,
83             'Usage' => 'create_object($class, $scalar) or create_object($class, $code_ref, ...)',
84             'ignore_package' => 'Object::InsideOut::Util');
85             }
86              
87             } else {
88             # ... the user-supplied scalar
89 0         0 $$obj = $id;
90             }
91              
92             # Bless the object into the specified class (optional)
93 233 50       718 if ($class) {
94 233         521 bless($obj, $class);
95             }
96              
97             # Make the object 'readonly' (Perl 5.8)
98 233 50       1839 Internals::SvREADONLY($$obj, 1) if ($] >= 5.008003);
99              
100             # Done - return the object
101 233         598 return ($obj);
102             }
103              
104              
105             # Make a thread-shared version of a complex data structure or object
106             sub make_shared
107             {
108 0     0 0 0 my $in = shift;
109 0   0     0 my $cloned = shift || {};
110              
111             # If not sharing or already thread-shared, then just return the input
112 0 0 0     0 if (! ref($in) ||
      0        
113             ! $Config::Config{useithreads} ||
114             ! $threads::threads ||
115             ! $threads::shared::threads_shared ||
116             threads::shared::is_shared($in))
117             {
118 0         0 return ($in);
119             }
120              
121             # Check for previously cloned references
122             # (this takes care of circular refs as well)
123 0         0 my $addr = Scalar::Util::refaddr($in);
124 0 0       0 if (exists($cloned->{$addr})) {
125             # Return the already existing clone
126 0         0 return $cloned->{$addr};
127             }
128              
129             # Make copies of array, hash and scalar refs
130 0         0 my $out;
131 0         0 my $ref_type = Scalar::Util::reftype($in);
132              
133             # Copy an array ref
134 0 0       0 if ($ref_type eq 'ARRAY') {
    0          
    0          
    0          
135             # Make empty shared array ref
136 0         0 $out = &threads::shared::share([]);
137             # Add to clone checking hash
138 0         0 $cloned->{$addr} = $out;
139             # Recursively copy and add contents
140 0         0 push(@$out, map { make_shared($_, $cloned) } @$in);
  0         0  
141             }
142              
143             # Copy a hash ref
144             elsif ($ref_type eq 'HASH') {
145             # Make empty shared hash ref
146 0         0 $out = &threads::shared::share({});
147             # Add to clone checking hash
148 0         0 $cloned->{$addr} = $out;
149             # Recursively copy and add contents
150 0         0 foreach my $key (keys(%{$in})) {
  0         0  
151 0         0 $out->{$key} = make_shared($in->{$key}, $cloned);
152             }
153             }
154              
155             # Copy a scalar ref
156             elsif ($ref_type eq 'SCALAR') {
157 0         0 $out = \do{ my $scalar = $$in; };
  0         0  
158 0         0 threads::shared::share($out);
159             # Add to clone checking hash
160 0         0 $cloned->{$addr} = $out;
161             }
162              
163             # Copy of a ref of a ref
164             elsif ($ref_type eq 'REF') {
165             # Special handling for $x = \$x
166 0 0       0 if ($addr == Scalar::Util::refaddr($$in)) {
167 0         0 $out = \$out;
168 0         0 threads::shared::share($out);
169 0         0 $cloned->{$addr} = $out;
170             } else {
171 0         0 my $tmp;
172 0         0 $out = \$tmp;
173 0         0 threads::shared::share($out);
174             # Add to clone checking hash
175 0         0 $cloned->{$addr} = $out;
176             # Recursively copy and add contents
177 0         0 $tmp = make_shared($$in, $cloned);
178             }
179              
180             } else {
181             # Just return anything else
182             # NOTE: This will end up generating an error
183 0         0 return ($in);
184             }
185              
186             # Return blessed copy, if applicable
187 0 0       0 if (my $class = Scalar::Util::blessed($in)) {
188 0         0 bless($out, $class);
189             }
190              
191             # Clone READONLY flag
192 0 0       0 if ($ref_type eq 'SCALAR') {
193 0 0       0 if (Internals::SvREADONLY($$in)) {
194 0 0       0 Internals::SvREADONLY($$out, 1) if ($] >= 5.008003);
195             }
196             }
197 0 0       0 if (Internals::SvREADONLY($in)) {
198 0 0       0 Internals::SvREADONLY($out, 1) if ($] >= 5.008003);
199             }
200              
201             # Return clone
202 0         0 return ($out);
203             }
204              
205              
206             # Make a copy of a complex data structure or object.
207             # If thread-sharing, then make the copy thread-shared.
208             sub shared_copy
209             {
210 0 0 0 0 0 0 return (($Config::Config{useithreads} && $threads::shared::threads_shared)
211             ? clone_shared(@_) : clone(@_));
212             }
213              
214              
215             # Recursively make a copy of a complex data structure or object that is
216             # thread-shared
217             sub clone_shared
218             {
219 0     0 0 0 my $in = shift;
220 0   0     0 my $cloned = shift || {};
221              
222             # Just return the item if not a ref or if it's an object
223 0 0 0     0 return $in if (! ref($in) || Scalar::Util::blessed($in));
224              
225             # Check for previously cloned references
226             # (this takes care of circular refs as well)
227 0         0 my $addr = Scalar::Util::refaddr($in);
228 0 0       0 if (exists($cloned->{$addr})) {
229             # Return the already existing clone
230 0         0 return $cloned->{$addr};
231             }
232              
233             # Make copies of array, hash and scalar refs
234 0         0 my $out;
235 0         0 my $ref_type = Scalar::Util::reftype($in);
236              
237             # Copy an array ref
238 0 0       0 if ($ref_type eq 'ARRAY') {
    0          
    0          
    0          
239             # Make empty shared array ref
240 0         0 $out = &threads::shared::share([]);
241             # Add to clone checking hash
242 0         0 $cloned->{$addr} = $out;
243             # Recursively copy and add contents
244 0         0 push(@$out, map { clone_shared($_, $cloned) } @$in);
  0         0  
245             }
246              
247             # Copy a hash ref
248             elsif ($ref_type eq 'HASH') {
249             # Make empty shared hash ref
250 0         0 $out = &threads::shared::share({});
251             # Add to clone checking hash
252 0         0 $cloned->{$addr} = $out;
253             # Recursively copy and add contents
254 0         0 foreach my $key (keys(%{$in})) {
  0         0  
255 0         0 $out->{$key} = clone_shared($in->{$key}, $cloned);
256             }
257             }
258              
259             # Copy a scalar ref
260             elsif ($ref_type eq 'SCALAR') {
261 0         0 $out = \do{ my $scalar = $$in; };
  0         0  
262 0         0 threads::shared::share($out);
263             # Add to clone checking hash
264 0         0 $cloned->{$addr} = $out;
265             }
266              
267             # Copy of a ref of a ref
268             elsif ($ref_type eq 'REF') {
269             # Special handling for $x = \$x
270 0 0       0 if ($addr == Scalar::Util::refaddr($$in)) {
271 0         0 $out = \$out;
272 0         0 threads::shared::share($out);
273 0         0 $cloned->{$addr} = $out;
274             } else {
275 0         0 my $tmp;
276 0         0 $out = \$tmp;
277 0         0 threads::shared::share($out);
278             # Add to clone checking hash
279 0         0 $cloned->{$addr} = $out;
280             # Recursively copy and add contents
281 0         0 $tmp = clone_shared($$in, $cloned);
282             }
283              
284             } else {
285             # Just return anything else
286             # NOTE: This will end up generating an error
287 0         0 return ($in);
288             }
289              
290             # Return blessed copy, if applicable
291 0 0       0 if (my $class = Scalar::Util::blessed($in)) {
292 0         0 bless($out, $class);
293             }
294              
295             # Clone READONLY flag
296 0 0       0 if ($ref_type eq 'SCALAR') {
297 0 0       0 if (Internals::SvREADONLY($$in)) {
298 0 0       0 Internals::SvREADONLY($$out, 1) if ($] >= 5.008003);
299             }
300             }
301 0 0       0 if (Internals::SvREADONLY($in)) {
302 0 0       0 Internals::SvREADONLY($out, 1) if ($] >= 5.008003);
303             }
304              
305             # Return clone
306 0         0 return ($out);
307             }
308              
309              
310             # Recursively make a copy of a complex data structure or object
311             sub clone
312             {
313 56     56 0 109 my $in = shift;
314 56   100     195 my $cloned = shift || {};
315              
316             # Just return the item if not a ref or if it's an object
317 56 100 100     260 return $in if (! ref($in) || Scalar::Util::blessed($in));
318              
319             # Check for previously cloned references
320             # (this takes care of circular refs as well)
321 9         30 my $addr = Scalar::Util::refaddr($in);
322 9 50       24 if (exists($cloned->{$addr})) {
323             # Return the already existing clone
324 0         0 return $cloned->{$addr};
325             }
326              
327             # Make copies of array, hash and scalar refs
328 9         32 my $out;
329 9         36 my $ref_type = Scalar::Util::reftype($in);
330              
331             # Copy an array ref
332 9 100       35 if ($ref_type eq 'ARRAY') {
    50          
    0          
    0          
333             # Make empty shared array ref
334 3         7 $out = [];
335             # Add to clone checking hash
336 3         9 $cloned->{$addr} = $out;
337             # Recursively copy and add contents
338 3         8 push(@$out, map { clone($_, $cloned) } @$in);
  6         11  
339             }
340              
341             # Copy a hash ref
342             elsif ($ref_type eq 'HASH') {
343             # Make empty shared hash ref
344 6         11 $out = {};
345             # Add to clone checking hash
346 6         14 $cloned->{$addr} = $out;
347             # Recursively copy and add contents
348 6         9 foreach my $key (keys(%{$in})) {
  6         19  
349 4         7 $out->{$key} = clone($in->{$key}, $cloned);
350             }
351             }
352              
353             # Copy a scalar ref
354             elsif ($ref_type eq 'SCALAR') {
355 0         0 $out = \do{ my $scalar = $$in; };
  0         0  
356             # Add to clone checking hash
357 0         0 $cloned->{$addr} = $out;
358             }
359              
360             # Copy of a ref of a ref
361             elsif ($ref_type eq 'REF') {
362             # Special handling for $x = \$x
363 0 0       0 if ($addr == Scalar::Util::refaddr($$in)) {
364 0         0 $out = \$out;
365 0         0 $cloned->{$addr} = $out;
366             } else {
367 0         0 my $tmp;
368 0         0 $out = \$tmp;
369             # Add to clone checking hash
370 0         0 $cloned->{$addr} = $out;
371             # Recursively copy and add contents
372 0         0 $tmp = clone($$in, $cloned);
373             }
374              
375             } else {
376             # Just return anything else
377             # NOTE: This will end up generating an error
378 0         0 return ($in);
379             }
380              
381             # Clone READONLY flag
382 9 50       31 if ($ref_type eq 'SCALAR') {
383 0 0       0 if (Internals::SvREADONLY($$in)) {
384 0 0       0 Internals::SvREADONLY($$out, 1) if ($] >= 5.008003);
385             }
386             }
387 9 50       30 if (Internals::SvREADONLY($in)) {
388 0 0       0 Internals::SvREADONLY($out, 1) if ($] >= 5.008003);
389             }
390              
391             # Return clone
392 9         31 return ($out);
393             }
394              
395              
396             # Access hash value using regex
397             sub hash_re
398             {
399 102     102 0 9717 my $hash = $_[0]; # Hash ref to search through
400 102         162 my $re = $_[1]; # Regex to match keys against
401              
402 102         181 foreach (keys(%{$hash})) {
  102         362  
403 155 100       668 if (/$re/) {
404 73 100       280 return ($hash->{$_}, $_) if wantarray();
405 40         150 return ($hash->{$_});
406             }
407             }
408 29         96 return;
409             }
410              
411              
412             # Checks if a scalar is a specified type
413             sub is_it
414             {
415 86     86 0 194 my ($thing, $what) = @_;
416              
417 86 100       991 return ((Scalar::Util::blessed($thing))
418             ? $thing->isa($what)
419             : (ref($thing) eq $what));
420             }
421              
422             } # End of package's lexical scope
423              
424             1;