File Coverage

blib/lib/Util/H2O.pm
Criterion Covered Total %
statement 152 154 100.0
branch 135 136 100.0
condition 82 82 100.0
subroutine 16 16 100.0
pod 2 2 100.0
total 387 390 100.0


line stmt bran cond sub pod time code
1             #!perl
2             package Util::H2O;
3 1     1   110374 use warnings;
  1         3  
  1         37  
4 1     1   5 use strict;
  1         2  
  1         61  
5 1     1   7 use Exporter 'import';
  1         3  
  1         39  
6 1     1   6 use Carp;
  1         2  
  1         56  
7 1     1   491 use Symbol qw/delete_package/;
  1         863  
  1         151  
8              
9             =head1 Name
10              
11             Util::H2O - Hash to Object: turns hashrefs into objects with accessors for keys
12              
13             =head1 Synopsis
14              
15             use Util::H2O;
16            
17             my $hash = h2o { foo => "bar", x => "y" }, qw/ more keys /;
18             print $hash->foo, "\n"; # accessor
19             $hash->x("z"); # change value
20             $hash->more("cowbell"); # additional keys
21            
22             my $struct = { hello => { perl => "world!" } };
23             h2o -recurse, $struct; # objectify nested hashrefs as well
24             print $struct->hello->perl, "\n";
25            
26             my $obj = h2o -meth, { # code references become methods
27             what => "beans",
28             cool => sub {
29             my $self = shift;
30             print $self->what, "\n";
31             } };
32             $obj->cool; # prints "beans"
33            
34             h2o -classify=>'Point', { # whip up a class
35             angle => sub { my $self = shift; atan2($self->y, $self->x) }
36             }, qw/ x y /;
37             my $one = Point->new(x=>1, y=>2);
38             my $two = Point->new(x=>3, y=>4);
39             printf "%.3f\n", $two->angle; # prints 0.927
40              
41             =cut
42              
43             our $VERSION = '0.20';
44             # For AUTHOR, COPYRIGHT, AND LICENSE see the bottom of this file
45              
46             our @EXPORT = qw/ h2o /; ## no critic (ProhibitAutomaticExportation)
47             our @EXPORT_OK = qw/ o2h /;
48              
49             BEGIN {
50             # lock_ref_keys wasn't available until Hash::Util 0.06 / Perl v5.8.9
51             # (note the following will probably also fail on the Perl v5.9 dev releases)
52             # uncoverable branch false
53             # uncoverable condition false
54 1 50   1   7 if ( $] ge '5.008009' ) {
55 1         1551 require Hash::Util;
56 1         6938 Hash::Util->import(qw/ lock_ref_keys lock_hashref /) }
57             else {
58             *lock_ref_keys = *lock_hashref = sub {
59 0         0 carp "this Perl is too old to lock the hash"; # uncoverable statement
60 0         0 }; # uncoverable statement
61             }
62             }
63              
64             =head1 Description
65              
66             This module allows you to turn hashrefs into objects, so that instead
67             of C<< $hash->{key} >> you can write C<< $hash->key >>, plus you get
68             protection from typos. In addition, options are provided that allow
69             you to whip up really simple classes.
70              
71             You can still use the hash like a normal hashref as well, as in
72             C<< $hash->{key} >>, C, and so on, but note that by
73             default this function also locks the hash's keyset to prevent typos
74             there too.
75              
76             This module exports a single function by default.
77              
78             =head2 C, I<$hashref>, I<@additional_keys>>
79              
80             =head3 C<@opts>
81              
82             If you specify an option with a value multiple times, only the last
83             one will take effect.
84              
85             =over
86              
87             =item C<-recurse>
88              
89             Nested hashes are objectified as well. The only options that are passed down to
90             nested hashes are C<-lock> and C<-ro>. I of the other options will be
91             applied to the nested hashes, including C<@additional_keys>. Nested arrayrefs
92             are not recursed into, but see the C<-arrays> option for that.
93              
94             Versions of this module before v0.12 did not pass down the C<-lock> option,
95             meaning that if you used C<-nolock, -recurse> on those versions, the nested
96             hashes would still be locked.
97              
98             =item C<-arrays>
99              
100             Like C<-recurse>, but additionally, C is applied to elements of
101             nested arrays as well. The same options as with C<-recurse> are
102             passed down to nested hashes and arrayrefs. Takes precedence over the
103             C<-pass> option, i.e. if you use these two options together,
104             arrayrefs are still descended into. Like hashrefs, the original
105             arrays are modified!
106              
107             This option implies C<-recurse>.
108             This option was added in v0.20.
109              
110             =item C<-meth>
111              
112             Any code references present in the hash at the time of this function
113             call will be turned into methods. Because these methods are installed
114             into the object's package, they can't be changed later by modifying
115             the hash.
116              
117             To avoid confusion when iterating over the hash, the hash entries
118             that were turned into methods are removed from the hash. The key is
119             also removed from the "allowed keys" (see the C<-lock> option),
120             I you specify it in C<@additional_keys>. In that case, you
121             can change the value of that key completely independently of the
122             method with the same name.
123              
124             =item C<< -class => I >>
125              
126             Specify the class name into which to bless the object (as opposed to
127             the default: a generated, unique package name in C).
128              
129             I If you use this option, C<-clean> defaults to I,
130             meaning that the package will stay in Perl's symbol table and use
131             memory accordingly, and since this function installs the accessors in
132             the package every time it is called, if you re-use the same package
133             name, you will get "redefined" warnings. Therefore, if you want to
134             create multiple objects in the same package, you should probably use
135             C<-new> or C<-classify>.
136              
137             If you wanted to generate a unique package name in a different package,
138             you could use:
139             C<< h2o -class => sprintf('My::Class::Name::_%x', $hash+0), $hash >>,
140             perhaps even in combination with C<< -isa => 'My::Class::Name' >>.
141             However, keep in mind that you shouldn't step into another class' namespace
142             without knowing that this won't cause conflicts, and also that not using the
143             default class names means that functions like C will no longer identify
144             the objects as coming from C.
145              
146             =item C<< -classify => I >>
147              
148             In the form C<< -classify => I >>, this is simply the short
149             form of the options C<< -new, -meth, -class => I >>.
150              
151             As of v0.16, in the special form C<< -classify => I<$hashref> >>, where the
152             C<-classify> B be the B option in C<@opts> before the
153             L|/"$hashref">, it is the same as
154             C<< -new, -meth, -class => __PACKAGE__, I<$hashref> >> - that is, the current
155             package's name is used as the custom class name. It does not make sense to use
156             this outside of an explicit package, since your class will be named C
.
157             With this option, the C example in the L can be written like
158             the following, which can be useful if you want to add more things to the
159             C, or perhaps if you want to write your methods as regular Cs:
160              
161             {
162             package Point;
163             use Util::H2O;
164             h2o -classify, {
165             angle => sub { my $self = shift; atan2($self->y, $self->x) }
166             }, qw/ x y /;
167             }
168              
169             Note C will remain in the package's namespace, one possibility is that you
170             could load L after you load this module.
171              
172             You might also note that in the above example, one could write C as a
173             regular C in the package. And at that point, one might recongize the
174             similarity between the code and what one can do with e.g.
175             L or even L.
176              
177             =item C<< -isa => I >>
178              
179             Convenience option to set the L|perlvar/"@ISA"> variable in the package
180             of the object, so that the object inherits from that/those package(s).
181             This option was added in v0.14.
182              
183             B The methods created by C will not call superclass methods.
184             This means the parent class' C method(s) are not called, and any
185             accessors generated from hash keys are blindly overriden.
186              
187             =item C<-new>
188              
189             Generates a constructor named C in the package. The constructor
190             works as a class and instance method, and dies if it is given any
191             arguments that it doesn't know about. If you want more advanced
192             features, like required arguments, validation, or other
193             initialization, you should probably L
194             to something like L instead.
195              
196             =item C<< -destroy => I >>
197              
198             Allows you to specify a custom destructor. This coderef will be called from the
199             object's actual C in void context with the first argument being the
200             same as the first argument to the C method. Errors will be converted
201             to warnings.
202             This option was added in v0.14.
203              
204             =item C<< -clean => I >>
205              
206             Whether or not to clean up the generated package when the object is
207             destroyed. Defaults to I when C<-class> is specified, I
208             otherwise. If this is I, be aware that the packages will stay
209             in Perl's symbol table and use memory accordingly.
210              
211             As of v0.16, this module will refuse to delete the package if it
212             is named C
.
213              
214             =item C<< -lock => I >>
215              
216             Whether or not to use L's C to prevent
217             modifications to the hash's keyset. Defaults to I.
218             The C<-nolock> option is provided as a short form of C<< -lock=>0 >>.
219              
220             Keysets of objects created by the constructor generated by the
221             C<-new> option are also locked. Versions of this module before
222             v0.12 did not lock the keysets of new objects.
223              
224             Note that on really old Perls, that is, before Perl v5.8.9,
225             L and its C are not available, so the hash
226             is never locked on those versions of Perl. Versions of this module
227             before v0.06 did not lock the keyset.
228             Versions of this module as of v0.12 issue a warning on old Perls.
229              
230             =item C<-nolock>
231              
232             Short form of the option C<< -lock=>0 >>.
233              
234             =item C<-ro>
235              
236             Makes the entire hash read-only using L's C and the
237             generated accessors will also throw an error if you try to change values. In
238             other words, this makes the object and the underlying hash immutable.
239              
240             You cannot specify any C<@additional_keys> with this option enabled unless you
241             also use the C<-new> option - the additional keys will then only be useful as
242             arguments to the constructor. This option can't be used with C<-nolock> or
243             C<< -lock=>0 >>.
244              
245             This option was added in v0.12. Using this option will not work and cause a
246             warning when used on really old Perls (before v5.8.9), because this
247             functionality was not yet available there.
248              
249             =item C<< -pass => "ref" I "undef" >>
250              
251             When this option is set to C<"undef"> (that's the string C<"undef">, I
252             C itself!), then passing a value of C for the C<$hashref> will
253             not result in a fatal error, the value will simply be passed through.
254              
255             When this option is set to the string C<"ref">, then any value other than a
256             plain hashref that is a reference, including objects, plus C as above,
257             will be passed through without modification. Any hashes nested inside of these
258             references will not be descended into, even when C<-recurse> is specified.
259             However, C<-arrays> takes precedence over this option, see its documentation.
260              
261             This option was added in v0.18.
262              
263             =back
264              
265             =head3 C<$hashref>
266              
267             You must supply a plain (unblessed) hash reference here, unless
268             you've specified the C<-pass> and/or C<-arrays> options. Be aware
269             that this function I modify the original hashref(s) by blessing
270             it and locking its keyset (the latter can be disabled with the
271             C<-lock> option), and if you use C<-meth> or C<-classify>, keys whose
272             values are code references will be removed.
273             If you use C<-arrays>, the elements of those arrays may also be modified.
274              
275             An accessor will be set up for each key in the hash(es); note that the
276             keys must of course be valid Perl identifiers for you to be able to
277             call the method normally (see also the L).
278              
279             The following keys will be treated specially by this module. Please note that
280             there are further keys that are treated specially by Perl and/or that other
281             code may expect to be special, such as L's C. See also
282             L and the references therein.
283              
284             =over
285              
286             =item C
287              
288             This key is not allowed in the hash if the C<-new> option is on.
289              
290             =item C
291              
292             This key is not allowed except if all of the following apply:
293              
294             =over
295              
296             =item *
297              
298             C<-destroy> is not used,
299              
300             =item *
301              
302             C<-clean> is off (which happens by default when you use C<-class>),
303              
304             =item *
305              
306             C<-meth> is on, and
307              
308             =item *
309              
310             the value of the key C is a coderef.
311              
312             =back
313              
314             Versions of this module before v0.14 allowed a C key in more
315             circumstances (whenever C<-clean> was off).
316              
317             =item C
318              
319             If your hash contains a key named C, or this key is present in
320             C<@additional_keys>, this module will set up a method called C, which
321             is subject to Perl's normal autoloading behavior - see L
322             and L. Without the C<-meth> option, you will get a
323             "catch-all" accessor to which all method calls to unknown method names will go,
324             and with C<-meth> enabled (which is implied by C<-classify>), you can install
325             your own custom C handler by passing a coderef as the value for this
326             key - see L. However, it is important to note that
327             enabling autoloading removes any typo protection on method names!
328              
329             =back
330              
331             =head3 C<@additional_keys>
332              
333             Methods will be set up for these keys even if they do not exist in the hash.
334              
335             Please see the list of keys that are treated specially above.
336              
337             =head3 Returns
338              
339             The (now blessed and optionally locked) C<$hashref>.
340              
341             =cut
342              
343             our $_PACKAGE_REGEX = qr/\AUtil::H2O::_[0-9A-Fa-f]+\z/;
344              
345             sub h2o { ## no critic (RequireArgUnpacking, ProhibitExcessComplexity)
346 156     156 1 42244 my ($recurse,$arrays,$meth,$class,$isa,$destroy,$new,$clean,$lock,$ro,$pass);
347 156   100     1372 while ( @_ && $_[0] && !ref$_[0] && $_[0]=~/^-/ ) {
      100        
      100        
348 224 100       967 if ($_[0] eq '-recurse' ) { $recurse = shift } ## no critic (ProhibitCascadingIfElse)
  25 100       147  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
349 36         193 elsif ($_[0] eq '-arrays'){ $arrays = shift }
350 20         102 elsif ($_[0] eq '-meth' ) { $meth = shift }
351 9 100       61 elsif ($_[0] eq '-clean') { $clean = (shift, shift()?1:0) }
352 46 100       238 elsif ($_[0] eq '-lock' ) { $lock = (shift, shift()?1:0) }
353 4         9 elsif ($_[0] eq '-nolock'){ $lock = 0; shift }
  4         26  
354 13         67 elsif ($_[0] eq '-ro' ) { $ro = shift }
355 8         46 elsif ($_[0] eq '-new' ) { $new = shift }
356             elsif ($_[0] eq '-pass' ) {
357 18         41 $pass = (shift, shift);
358 18 100 100     479 croak "invalid -pass option value (must be 'undef' or 'ref')"
      100        
359             if !defined $pass || $pass ne 'undef' && $pass ne 'ref';
360             }
361             elsif ($_[0] eq '-class') {
362 16         38 $class = (shift, shift);
363 16 100 100     383 croak "invalid -class option value"
      100        
364             if !defined $class || ref $class || !length $class;
365             }
366             elsif ($_[0] eq '-classify') {
367 11         25 $class = (shift, shift);
368 11 100       41 if ( ref $class eq 'HASH' ) { unshift @_, $class; $class = caller; }
  3         11  
  3         8  
369 11 100 100     294 croak "invalid -classify option value"
      100        
370             if !defined $class || ref $class || !length $class;
371 8         13 $meth = 1; $new = 1;
  8         43  
372             }
373             elsif ($_[0] eq '-isa') {
374 7         14 $isa = (shift, shift);
375 7 100 100     116 croak "invalid -isa option value" if !( ref($isa) eq 'ARRAY' || !ref($isa) );
376 6 100       42 $isa = [$isa] unless ref $isa;
377             }
378             elsif ($_[0] eq '-destroy') {
379 8         18 $destroy = (shift, shift);
380 8 100       196 croak "invalid -destroy option value" unless ref $destroy eq 'CODE';
381             }
382 3         254 else { croak "unknown option to h2o: '$_[0]'" }
383             }
384 140 100       383 $clean = !defined $class unless defined $clean;
385 140 100       260 $lock = 1 unless defined $lock;
386 140 100       240 $recurse = 1 if $arrays;
387 140         198 my $hash = shift;
388 140 100       287 if ( ref $hash ne 'HASH' ) {
389 31 100 100     110 if ( $arrays && ref $hash eq 'ARRAY' ) {
    100          
390 14         32 for (@$hash)
391 21 100 100     112 { h2o( -arrays, -lock=>$lock, ($ro?-ro:()), $_ )
    100          
392             if ref eq 'HASH' || ref eq 'ARRAY' }
393 14         41 return $hash;
394             }
395             elsif ( $pass ) {
396 9 100       32 if ( $pass eq 'ref' ) {
397 6 100 100     51 return $hash if !defined $hash || ref $hash;
398 2         214 croak "this h2o call only accepts references or undef";
399             }
400             else { # $pass must be 'undef' due to checks above
401 3 100       16 return $hash if !defined $hash;
402 2         192 croak "this h2o call only accepts a plain hashref or undef";
403             }
404             }
405 8         620 croak "this h2o call only accepts plain hashrefs";
406             }
407 109 100 100     333 croak "h2o with additional keys doesn't make sense with -ro" if $ro && @_ && !$new;
      100        
408 108         207 my %ak = map {$_=>1} @_;
  22         70  
409 108         307 my %keys = map {$_=>1} @_, keys %$hash;
  130         334  
410             croak "h2o hashref may not contain a key named DESTROY"
411 108 100 100     1400 if exists $keys{DESTROY} && ( $destroy || $clean || !$meth || ref $hash->{DESTROY} ne 'CODE' );
      100        
412             croak "h2o hashref may not contain a key named new if you use the -new option"
413 96 100 100     274 if $new && exists $keys{new};
414 95 100 100     314 croak "h2o can't turn off -lock if -ro is on" if $ro && !$lock;
415 94 100       172 if ($recurse) {
416 44         92 for (values %$hash) {
417 52 100 100     186 if ( $arrays && ref eq 'ARRAY' )
    100          
418 9 100       36 { h2o(-arrays, -lock=>$lock, ($ro?-ro:()), $_) }
419             elsif ( ref eq 'HASH' )
420 15 100       165 { h2o(-recurse, -lock=>$lock, ($ro?-ro:()), $_) }
421             }
422             }
423 94 100       373 my $pack = defined $class ? $class : sprintf('Util::H2O::_%x', $hash+0);
424 94         256 for my $k (keys %keys) {
425             my $sub = $ro
426 29 100   29   9239 ? sub { my $self = shift; croak "this object is read-only" if @_; exists $self->{$k} ? $self->{$k} : undef }
  29 100       1277  
  19         110  
427 112 100   103   395 : sub { my $self = shift; $self->{$k} = shift if @_; $self->{$k} };
  103 100       11219  
  103         245  
  103         503  
428 112 100 100     301 if ( $meth && ref $$hash{$k} eq 'CODE' )
429 18 100       68 { $sub = delete $$hash{$k}; $ak{$k} or delete $keys{$k} }
  18         42  
430 1     1   958 { no strict 'refs'; *{"${pack}::$k"} = $sub } ## no critic (ProhibitNoStrict)
  1         2  
  1         173  
  112         131  
  112         130  
  112         794  
431             }
432 94 100 100     319 if ( $destroy || $clean ) {
433             my $sub = sub {
434 78 100 100 78   47625 $destroy and ( eval { $destroy->($_[0]); 1 } or carp $@ ); ## no critic (ProhibitMixedBooleanOperators)
  6         17  
  5         765  
435 78 100       370 if ( $clean ) {
436 76 100       172 if ( $pack eq 'main' ) { carp "h2o refusing to delete package \"main\"" }
  1         100  
437 75         170 else { delete_package($pack) }
438             }
439 79         238 };
440 1     1   7 { no strict 'refs'; *{$pack.'::DESTROY'} = $sub } ## no critic (ProhibitNoStrict)
  1         10  
  1         192  
  79         116  
  79         104  
  79         388  
441             }
442 94 100       183 if ( $new ) {
443             my $sub = sub {
444 17     17   3797 my $class = shift;
445 17 100       46 $class = ref $class if ref $class;
446 17 100       190 croak "Odd number of elements in argument list" if @_%2;
447 16         35 my $self = {@_};
448 16   100     148 exists $keys{$_} or croak "Unknown argument '$_'" for keys %$self;
449 15         30 bless $self, $class;
450 15 100       36 if ($ro) { lock_hashref $self }
  2 100       6  
451 12         38 elsif ($lock) { lock_ref_keys $self, keys %keys }
452 15         309 return $self;
453 15         53 };
454 1     1   8 { no strict 'refs'; *{$pack.'::new'} = $sub } ## no critic (ProhibitNoStrict)
  1         3  
  1         79  
  15         21  
  15         23  
  15         58  
455             }
456 1 100   1   20 if ($isa) { no strict 'refs'; @{$pack.'::ISA'} = @$isa } ## no critic (ProhibitNoStrict)
  1         3  
  1         439  
  94         167  
  6         9  
  6         121  
457 94         247 bless $hash, $pack;
458 94 100       205 if ($ro) { lock_hashref $hash }
  10 100       33  
459 79         257 elsif ($lock) { lock_ref_keys $hash, keys %keys }
460 94         2606 return $hash;
461             }
462              
463             =head2 C, I<$h2object>>
464              
465             This function takes an object as created by C and turns it back
466             into a hashref by making shallow copies of the object hash and any
467             nested objects that may have been created via C<-recurse>,
468             C<-arrays>, or created manually. This function is recursive by
469             default because for a non-recursive operation you can simply write:
470             C<{%$h2object}> (making a shallow copy).
471              
472             Unlike C, this function returns a new hashref instead of
473             modifying the given variable in place (unless what you give this
474             function is not an C object, in which case it will just be
475             returned unchanged). Similarly, if you specify the C<-arrays> option,
476             shallow copies of arrays will be returned in place of the original
477             ones, with C applied to the elements.
478              
479             B that this function operates only on objects in the default
480             package - it does not step into plain hashrefs, it does not step into
481             arrayrefs unless you specify C<-arrays>, nor does it operate on
482             objects created with the C<-class> or C<-classify> options. Also be
483             aware that because methods created via C<-meth> are removed from the
484             object hash, these will disappear in the resulting hashref.
485              
486             This function was added in v0.18.
487              
488             =head3 C<@opts>
489              
490             If you specify an option with a value multiple times, only the last
491             one will take effect.
492              
493             =over
494              
495             =item C<-arrays>
496              
497             If you specify this option, nested arrayrefs are descended into as well.
498              
499             This option was added in v0.20.
500              
501             =back
502              
503             =cut
504              
505             sub o2h { ## no critic (RequireArgUnpacking)
506 40     40 1 2031 my ($arrays);
507 40   100     258 while ( @_ && $_[0] && !ref$_[0] && $_[0]=~/^-/ ) {
      100        
      100        
508 26 100       56 if ($_[0] eq '-arrays' ) { $arrays = shift }
  25         112  
509 1         83 else { croak "unknown option to o2h: '$_[0]'" }
510             }
511 39 100       148 croak "missing argument to o2h" unless @_;
512 38         52 my $h2o = shift;
513 38 100       210 croak "too many arguments to o2h" if @_;
514 36 100       77 my @args = ( $arrays ? (-arrays) : () );
515 36 100 100     218 if ( ref($h2o) =~ $_PACKAGE_REGEX )
    100          
516 17         42 { return { map { $_ => o2h(@args, $h2o->{$_}) } keys %$h2o } }
  21         69  
517             elsif ( $arrays && ref $h2o eq 'ARRAY' )
518 6         13 { return [ map { o2h(@args, $_) } @$h2o ] }
  9         29  
519 13         133 return $h2o;
520             }
521              
522             1;
523             __END__