File Coverage

blib/lib/Util/H2O.pm
Criterion Covered Total %
statement 155 157 100.0
branch 141 142 100.0
condition 91 91 100.0
subroutine 16 16 100.0
pod 2 2 100.0
total 405 408 100.0


line stmt bran cond sub pod time code
1             #!perl
2             package Util::H2O;
3 1     1   142755 use warnings;
  1         2  
  1         79  
4 1     1   7 use strict;
  1         2  
  1         31  
5 1     1   8 use Exporter 'import';
  1         1  
  1         40  
6 1     1   22 use Carp;
  1         4  
  1         124  
7 1     1   7 use Symbol qw/delete_package/;
  1         3  
  1         256  
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.24';
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   8 if ( $] ge '5.008009' ) {
55 1         837 require Hash::Util;
56 1         5302 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 162     162 1 236329 my ($recurse,$arrays,$meth,$class,$isa,$destroy,$new,$clean,$lock,$ro,$pass);
349 162   100     1655 while ( @_ && $_[0] && !ref$_[0] && $_[0]=~/^-/ ) {
      100        
      100        
350 233 100       1297 if ($_[0] eq '-recurse' ) { $recurse = shift } ## no critic (ProhibitCascadingIfElse)
  21 100       127  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
351 45         263 elsif ($_[0] eq '-arrays'){ $arrays = shift }
352 20         126 elsif ($_[0] eq '-meth' ) { $meth = shift }
353 9 100       98 elsif ($_[0] eq '-clean') { $clean = (shift, shift()?1:0) }
354 50 100       279 elsif ($_[0] eq '-lock' ) { $lock = (shift, shift()?1:0) }
355 4         9 elsif ($_[0] eq '-nolock'){ $lock = 0; shift }
  4         23  
356 13         78 elsif ($_[0] eq '-ro' ) { $ro = shift }
357 8         50 elsif ($_[0] eq '-new' ) { $new = shift }
358             elsif ($_[0] eq '-pass' ) {
359 18         38 $pass = (shift, shift);
360 18 100 100     687 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         43 $class = (shift, shift);
365 16 100 100     614 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       36 if ( ref $class eq 'HASH' ) { unshift @_, $class; $class = caller; }
  3         9  
  3         9  
371 11 100 100     423 croak "invalid -classify option value"
      100        
372             if !defined $class || ref $class || !length $class;
373 8         17 $meth = 1; $new = 1;
  8         48  
374             }
375             elsif ($_[0] eq '-isa') {
376 7         21 $isa = (shift, shift);
377 7 100 100     154 croak "invalid -isa option value" if !( ref($isa) eq 'ARRAY' || !ref($isa) );
378 6 100       49 $isa = [$isa] unless ref $isa;
379             }
380             elsif ($_[0] eq '-destroy') {
381 8         18 $destroy = (shift, shift);
382 8 100       318 croak "invalid -destroy option value" unless ref $destroy eq 'CODE';
383             }
384 3         411 else { croak "unknown option to h2o: '$_[0]'" }
385             }
386 146 100       468 $clean = !defined $class unless defined $clean;
387 146 100       341 $lock = 1 unless defined $lock;
388 146 100       367 $recurse = 1 if $arrays;
389 146         253 my $hash = shift;
390 146 100       462 if ( ref $hash ne 'HASH' ) {
391 32 100 100     133 if ( $arrays && ref $hash eq 'ARRAY' ) {
    100          
392 15         34 for (@$hash)
393 23 100 100     160 { h2o( -arrays, -lock=>$lock, ($ro?-ro:()), $_ )
    100          
394             if ref eq 'HASH' || ref eq 'ARRAY' }
395 15         51 return $hash;
396             }
397             elsif ( $pass ) {
398 9 100       28 if ( $pass eq 'ref' ) {
399 6 100 100     50 return $hash if !defined $hash || ref $hash;
400 2         311 croak "this h2o call only accepts references or undef";
401             }
402             else { # $pass must be 'undef' due to checks above
403 3 100       15 return $hash if !defined $hash;
404 2         333 croak "this h2o call only accepts a plain hashref or undef";
405             }
406             }
407 8         967 croak "this h2o call only accepts plain hashrefs";
408             }
409 114 100 100     514 croak "h2o with additional keys doesn't make sense with -ro" if $ro && @_ && !$new;
      100        
410 113         292 my %ak = map {$_=>1} @_;
  22         94  
411 113         359 my %keys = map {$_=>1} @_, keys %$hash;
  140         512  
412             croak "h2o hashref may not contain a key named DESTROY"
413 113 100 100     2040 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 101 100 100     436 if $new && exists $keys{new};
416 100 100 100     452 croak "h2o can't turn off -lock if -ro is on" if $ro && !$lock;
417 99 100       232 if ($recurse) {
418 48         114 for (values %$hash) {
419 59 100 100     272 if ( $arrays && ref eq 'ARRAY' )
    100          
420 10 100       53 { h2o(-arrays, -lock=>$lock, ($ro?-ro:()), $_) }
421             elsif ( ref eq 'HASH' )
422 17 100       112 { h2o($arrays?-arrays:-recurse, -lock=>$lock, ($ro?-ro:()), $_) }
    100          
423             }
424             }
425 99 100       495 my $pack = defined $class ? $class : sprintf('Util::H2O::_%x', $hash+0);
426 99         273 for my $k (keys %keys) {
427             my $sub = $ro
428 29 100   29   12103 ? sub { my $self = shift; croak "this object is read-only" if @_; exists $self->{$k} ? $self->{$k} : undef }
  29 100       1646  
  19         142  
429 122 100   113   679 : sub { my $self = shift; $self->{$k} = shift if @_; $self->{$k} };
  113 100       17765  
  113         371  
  113         654  
430 122 100 100     396 if ( $meth && ref $$hash{$k} eq 'CODE' )
431 18 100       100 { $sub = delete $$hash{$k}; $ak{$k} or delete $keys{$k} }
  18         62  
432 1     1   1447 { no strict 'refs'; *{"${pack}::$k"} = $sub } ## no critic (ProhibitNoStrict)
  1         3  
  1         315  
  122         197  
  122         209  
  122         1038  
433             }
434 99 100 100     471 if ( $destroy || $clean ) {
435             my $sub = sub {
436 84 100 100 84   84500 $destroy and ( eval { $destroy->($_[0]); 1 } or carp $@ ); ## no critic (ProhibitMixedBooleanOperators)
  6         24  
  5         1046  
437 84 100       391 if ( $clean ) {
438 82 100       223 if ( $pack eq 'main' ) { carp "h2o refusing to delete package \"main\"" }
  1         200  
439 81         299 else { delete_package($pack) }
440             }
441 84         344 };
442 1     1   16 { no strict 'refs'; *{$pack.'::DESTROY'} = $sub } ## no critic (ProhibitNoStrict)
  1         12  
  1         292  
  84         142  
  84         119  
  84         544  
443             }
444 99 100       229 if ( $new ) {
445             my $sub = sub {
446 17     17   5230 my $class = shift;
447 17 100       60 $class = ref $class if ref $class;
448 17 100       244 croak "Odd number of elements in argument list" if @_%2;
449 16         43 my $self = {@_};
450 16   100     203 exists $keys{$_} or croak "Unknown argument '$_'" for keys %$self;
451 15         33 bless $self, $class;
452 15 100       46 if ($ro) { lock_hashref $self }
  2 100       6  
453 12         38 elsif ($lock) { lock_ref_keys $self, keys %keys }
454 15         396 return $self;
455 15         64 };
456 1     1   9 { no strict 'refs'; *{$pack.'::new'} = $sub } ## no critic (ProhibitNoStrict)
  1         3  
  1         64  
  15         30  
  15         22  
  15         71  
457             }
458 1 100   1   7 if ($isa) { no strict 'refs'; @{$pack.'::ISA'} = @$isa } ## no critic (ProhibitNoStrict)
  1         2  
  1         705  
  99         196  
  6         13  
  6         125  
459 99         330 bless $hash, $pack;
460 99 100       254 if ($ro) { lock_hashref $hash }
  10 100       36  
461 84         363 elsif ($lock) { lock_ref_keys $hash, keys %keys }
462 99         3645 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             =item C<-->
504              
505             This string ends the option processing, allowing you to pass scalar values to
506             C that would otherwise be interpreted as options.
507              
508             The C function is special-cased such that a call C returns
509             C<"--"> instead of throwing an error.
510              
511             This was added in v0.24 in order to fix a bug with scalars beginning with
512             C<"-"> in earlier versions of this module. Users of C are advised to
513             upgrade.
514              
515             =back
516              
517             =cut
518              
519             sub o2h { ## no critic (RequireArgUnpacking)
520 54     54 1 6625 my ($arrays);
521 54 100 100     204 unless ( @_==1 && $_[0] && !ref$_[0] && $_[0]eq'--' ) { ## no critic (ProhibitNegativeExpressionsInUnlessAndUntilConditions)
      100        
      100        
522 53   100     417 while ( @_ && $_[0] && !ref$_[0] && $_[0]=~/^-/ ) {
      100        
      100        
523 70 100       170 if ($_[0] eq '-arrays' ) { $arrays = shift }
  30 100       166  
524 38         61 elsif ($_[0] eq '--') { shift; last }
  38         54  
525 2         268 else { croak "unknown option to o2h: '$_[0]'" }
526             }
527             }
528 52 100       408 croak "missing argument to o2h" unless @_;
529 50         89 my $h2o = shift;
530 50 100       326 croak "too many arguments to o2h" if @_;
531 48 100       161 my @args = ( ( $arrays ? (-arrays) : () ), '--' );
532 48 100 100     326 if ( ref($h2o) =~ $_PACKAGE_REGEX )
    100          
533 19         51 { return { map { $_ => o2h(@args, $h2o->{$_}) } keys %$h2o } }
  25         82  
534             elsif ( $arrays && ref $h2o eq 'ARRAY' )
535 7         17 { return [ map { o2h(@args, $_) } @$h2o ] }
  11         26  
536 22         145 return $h2o;
537             }
538              
539             1;
540             __END__