File Coverage

blib/lib/Util/H2O.pm
Criterion Covered Total %
statement 152 154 100.0
branch 137 138 100.0
condition 82 82 100.0
subroutine 16 16 100.0
pod 2 2 100.0
total 389 392 100.0


line stmt bran cond sub pod time code
1             #!perl
2             package Util::H2O;
3 1     1   114608 use warnings;
  1         3  
  1         33  
4 1     1   8 use strict;
  1         2  
  1         20  
5 1     1   8 use Exporter 'import';
  1         3  
  1         24  
6 1     1   5 use Carp;
  1         3  
  1         73  
7 1     1   7 use Symbol qw/delete_package/;
  1         1  
  1         226  
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.22';
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   9 if ( $] ge '5.008009' ) {
55 1         2653 require Hash::Util;
56 1         4983 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, and any subs/methods
210             in those packages may cause "redefined" warnings if the package
211             name is re-used.
212              
213             As of v0.16, this module will refuse to delete the package if it
214             is named C
.
215              
216             =item C<< -lock => I >>
217              
218             Whether or not to use L's C to prevent
219             modifications to the hash's keyset. Defaults to I.
220             The C<-nolock> option is provided as a short form of C<< -lock=>0 >>.
221              
222             Keysets of objects created by the constructor generated by the
223             C<-new> option are also locked. Versions of this module before
224             v0.12 did not lock the keysets of new objects.
225              
226             Note that on really old Perls, that is, before Perl v5.8.9,
227             L and its C are not available, so the hash
228             is never locked on those versions of Perl. Versions of this module
229             before v0.06 did not lock the keyset.
230             Versions of this module as of v0.12 issue a warning on old Perls.
231              
232             =item C<-nolock>
233              
234             Short form of the option C<< -lock=>0 >>.
235              
236             =item C<-ro>
237              
238             Makes the entire hash read-only using L's C and the
239             generated accessors will also throw an error if you try to change values. In
240             other words, this makes the object and the underlying hash immutable.
241              
242             You cannot specify any C<@additional_keys> with this option enabled unless you
243             also use the C<-new> option - the additional keys will then only be useful as
244             arguments to the constructor. This option can't be used with C<-nolock> or
245             C<< -lock=>0 >>.
246              
247             This option was added in v0.12. Using this option will not work and cause a
248             warning when used on really old Perls (before v5.8.9), because this
249             functionality was not yet available there.
250              
251             =item C<< -pass => "ref" I "undef" >>
252              
253             When this option is set to C<"undef"> (that's the string C<"undef">, I
254             C itself!), then passing a value of C for the C<$hashref> will
255             not result in a fatal error, the value will simply be passed through.
256              
257             When this option is set to the string C<"ref">, then any value other than a
258             plain hashref that is a reference, including objects, plus C as above,
259             will be passed through without modification. Any hashes nested inside of these
260             references will not be descended into, even when C<-recurse> is specified.
261             However, C<-arrays> takes precedence over this option, see its documentation.
262              
263             This option was added in v0.18.
264              
265             =back
266              
267             =head3 C<$hashref>
268              
269             You must supply a plain (unblessed) hash reference here, unless
270             you've specified the C<-pass> and/or C<-arrays> options. Be aware
271             that this function I modify the original hashref(s) by blessing
272             it and locking its keyset (the latter can be disabled with the
273             C<-lock> option), and if you use C<-meth> or C<-classify>, keys whose
274             values are code references will be removed.
275             If you use C<-arrays>, the elements of those arrays may also be modified.
276              
277             An accessor will be set up for each key in the hash(es); note that the
278             keys must of course be valid Perl identifiers for you to be able to
279             call the method normally (see also the L).
280              
281             The following keys will be treated specially by this module. Please note that
282             there are further keys that are treated specially by Perl and/or that other
283             code may expect to be special, such as L's C. See also
284             L and the references therein.
285              
286             =over
287              
288             =item C
289              
290             This key is not allowed in the hash if the C<-new> option is on.
291              
292             =item C
293              
294             This key is not allowed except if all of the following apply:
295              
296             =over
297              
298             =item *
299              
300             C<-destroy> is not used,
301              
302             =item *
303              
304             C<-clean> is off (which happens by default when you use C<-class>),
305              
306             =item *
307              
308             C<-meth> is on, and
309              
310             =item *
311              
312             the value of the key C is a coderef.
313              
314             =back
315              
316             Versions of this module before v0.14 allowed a C key in more
317             circumstances (whenever C<-clean> was off).
318              
319             =item C
320              
321             If your hash contains a key named C, or this key is present in
322             C<@additional_keys>, this module will set up a method called C, which
323             is subject to Perl's normal autoloading behavior - see L
324             and L. Without the C<-meth> option, you will get a
325             "catch-all" accessor to which all method calls to unknown method names will go,
326             and with C<-meth> enabled (which is implied by C<-classify>), you can install
327             your own custom C handler by passing a coderef as the value for this
328             key - see L. However, it is important to note that
329             enabling autoloading removes any typo protection on method names!
330              
331             =back
332              
333             =head3 C<@additional_keys>
334              
335             Methods will be set up for these keys even if they do not exist in the hash.
336              
337             Please see the list of keys that are treated specially above.
338              
339             =head3 Returns
340              
341             The (now blessed and optionally locked) C<$hashref>.
342              
343             =cut
344              
345             our $_PACKAGE_REGEX = qr/\AUtil::H2O::_[0-9A-Fa-f]+\z/;
346              
347             sub h2o { ## no critic (RequireArgUnpacking, ProhibitExcessComplexity)
348 161     161 1 43125 my ($recurse,$arrays,$meth,$class,$isa,$destroy,$new,$clean,$lock,$ro,$pass);
349 161   100     1374 while ( @_ && $_[0] && !ref$_[0] && $_[0]=~/^-/ ) {
      100        
      100        
350 233 100       984 if ($_[0] eq '-recurse' ) { $recurse = shift } ## no critic (ProhibitCascadingIfElse)
  21 100       116  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
351 45         249 elsif ($_[0] eq '-arrays'){ $arrays = shift }
352 20         133 elsif ($_[0] eq '-meth' ) { $meth = shift }
353 9 100       63 elsif ($_[0] eq '-clean') { $clean = (shift, shift()?1:0) }
354 50 100       246 elsif ($_[0] eq '-lock' ) { $lock = (shift, shift()?1:0) }
355 4         13 elsif ($_[0] eq '-nolock'){ $lock = 0; shift }
  4         21  
356 13         69 elsif ($_[0] eq '-ro' ) { $ro = shift }
357 8         47 elsif ($_[0] eq '-new' ) { $new = shift }
358             elsif ($_[0] eq '-pass' ) {
359 18         34 $pass = (shift, shift);
360 18 100 100     474 croak "invalid -pass option value (must be 'undef' or 'ref')"
      100        
361             if !defined $pass || $pass ne 'undef' && $pass ne 'ref';
362             }
363             elsif ($_[0] eq '-class') {
364 16         39 $class = (shift, shift);
365 16 100 100     382 croak "invalid -class option value"
      100        
366             if !defined $class || ref $class || !length $class;
367             }
368             elsif ($_[0] eq '-classify') {
369 11         27 $class = (shift, shift);
370 11 100       45 if ( ref $class eq 'HASH' ) { unshift @_, $class; $class = caller; }
  3         11  
  3         7  
371 11 100 100     358 croak "invalid -classify option value"
      100        
372             if !defined $class || ref $class || !length $class;
373 8         13 $meth = 1; $new = 1;
  8         49  
374             }
375             elsif ($_[0] eq '-isa') {
376 7         13 $isa = (shift, shift);
377 7 100 100     112 croak "invalid -isa option value" if !( ref($isa) eq 'ARRAY' || !ref($isa) );
378 6 100       45 $isa = [$isa] unless ref $isa;
379             }
380             elsif ($_[0] eq '-destroy') {
381 8         14 $destroy = (shift, shift);
382 8 100       208 croak "invalid -destroy option value" unless ref $destroy eq 'CODE';
383             }
384 3         256 else { croak "unknown option to h2o: '$_[0]'" }
385             }
386 145 100       359 $clean = !defined $class unless defined $clean;
387 145 100       259 $lock = 1 unless defined $lock;
388 145 100       287 $recurse = 1 if $arrays;
389 145         185 my $hash = shift;
390 145 100       287 if ( ref $hash ne 'HASH' ) {
391 32 100 100     150 if ( $arrays && ref $hash eq 'ARRAY' ) {
    100          
392 15         34 for (@$hash)
393 23 100 100     136 { h2o( -arrays, -lock=>$lock, ($ro?-ro:()), $_ )
    100          
394             if ref eq 'HASH' || ref eq 'ARRAY' }
395 15         80 return $hash;
396             }
397             elsif ( $pass ) {
398 9 100       19 if ( $pass eq 'ref' ) {
399 6 100 100     40 return $hash if !defined $hash || ref $hash;
400 2         225 croak "this h2o call only accepts references or undef";
401             }
402             else { # $pass must be 'undef' due to checks above
403 3 100       13 return $hash if !defined $hash;
404 2         204 croak "this h2o call only accepts a plain hashref or undef";
405             }
406             }
407 8         644 croak "this h2o call only accepts plain hashrefs";
408             }
409 113 100 100     354 croak "h2o with additional keys doesn't make sense with -ro" if $ro && @_ && !$new;
      100        
410 112         219 my %ak = map {$_=>1} @_;
  22         82  
411 112         313 my %keys = map {$_=>1} @_, keys %$hash;
  137         414  
412             croak "h2o hashref may not contain a key named DESTROY"
413 112 100 100     1475 if exists $keys{DESTROY} && ( $destroy || $clean || !$meth || ref $hash->{DESTROY} ne 'CODE' );
      100        
414             croak "h2o hashref may not contain a key named new if you use the -new option"
415 100 100 100     311 if $new && exists $keys{new};
416 99 100 100     354 croak "h2o can't turn off -lock if -ro is on" if $ro && !$lock;
417 98 100       166 if ($recurse) {
418 48         98 for (values %$hash) {
419 59 100 100     250 if ( $arrays && ref eq 'ARRAY' )
    100          
420 10 100       49 { h2o(-arrays, -lock=>$lock, ($ro?-ro:()), $_) }
421             elsif ( ref eq 'HASH' )
422 17 100       101 { h2o($arrays?-arrays:-recurse, -lock=>$lock, ($ro?-ro:()), $_) }
    100          
423             }
424             }
425 98 100       445 my $pack = defined $class ? $class : sprintf('Util::H2O::_%x', $hash+0);
426 98         245 for my $k (keys %keys) {
427             my $sub = $ro
428 29 100   29   9419 ? sub { my $self = shift; croak "this object is read-only" if @_; exists $self->{$k} ? $self->{$k} : undef }
  29 100       942  
  19         131  
429 119 100   112   427 : sub { my $self = shift; $self->{$k} = shift if @_; $self->{$k} };
  112 100       12152  
  112         251  
  112         584  
430 119 100 100     296 if ( $meth && ref $$hash{$k} eq 'CODE' )
431 18 100       65 { $sub = delete $$hash{$k}; $ak{$k} or delete $keys{$k} }
  18         49  
432 1     1   1024 { no strict 'refs'; *{"${pack}::$k"} = $sub } ## no critic (ProhibitNoStrict)
  1         5  
  1         182  
  119         140  
  119         146  
  119         860  
433             }
434 98 100 100     340 if ( $destroy || $clean ) {
435             my $sub = sub {
436 81 100 100 81   48873 $destroy and ( eval { $destroy->($_[0]); 1 } or carp $@ ); ## no critic (ProhibitMixedBooleanOperators)
  6         26  
  5         770  
437 81 100       347 if ( $clean ) {
438 79 100       161 if ( $pack eq 'main' ) { carp "h2o refusing to delete package \"main\"" }
  1         100  
439 78         186 else { delete_package($pack) }
440             }
441 83         280 };
442 1     1   9 { no strict 'refs'; *{$pack.'::DESTROY'} = $sub } ## no critic (ProhibitNoStrict)
  1         2  
  1         216  
  83         150  
  83         105  
  83         411  
443             }
444 98 100       235 if ( $new ) {
445             my $sub = sub {
446 17     17   3868 my $class = shift;
447 17 100       47 $class = ref $class if ref $class;
448 17 100       176 croak "Odd number of elements in argument list" if @_%2;
449 16         37 my $self = {@_};
450 16   100     149 exists $keys{$_} or croak "Unknown argument '$_'" for keys %$self;
451 15         29 bless $self, $class;
452 15 100       34 if ($ro) { lock_hashref $self }
  2 100       6  
453 12         35 elsif ($lock) { lock_ref_keys $self, keys %keys }
454 15         314 return $self;
455 15         55 };
456 1     1   8 { no strict 'refs'; *{$pack.'::new'} = $sub } ## no critic (ProhibitNoStrict)
  1         2  
  1         86  
  15         21  
  15         17  
  15         54  
457             }
458 1 100   1   31 if ($isa) { no strict 'refs'; @{$pack.'::ISA'} = @$isa } ## no critic (ProhibitNoStrict)
  1         2  
  1         423  
  98         170  
  6         10  
  6         133  
459 98         248 bless $hash, $pack;
460 98 100       213 if ($ro) { lock_hashref $hash }
  10 100       32  
461 83         245 elsif ($lock) { lock_ref_keys $hash, keys %keys }
462 98         2770 return $hash;
463             }
464              
465             =head2 C, I<$h2object>>
466              
467             This function takes an object as created by C and turns it back
468             into a hashref by making shallow copies of the object hash and any
469             nested objects that may have been created via C<-recurse>,
470             C<-arrays>, or created manually. This function is recursive by
471             default because for a non-recursive operation you can simply write:
472             C<{%$h2object}> (making a shallow copy).
473              
474             Unlike C, this function returns a new hashref instead of
475             modifying the given variable in place (unless what you give this
476             function is not an C object, in which case it will just be
477             returned unchanged). Similarly, if you specify the C<-arrays> option,
478             shallow copies of arrays will be returned in place of the original
479             ones, with C applied to the elements.
480              
481             B that this function operates only on objects in the default
482             package - it does not step into plain hashrefs, it does not step into
483             arrayrefs unless you specify C<-arrays>, nor does it operate on
484             objects created with the C<-class> or C<-classify> options. Also be
485             aware that because methods created via C<-meth> are removed from the
486             object hash, these will disappear in the resulting hashref.
487              
488             This function was added in v0.18.
489              
490             =head3 C<@opts>
491              
492             If you specify an option with a value multiple times, only the last
493             one will take effect.
494              
495             =over
496              
497             =item C<-arrays>
498              
499             If you specify this option, nested arrayrefs are descended into as well.
500              
501             This option was added in v0.20.
502              
503             =back
504              
505             =cut
506              
507             sub o2h { ## no critic (RequireArgUnpacking)
508 41     41 1 2094 my ($arrays);
509 41   100     272 while ( @_ && $_[0] && !ref$_[0] && $_[0]=~/^-/ ) {
      100        
      100        
510 27 100       53 if ($_[0] eq '-arrays' ) { $arrays = shift }
  26         113  
511 1         81 else { croak "unknown option to o2h: '$_[0]'" }
512             }
513 40 100       154 croak "missing argument to o2h" unless @_;
514 39         50 my $h2o = shift;
515 39 100       254 croak "too many arguments to o2h" if @_;
516 37 100       73 my @args = ( $arrays ? (-arrays) : () );
517 37 100 100     209 if ( ref($h2o) =~ $_PACKAGE_REGEX )
    100          
518 18         52 { return { map { $_ => o2h(@args, $h2o->{$_}) } keys %$h2o } }
  22         83  
519             elsif ( $arrays && ref $h2o eq 'ARRAY' )
520 6         13 { return [ map { o2h(@args, $_) } @$h2o ] }
  9         17  
521 13         102 return $h2o;
522             }
523              
524             1;
525             __END__