File Coverage

blib/lib/Data/Transform/ExplicitMetadata.pm
Criterion Covered Total %
statement 265 277 95.6
branch 137 164 83.5
condition 53 77 68.8
subroutine 35 38 92.1
pod 2 2 100.0
total 492 558 88.1


line stmt bran cond sub pod time code
1             package Data::Transform::ExplicitMetadata;
2              
3 5     5   2108 use strict;
  5         6  
  5         131  
4 5     5   18 use warnings;
  5         5  
  5         128  
5              
6 5     5   14 use Scalar::Util;
  5         8  
  5         249  
7 5     5   2248 use Symbol;
  5         3021  
  5         285  
8 5     5   23 use Carp;
  5         5  
  5         287  
9 5     5   20 use Fcntl qw(F_GETFL O_WRONLY O_RDWR O_APPEND);
  5         5  
  5         261  
10              
11             our $VERSION = "0.08";
12              
13 5     5   17 use base 'Exporter';
  5         7  
  5         638  
14              
15             our @EXPORT_OK = qw( encode decode );
16              
17             our $HAS_FMODE;
18             BEGIN {
19 5   50 5   8 $HAS_FMODE = eval { require FileHandle::Fmode } || '';
20             }
21              
22             sub _get_open_mode_filehandle_fmode {
23 5     5   25 my $fh = shift;
24              
25 5 50       9 return unless FileHandle::Fmode::is_FH($fh);
26              
27 5         47 my $is_append = FileHandle::Fmode::is_A($fh);
28 5 100       47 if (FileHandle::Fmode::is_WO($fh)) {
    100          
29 2 100       21 return $is_append ? '>>' : '>';
30             } elsif (FileHandle::Fmode::is_RW($fh)) {
31 2 100       36 return $is_append ? '+>>' : '+<';
32             } else {
33 1         27 return '<';
34             }
35             }
36              
37             sub _get_open_mode_fcntl {
38 7     7   5 my $fh = shift;
39              
40 5     5   5323 my $flags = eval { no warnings 'uninitialized';
  5         6  
  5         4591  
  7         8  
41 7         35 fcntl($fh, F_GETFL, my $junk) };
42 7 50       12 return unless $flags;
43              
44 7         8 my $is_append = $flags & O_APPEND;
45 7 100       16 if ($flags & O_WRONLY) {
    100          
46 3 100       15 return $is_append ? '>>' : '>';
47             } elsif ($flags & O_RDWR) {
48 2 100       6 return $is_append ? '+>>' : '+<';
49             } else {
50 2         7 return '<';
51             }
52             }
53              
54             sub _get_open_mode {
55 12     12   10 my $fh = shift;
56              
57 12   66     21 return _get_open_mode_fcntl($fh)
58             ||
59             ($HAS_FMODE && _get_open_mode_filehandle_fmode($fh));
60             }
61              
62             sub encode {
63 138     138 1 24070 my $value = shift;
64 138         101 my $path_expr = shift;
65 138         94 my $seen = shift;
66              
67 138 100       207 if (!ref($value)) {
68 63         47 my $ref_to_value = \$value;
69 63         65 my $ref = ref($ref_to_value);
70 63         44 my $encoded_value = $value;
71             # perl 5.8 - ref() with a vstring returns SCALAR
72 63 100 66     341 if ($ref eq 'GLOB'
      66        
73             or
74             $ref eq 'VSTRING' or Scalar::Util::isvstring($value)
75             ) {
76 1         2 $encoded_value = encode($ref_to_value, $path_expr, $seen);
77 1         2 delete $encoded_value->{__refaddr};
78 1         2 delete $seen->{$ref_to_value};
79             }
80 63         129 return $encoded_value;
81             }
82              
83 75   100     164 $path_expr ||= '$VAR';
84 75   100     140 $seen ||= {};
85              
86 75         116 my $reftype = Scalar::Util::reftype($value);
87 75         87 my $refaddr = Scalar::Util::refaddr($value);
88 75         95 my $blesstype = Scalar::Util::blessed($value);
89              
90 75         49 my $encoded_value;
91              
92 75 100       150 if ($seen->{$value}) {
93             $encoded_value = { __reftype => $reftype,
94             __refaddr => $refaddr,
95             __recursive => 1,
96 7         19 __value => $seen->{$value} };
97 7 100       11 $encoded_value->{__blessed} = $blesstype if $blesstype;
98 7         18 return $encoded_value;
99             }
100 68         105 $seen->{$value} = $path_expr;
101              
102             # Build a new path string for recursive calls
103             my $_p = sub {
104 102 100 100 102   363 return join('', '${', $path_expr, '}') if ($reftype eq 'SCALAR' or $reftype eq 'REF');
105 74 100       164 return join('', '*{', $path_expr, '}') if ($reftype eq 'GLOB');
106              
107 42 100       77 my @bracket = $reftype eq 'ARRAY' ? ( '[', ']' ) : ( '{', '}' );
108 42         174 return sprintf('%s->%s%s%s', $path_expr, $bracket[0], $_, $bracket[1]);
109 68         176 };
110              
111 68 100       150 if (my $tied = _is_tied($value)) {
112 4         5 local $_ = 'tied'; # &$_p needs this
113 4         8 my $original = encode(_untie_and_get_original_value($value), &$_p, $seen);
114             $encoded_value = { __reftype => $reftype,
115             __refaddr => $refaddr,
116 4 100       11 __tied => ref($original) ? $original->{__value} : $original,
117             __value => encode($tied, &$_p, $seen) };
118 4         7 _retie($value, $tied);
119 4 50       8 $encoded_value->{__blessed} = $blesstype if $blesstype;
120 4         21 return $encoded_value;
121             }
122              
123 64 100 100     375 if ($reftype eq 'HASH') {
    100 66        
    100 66        
    100 66        
    100 66        
    100          
    100          
    50          
124 7         28 $encoded_value = { map { $_ => encode($value->{$_}, &$_p, $seen) } sort(keys %$value) };
  13         23  
125              
126             } elsif ($reftype eq 'ARRAY') {
127 12         29 $encoded_value = [ map { encode($value->[$_], &$_p, $seen) } (0 .. $#$value) ];
  25         43  
128              
129             } elsif ($reftype eq 'GLOB') {
130 17         15 my %encoded_value = map { $_ => encode(*{$value}{$_},
  17         31  
131             &$_p."{$_}",
132             $seen) }
133 14         21 grep { *{$value}{$_} }
  42         35  
  42         55  
134             qw(HASH ARRAY SCALAR);
135 14         17 @encoded_value{'NAME','PACKAGE'} = (*{$value}{NAME}, *{$value}{PACKAGE});
  14         19  
  14         31  
136 14 100       14 if (*{$value}{CODE}) {
  14         27  
137 1         2 $encoded_value{CODE} = encode(*{$value}{CODE}, &$_p, $seen);
  1         2  
138             }
139 14 100       15 if (*{$value}{IO}) {
  14         48  
140 12 50       29 if ( $encoded_value{IO} = encode(fileno(*{$value}{IO}), &$_p, $seen) )
  12         37  
141             {
142 12         17 $encoded_value{IOmode} = _get_open_mode(*{$value}{IO});
  12         21  
143 12         39 $encoded_value{IOseek} = sysseek($value, 0, 1);
144             }
145             }
146 14         23 $encoded_value = \%encoded_value;
147             } elsif (($reftype eq 'REGEXP')
148             or ($reftype eq 'SCALAR' and defined($blesstype) and $blesstype eq 'Regexp')
149             ) {
150 1         1 $reftype = 'REGEXP';
151 1 50       3 undef($blesstype) unless $blesstype ne 'Regexp';
152 1         1 my($pattern, $modifiers);
153 1 50       17 if ($^V ge v5.9.5) {
154 1         5 require re;
155             }
156 1 50       10 if (defined &re::regexp_pattern) {
157 1         4 ($pattern, $modifiers) = re::regexp_pattern($value);
158             } else {
159 0         0 my $value_as_str = "$value";
160 0         0 ($modifiers, $pattern) = $value_as_str =~ m/\(\?(\w*)-\w*:(.*)\)$/;
161             }
162 1         2 $encoded_value = [ $pattern, $modifiers ];
163             } elsif ($reftype eq 'CODE') {
164 2         8 (my $copy = $value.'') =~ s/^(\w+)\=//; # Hack to change CodeClass=CODE(0x123) to CODE=(0x123)
165 2         6 $encoded_value = $copy;
166             } elsif ($reftype eq 'REF') {
167 7         11 $encoded_value = encode($$value, &$_p, $seen );
168             } elsif (($reftype eq 'VSTRING') or (ref($value) eq 'SCALAR' and Scalar::Util::isvstring($$value))) {
169 2         2 $reftype = 'VSTRING';
170 2         7 $encoded_value = [ unpack('c*', $$value) ];
171             } elsif ($reftype eq 'SCALAR') {
172 19         27 $encoded_value = encode($$value, &$_p, $seen);
173             }
174              
175 64         142 $encoded_value = { __reftype => $reftype, __refaddr => $refaddr, __value => $encoded_value };
176 64 100       97 $encoded_value->{__blessed} = $blesstype if $blesstype;
177              
178 64         265 return $encoded_value;
179             }
180              
181             sub _is_tied {
182 72     72   57 my $ref = shift;
183              
184 72         93 my $reftype = Scalar::Util::reftype($ref);
185 72         42 my $tied;
186 72 100       196 if ($reftype eq 'HASH') { $tied = tied %$ref }
  9 100       12  
    100          
    100          
187 14         15 elsif ($reftype eq 'ARRAY') { $tied = tied @$ref }
188 21         23 elsif ($reftype eq 'SCALAR') { $tied = tied $$ref }
189 16         18 elsif ($reftype eq 'GLOB') { $tied = tied *$ref }
190              
191 72         131 return $tied;
192             }
193              
194             sub _untie_and_get_original_value {
195 4     4   5 my $ref = shift;
196              
197 4         3 my $tied_val = _is_tied($ref);
198 4         9 my $class = Scalar::Util::blessed($tied_val);
199 4         8 my $untie_function = join('::', $class, 'UNTIE');
200 5     5   21 no strict 'refs';
  5         35  
  5         198  
201 4     4   23 local *$untie_function = sub { };
202 5     5   16 use strict 'refs';
  5         3  
  5         860  
203              
204 4         8 my $reftype = Scalar::Util::reftype($ref);
205 4         2 my $original;
206 4 50       19 if (!$reftype) {
    100          
    100          
    100          
    50          
207 0         0 untie $ref;
208 0         0 $original = $ref;
209             } elsif ($reftype eq 'SCALAR') {
210 1         3 untie $$ref;
211 1         2 $original = $$ref;
212             } elsif ($reftype eq 'ARRAY') {
213 1         2 untie @$ref;
214 1         2 $original = [ @$ref ];
215             } elsif ($reftype eq 'HASH') {
216 1         3 untie %$ref;
217 1         3 $original = { %$ref };
218             } elsif ($reftype eq 'GLOB') {
219 1         3 untie *$ref;
220 1         3 my $pkg = *$ref{PACKAGE};
221 1         2 my $name = *$ref{NAME};
222 1         3 $original = _create_anon_ref_of_type('GLOB', $pkg, $name);
223 1         3 *$original = *$ref;
224             } else {
225 0         0 Carp::croak("Cannot retrieve the original value of a tied $reftype");
226             }
227 4         13 return $original;
228             }
229              
230             sub _retie {
231 8     8   9 my($ref, $value) = @_;
232              
233 8         12 my $reftype = Scalar::Util::reftype($ref);
234 8         14 my $class = Scalar::Util::blessed($value);
235 5     5   19 no strict 'refs';
  5         5  
  5         110  
236 5     5   14 no warnings 'redefine';
  5         12  
  5         1309  
237 8 100       24 if ($reftype eq 'SCALAR') {
    100          
    100          
    50          
238 2         4 my $tiescalar = join('::',$class, 'TIESCALAR');
239 2     2   10 local *$tiescalar = sub { return $value };
  2         6  
240 2         6 tie $$ref, $class;
241              
242             } elsif ($reftype eq 'ARRAY') {
243 2         12 my $tiearray = join('::', $class, 'TIEARRAY');
244 2     2   12 local *$tiearray = sub { return $value };
  2         8  
245 2         7 tie @$ref, $class;
246              
247             } elsif ($reftype eq 'HASH') {
248 2         3 my $tiehash = join('::', $class, 'TIEHASH');
249 2     2   10 local *$tiehash = sub { return $value };
  2         6  
250 2         5 tie %$ref, $class;
251              
252             } elsif ($reftype eq 'GLOB') {
253 2         4 my $tiehandle = join('::', $class, 'TIEHANDLE');
254 2     2   9 local *$tiehandle = sub { return $value };
  2         6  
255 2         10 tie *$ref, $class;
256              
257             } else {
258 0         0 Carp::croak('Cannot recreate a tied '.scalar(ref $value));
259             }
260             }
261              
262             sub _create_anon_ref_of_type {
263 8     8   10 my($type, $package, $name) = @_;
264              
265 8 100       48 if ($type eq 'SCALAR') {
    100          
    100          
    50          
266 1         4 my $anon;
267 1         1 return \$anon;
268             } elsif ($type eq 'ARRAY') {
269 1         3 return [];
270             } elsif ($type eq 'HASH') {
271 1         2 return {};
272             } elsif ($type eq 'GLOB') {
273 5         6 my $rv;
274 5 100 66     79 if ($package and $name
      66        
      33        
275             and
276             $package ne 'Symbol'
277             and
278             $name !~ m/GEN\d/
279             ) {
280 4         10 my $globname = join('::',$package, $name);
281 5     5   20 $rv = do { no strict 'refs'; local *$globname; \*$globname; };
  5         9  
  5         4377  
  4         5  
  4         14  
  4         10  
282             } else {
283 1         5 $rv = Symbol::gensym();
284             }
285 5         22 return $rv;
286             }
287             }
288              
289             sub _recreate_fh {
290 1     1   2 my($fileno, $mode) = @_;
291              
292 1         1 my $fh;
293 1 50       3 if ($mode) {
294 0 0       0 open($fh, $mode . '&=', $fileno)
295             || Carp::carp("Couldn't open filehandle for descriptor $fileno with mode $mode: $!");
296              
297             } else {
298 1 50 33     14 open($fh, '>&=', $fileno)
299             || open($fh, '<&=', $fileno)
300             || Carp::carp("Couldn't open filehandle for descriptor $fileno: $!");
301             }
302 1         2 return $fh;
303             }
304              
305             sub decode {
306 77     77 1 16534 my($input, $recursive_queue, $recurse_fill) = @_;
307              
308 77 100       125 unless (ref $input) {
309 27         85 return $input;
310             }
311              
312 50         64 _validate_decode_structure($input);
313              
314 50         74 my($value, $reftype, $refaddr, $blessed) = @$input{'__value','__reftype','__refaddr','__blessed'};
315 50         34 my $rv;
316 50         48 my $is_first_invocation = ! $recursive_queue;
317 50   100     94 $recursive_queue ||= [];
318              
319 50 100       180 if ($input->{__recursive}) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
320 7         7 my $path = $input->{__value};
321             push @$recursive_queue,
322             sub {
323 7     7   9 my $VAR = shift;
324 7         404 $recurse_fill->(eval $path);
325 7         18 };
326              
327             } elsif ($input->{__tied}) {
328 4         8 $rv = _create_anon_ref_of_type($reftype);
329 4         3 my $tied_value;
330 4     0   17 $tied_value = decode($value, $recursive_queue, sub { $tied_value });
  0         0  
331 4         13 _retie($rv, $tied_value);
332              
333             } elsif ($reftype eq 'SCALAR') {
334 8         10 $rv = \$value;
335              
336             } elsif ($reftype eq 'ARRAY') {
337 11         18 $rv = [];
338 11         26 for (my $i = 0; $i < @$value; $i++) {
339 23         18 my $idx = $i;
340 23     4   95 push @$rv, decode($value->[$i], $recursive_queue, sub { $rv->[$idx] = shift });
  4         13  
341             }
342              
343             } elsif ($reftype eq 'HASH') {
344 6         7 $rv = {};
345 6         20 foreach my $key ( sort keys %$value ) {
346 12         13 my $k = $key;
347 12     1   42 $rv->{$key} = decode($value->{$key}, $recursive_queue, sub { $rv->{$k} = shift });
  1         3  
348             }
349              
350             } elsif ($reftype eq 'GLOB') {
351             my $is_real_glob = ($value->{PACKAGE} ne 'Symbol'
352             and $value->{NAME} !~ m/^GEN\d+/
353 3   33     29 and $value->{NAME} =~ m/^\w/);
354 3         8 $rv = _create_anon_ref_of_type('GLOB', $value->{PACKAGE}, $value->{NAME});
355              
356 3         8 foreach my $type ( keys %$value ) {
357 14 50 100     75 next if ($type eq 'NAME' or $type eq 'PACKAGE' or $type eq 'IOseek' or $type eq 'IOmode');
      66        
      66        
358 8 100       16 if ($type eq 'IO') {
    100          
359 1 50       3 if (my $fileno = $value->{IO}) {
360 1         3 $rv = _recreate_fh($fileno, $value->{IOmode});
361             }
362             } elsif ($type eq 'CODE') {
363 1 50       2 *{$rv} = \&_dummy_sub unless ($is_real_glob);
  0         0  
364              
365             } else {
366 6     0   19 *{$rv} = decode($value->{$type}, $recursive_queue, sub { *{$rv} = shift });
  6         19  
  0         0  
  0         0  
367             }
368             }
369              
370 3 50       7 $rv = *$rv unless $refaddr;
371              
372             } elsif ($reftype eq 'CODE') {
373 1         3 $rv = \&_dummy_sub;
374              
375             } elsif ($reftype eq 'REF') {
376 7         6 my $ref;
377 7     2   23 $ref = decode($value, $recursive_queue, sub { $ref = shift });
  2         6  
378 7         12 $rv = \$ref;
379              
380             } elsif ($reftype eq 'REGEXP') {
381 1         2 my($pattern,$modifiers) = @$value[0,1];
382 1         78 $rv = eval "qr($pattern)$modifiers";
383              
384             } elsif ($reftype eq 'VSTRING') {
385 2         90 my $vstring = eval 'v' . join('.', @$value);
386 2 100       8 $rv = $refaddr ? \$vstring : $vstring;
387              
388             }
389              
390 50 100 100     104 bless $rv, $blessed if ($blessed and ! $input->{__recursive});
391              
392 50 100       67 if ($is_first_invocation) {
393 21         34 $_->($rv) foreach @$recursive_queue;
394             }
395              
396 50         91 return $rv;
397             }
398              
399             sub _dummy_sub {
400 0     0   0 'Put in place by ' . __PACKAGE__ . ' when it could not find the named sub';
401             }
402              
403             sub _validate_decode_structure {
404 50     50   38 my $input = shift;
405              
406 50 50       87 ref($input) eq 'HASH'
407             or Carp::croak('Invalid decode data: expected hashref but got '.ref($input));
408              
409             exists($input->{__value})
410 50 50       74 or Carp::croak('Invalid decode data: expected key __value');
411             exists($input->{__reftype})
412 50 50       69 or Carp::croak('Invalid decode data: expected key __reftype');
413              
414 50         74 my($reftype, $value, $blesstype) = @$input{'__reftype','__value','__blesstype'};
415             $reftype eq 'GLOB'
416             or $reftype eq 'VSTRING'
417             or exists($input->{__refaddr})
418 50 50 100     216 or Carp::croak('Invalid decode data: expected key __refaddr');
      66        
419              
420 50 50 33     176 ($blesstype and $reftype)
      33        
421             or !$blesstype
422             or Carp::croak('Invalid decode data: Cannot have __blesstype without __reftype');
423              
424             my $compatible_references =
425             ( ( $reftype eq 'SCALAR' and ! ref($value) )
426             or
427             ( $reftype eq ref($value) )
428             or
429             ( $reftype eq 'GLOB' and exists($value->{SCALAR}))
430             or
431             ( $reftype eq 'CODE' and $value and ref($value) eq '' )
432             or
433             ( $reftype eq 'REF' and ref($value) eq 'HASH' and exists($value->{__reftype}) )
434             or
435             ( $reftype eq 'REGEXP' and ref($value) eq 'ARRAY' )
436             or
437             ( $reftype eq 'VSTRING' and ref($value) eq 'ARRAY' )
438             or
439             ( $reftype and ! ref($input->{__value}) and $input->{__recursive} )
440             or
441             ( $input->{__tied} and ref($input->{__value}) and $input->{__value}->{__blessed} )
442 50   66     462 );
443             $compatible_references or Carp::croak('Invalid decode data: __reftype is '
444             . $input->{__reftype}
445             . ' but __value is a '
446 50 50       65 . ref($input->{__value}));
447 50         48 return 1;
448             }
449              
450             1;
451              
452             =pod
453              
454             =head1 NAME
455              
456             Data::Transform::ExplicitMetadata - Encode Perl values in a json-friendly way
457              
458             =head1 SYNOPSIS
459              
460             use Data::Transform::ExplicitMetadata qw(encode decode);
461             use JSON;
462              
463             my $val = encode($some_data_structure);
464             $io->print( JSON::encode_json( $val ));
465              
466             my $data_structure_copy = decode($val);
467              
468             =head1 DESCRIPTION
469              
470             Transforms an arbitrarily nested data structure into an analogous data
471             structure composed of only simple scalars, arrayrefs and hashrefs that may
472             be safely JSON-encoded, while retaining all the Perl-specific metadata
473             about typeglobs, blessed and tied references, self-referential data,
474             reference addresses, etc.
475              
476             With a few exceptions, a copy of the original data structure can be recreated
477             from the encoded version.
478              
479             =head2 Functions
480              
481             =over 4
482              
483             =item encode
484              
485             Accepts a single value and returns a value that may be safely passed to
486             JSON::encode_json(). encode_json() cannot handle Perl-specific data like
487             blessed references or typeglobs. Non-reference scalar values like numbers
488             and strings are returned unchanged. For all references, encode()
489             returns a hashref with these keys
490              
491             =over 4
492              
493             =item * __reftype
494              
495             String indicating the type of reference, as returned by Scalar::Util::reftype()
496              
497             =item * __refaddr
498              
499             Memory address of the reference, as returned by Scalar::Util::refaddr()
500              
501             =item * __blessed
502              
503             Package this reference is blessed into, as returned by Scalar::Util::blessed.
504              
505             =item * __value
506              
507             Reference to the unblessed data.
508              
509             =item * __tied
510              
511             The original value hidden by the tie() operation.
512              
513             =item * __recursive
514              
515             Flag indicating this reference was seen before
516              
517             =back
518              
519             If the reference was not blessed or tied, then the __blessed and/or __tied keys
520             will not be present.
521              
522             C<__value> is generally a copy of the underlying data. For example, if the input
523             value is an hashref, then __value will also be a hashref containing the input
524             value's kays and values. For typeblobs and glob refs, __value will be a
525             hashref with the keys NAME, PACKAGE, SCALAR, ARRAY, HASH, IO and CODE. For
526             compiled regexps, __value will be a 2-element arrayref of the pattern and
527             modifiers. For coderefs, __value will be the stringified reference, like
528             "CODE=(0x12345678)". For v-strings and v-string refs, __value will by an
529             arrayref containing the integers making up the v-string.
530              
531             For tied objects, C<__tied> will be contain the original value hidden by tie()
532             and __value will contain the tied data. The original data is retrieved by:
533              
534             =over 4
535              
536             =item *
537              
538             call tied() to get a copy of the tied data
539              
540             =item *
541              
542             localize the UNTIE method in the appropriate class
543              
544             =item *
545              
546             untie the variable
547              
548             =item *
549              
550             save a copy of the original value
551              
552             =item *
553              
554             localize the appropriate TIE* mythod to return the tied data
555              
556             =item *
557              
558             call tie() to retie the variable
559              
560             =back
561              
562             if C<__recursive> is true, then __value will contain a string representation
563             of the first place this reference was seen in the data structure.
564              
565             encode() handles arbitrarily nested data structures, meaning that
566             values in the __values slot may also be encoded this way.
567              
568             =item decode
569              
570             Accepts a single value and returns a copy of the data structure originally
571             passed to encode(). __refaddr information is discarded and new copies of
572             nested data structures is created. Self-referential data is re-linked to the
573             appropriate placxe in the new copy. Blessed references are re-bless into
574             the original packages.
575              
576             Tied variables are re-tied by localizing the appropriate TIE* method to return
577             the tied data. The variable's original data is filled in before calling tie().
578              
579             The IO slot of typeglobs is recreated by opening the handle with the same
580             descriptor number and open mode. It will first try fcntl() with F_GETFL
581             to determine the open mode, falling back to using FileHandle::Fmode if it's
582             available. Finally, it will first try re-opening the file descriptor in
583             read mode, then write mode.
584              
585             Coderefs cannot be decoded properly. They are recreated by returning a
586             reference to a dummy sub that returns a message explaning the situation.
587              
588             =back
589              
590             =head1 SEE ALSO
591              
592             L, L, L, L
593              
594             =head1 AUTHOR
595              
596             Anthony Brummett
597              
598             =head1 COPYRIGHT
599              
600             Copyright 2016, Anthony Brummett. This module is free software. It may
601             be used, redistributed and/or modified under the same terms as Perl itself.