File Coverage

blib/lib/Util/H2O.pm
Criterion Covered Total %
statement 132 134 100.0
branch 109 110 100.0
condition 63 63 100.0
subroutine 16 16 100.0
pod 2 2 100.0
total 322 325 100.0


line stmt bran cond sub pod time code
1             #!perl
2             package Util::H2O;
3 1     1   87261 use warnings;
  1         2  
  1         28  
4 1     1   4 use strict;
  1         4  
  1         18  
5 1     1   4 use Exporter 'import';
  1         1  
  1         30  
6 1     1   4 use Carp;
  1         2  
  1         52  
7 1     1   429 use Symbol qw/delete_package/;
  1         731  
  1         118  
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.18';
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   5 if ( $] ge '5.008009' ) {
55 1         454 require Hash::Util;
56 1         2604 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.
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<-meth>
99              
100             Any code references present in the hash at the time of this function
101             call will be turned into methods. Because these methods are installed
102             into the object's package, they can't be changed later by modifying
103             the hash.
104              
105             To avoid confusion when iterating over the hash, the hash entries
106             that were turned into methods are removed from the hash. The key is
107             also removed from the "allowed keys" (see the C<-lock> option),
108             I you specify it in C<@additional_keys>. In that case, you
109             can change the value of that key completely independently of the
110             method with the same name.
111              
112             =item C<< -class => I >>
113              
114             Specify the class name into which to bless the object (as opposed to
115             the default: a generated, unique package name in C).
116              
117             I If you use this option, C<-clean> defaults to I,
118             meaning that the package will stay in Perl's symbol table and use
119             memory accordingly, and since this function installs the accessors in
120             the package every time it is called, if you re-use the same package
121             name, you will get "redefined" warnings. Therefore, if you want to
122             create multiple objects in the same package, you should probably use
123             C<-new>.
124              
125             If you wanted to generate a unique package name in a different package,
126             you could use:
127             C<< h2o -class => sprintf('My::Class::Name::_%x', $hash+0), $hash >>,
128             perhaps even in combination with C<< -isa => 'My::Class::Name' >>.
129             However, keep in mind that you shouldn't step into another class' namespace
130             without knowing that this won't cause conflicts, and also that not using the
131             default class names means that functions like C will no longer identify
132             the objects as coming from C.
133              
134             =item C<< -classify => I >>
135              
136             In the form C<< -classify => I >>, this is simply the short
137             form of the options C<< -new, -meth, -class => I >>.
138              
139             As of v0.16, in the special form C<< -classify => I<$hashref> >>, where the
140             C<-classify> B be the B option in C<@opts> before the
141             L|/"$hashref">, it is the same as
142             C<< -new, -meth, -class => __PACKAGE__, I<$hashref> >> - that is, the current
143             package's name is used as the custom class name. It does not make sense to use
144             this outside of an explicit package, since your class will be named C
.
145             With this option, the C example in the L can be written like
146             the following, which can be useful if you want to add more things to the
147             C, or perhaps if you want to write your methods as regular Cs:
148              
149             {
150             package Point;
151             use Util::H2O;
152             h2o -classify, {
153             angle => sub { my $self = shift; atan2($self->y, $self->x) }
154             }, qw/ x y /;
155             }
156              
157             Note C will remain in the package's namespace, one possibility is that you
158             could load L after you load this module.
159              
160             =item C<< -isa => I >>
161              
162             Convenience option to set the L|perlvar/"@ISA"> variable in the package
163             of the object, so that the object inherits from that/those package(s).
164             This option was added in v0.14.
165              
166             B The methods created by C will not call superclass methods.
167             This means the parent class' C method(s) are not called, and any
168             accessors generated from hash keys are blindly overriden.
169              
170             =item C<-new>
171              
172             Generates a constructor named C in the package. The constructor
173             works as a class and instance method, and dies if it is given any
174             arguments that it doesn't know about. If you want more advanced
175             features, like required arguments, validation, or other
176             initialization, you should probably L
177             to something like L instead.
178              
179             =item C<< -destroy => I >>
180              
181             Allows you to specify a custom destructor. This coderef will be called from the
182             object's actual C in void context with the first argument being the
183             same as the first argument to the C method. Errors will be converted
184             to warnings.
185             This option was added in v0.14.
186              
187             =item C<< -clean => I >>
188              
189             Whether or not to clean up the generated package when the object is
190             destroyed. Defaults to I when C<-class> is specified, I
191             otherwise. If this is I, be aware that the packages will stay
192             in Perl's symbol table and use memory accordingly.
193              
194             As of v0.16, this module will refuse to delete the package if it
195             is named C
.
196              
197             =item C<< -lock => I >>
198              
199             Whether or not to use L's C to prevent
200             modifications to the hash's keyset. Defaults to I.
201             The C<-nolock> option is provided as a short form of C<< -lock=>0 >>.
202              
203             Keysets of objects created by the constructor generated by the
204             C<-new> option are also locked. Versions of this module before
205             v0.12 did not lock the keysets of new objects.
206              
207             Note that on really old Perls, that is, before Perl v5.8.9,
208             L and its C are not available, so the hash
209             is never locked on those versions of Perl. Versions of this module
210             before v0.06 did not lock the keyset.
211             Versions of this module as of v0.12 issue a warning on old Perls.
212              
213             =item C<-nolock>
214              
215             Short form of the option C<< -lock=>0 >>.
216              
217             =item C<-ro>
218              
219             Makes the entire hash read-only using L's C and the
220             generated accessors will also throw an error if you try to change values. In
221             other words, this makes the object and the underlying hash immutable.
222              
223             You cannot specify any C<@additional_keys> with this option enabled unless you
224             also use the C<-new> option - the additional keys will then only be useful as
225             arguments to the constructor. This option can't be used with C<-nolock> or
226             C<< -lock=>0 >>.
227              
228             This option was added in v0.12. Using this option will not work and cause a
229             warning when used on really old Perls (before v5.8.9), because this
230             functionality was not yet available there.
231              
232             =item C<< -pass => "ref" I "undef" >>
233              
234             When this option is set to C<"undef"> (that's the string C<"undef">, I
235             C itself!), then passing a value of C for the C<$hashref> will
236             not result in a fatal error, the value will simply be passed through.
237              
238             When this option is set to the string C<"ref">, then any value other than a
239             plain hashref that is a reference, including objects, plus C as above,
240             will be passed through without modification. Any hashes nested inside of these
241             references will not be descended into, even when C<-recurse> is specified.
242              
243             This option was added in v0.18.
244              
245             =back
246              
247             =head3 C<$hashref>
248              
249             You must supply a plain (unblessed) hash reference here. Be aware
250             that this function I modify the original hashref(s) by blessing
251             it and locking its keyset (the latter can be disabled with the
252             C<-lock> option), and if you use C<-meth> or C<-classify>, keys whose
253             values are code references will be removed.
254              
255             An accessor will be set up for each key in the hash; note that the
256             keys must of course be valid Perl identifiers for you to be able to
257             call the method normally.
258              
259             The following keys will be treated specially by this module. Please note that
260             there are further keys that are treated specially by Perl and/or that other
261             code may expect to be special, such as L's C. See also
262             L and the references therein.
263              
264             =over
265              
266             =item C
267              
268             This key is not allowed in the hash if the C<-new> option is on.
269              
270             =item C
271              
272             This key is not allowed except if all of the following apply:
273              
274             =over
275              
276             =item *
277              
278             C<-destroy> is not used,
279              
280             =item *
281              
282             C<-clean> is off (which happens by default when you use C<-class>),
283              
284             =item *
285              
286             C<-meth> is on, and
287              
288             =item *
289              
290             the value of the key C is a coderef.
291              
292             =back
293              
294             Versions of this module before v0.14 allowed a C key in more
295             circumstances (whenever C<-clean> was off).
296              
297             =item C
298              
299             If your hash contains a key named C, or this key is present in
300             C<@additional_keys>, this module will set up a method called C, which
301             is subject to Perl's normal autoloading behavior - see L
302             and L. Without the C<-meth> option, you will get a
303             "catch-all" accessor to which all method calls to unknown method names will go,
304             and with C<-meth> enabled (which is implied by C<-classify>), you can install
305             your own custom C handler by passing a coderef as the value for this
306             key - see L. However, it is important to note that
307             enabling autoloading removes any typo protection on method names!
308              
309             =back
310              
311             =head3 C<@additional_keys>
312              
313             Methods will be set up for these keys even if they do not exist in the hash.
314              
315             Please see the list of keys that are treated specially above.
316              
317             =head3 Returns
318              
319             The (now blessed and optionally locked) C<$hashref>.
320              
321             =cut
322              
323             our $_PACKAGE_REGEX = qr/\AUtil::H2O::_[0-9A-Fa-f]+\z/;
324              
325             sub h2o { ## no critic (RequireArgUnpacking, ProhibitExcessComplexity)
326 116     116 1 33161 my ($recurse,$meth,$class,$isa,$destroy,$new,$clean,$lock,$ro,$pass);
327 116   100     858 while ( @_ && $_[0] && !ref$_[0] && $_[0]=~/^-/ ) {
      100        
      100        
328 148 100       538 if ($_[0] eq '-recurse' ) { $recurse = shift } ## no critic (ProhibitCascadingIfElse)
  21 100       90  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
329 20         83 elsif ($_[0] eq '-meth' ) { $meth = shift }
330 9 100       44 elsif ($_[0] eq '-clean') { $clean = (shift, shift()?1:0) }
331 14 100       55 elsif ($_[0] eq '-lock' ) { $lock = (shift, shift()?1:0) }
332 4         6 elsif ($_[0] eq '-nolock'){ $lock = 0; shift }
  4         16  
333 10         45 elsif ($_[0] eq '-ro' ) { $ro = shift }
334 8         36 elsif ($_[0] eq '-new' ) { $new = shift }
335             elsif ($_[0] eq '-pass' ) {
336 17         22 $pass = (shift, shift);
337 17 100 100     381 croak "invalid -pass option value (must be 'undef' or 'ref')"
      100        
338             if !defined $pass || $pass ne 'undef' && $pass ne 'ref';
339             }
340             elsif ($_[0] eq '-class') {
341 16         25 $class = (shift, shift);
342 16 100 100     300 croak "invalid -class option value"
      100        
343             if !defined $class || ref $class || !length $class;
344             }
345             elsif ($_[0] eq '-classify') {
346 11         16 $class = (shift, shift);
347 11 100       33 if ( ref $class eq 'HASH' ) { unshift @_, $class; $class = caller; }
  3         5  
  3         5  
348 11 100 100     232 croak "invalid -classify option value"
      100        
349             if !defined $class || ref $class || !length $class;
350 8         8 $meth = 1; $new = 1;
  8         35  
351             }
352             elsif ($_[0] eq '-isa') {
353 7         11 $isa = (shift, shift);
354 7 100 100     86 croak "invalid -isa option value" if !( ref($isa) eq 'ARRAY' || !ref($isa) );
355 6 100       45 $isa = [$isa] unless ref $isa;
356             }
357             elsif ($_[0] eq '-destroy') {
358 8         13 $destroy = (shift, shift);
359 8 100       155 croak "invalid -destroy option value" unless ref $destroy eq 'CODE';
360             }
361 3         220 else { croak "unknown option to h2o: '$_[0]'" }
362             }
363 100 100       210 $clean = !defined $class unless defined $clean;
364 100 100       142 $lock = 1 unless defined $lock;
365 100         114 my $hash = shift;
366 100 100       174 if ( ref $hash ne 'HASH' ) {
367 16 100       23 if ( $pass ) {
368 9 100       16 if ( $pass eq 'ref' ) {
369 6 100 100     31 return $hash if !defined $hash || ref $hash;
370 2         145 croak "this h2o call only accepts references or undef";
371             }
372             else { # $pass must be 'undef' due to checks above
373 3 100       16 return $hash if !defined $hash;
374 2         153 croak "this h2o call only accepts a plain hashref or undef";
375             }
376             }
377 7         437 croak "this h2o call only accepts plain hashrefs";
378             }
379 84 100 100     227 croak "h2o with additional keys doesn't make sense with -ro" if $ro && @_ && !$new;
      100        
380 83         121 my %ak = map {$_=>1} @_;
  22         60  
381 83         177 my %keys = map {$_=>1} @_, keys %$hash;
  102         238  
382             croak "h2o hashref may not contain a key named DESTROY"
383 83 100 100     1094 if exists $keys{DESTROY} && ( $destroy || $clean || !$meth || ref $hash->{DESTROY} ne 'CODE' );
      100        
384             croak "h2o hashref may not contain a key named new if you use the -new option"
385 71 100 100     201 if $new && exists $keys{new};
386 70 100 100     193 croak "h2o can't turn off -lock if -ro is on" if $ro && !$lock;
387 69 100 100     94 if ($recurse) { ref eq 'HASH' and h2o(-recurse,-lock=>$lock,($ro?-ro:()),$_) for values %$hash }
  19 100       77  
388 69 100       217 my $pack = defined $class ? $class : sprintf('Util::H2O::_%x', $hash+0);
389 69         130 for my $k (keys %keys) {
390             my $sub = $ro
391 22 100   22   6290 ? sub { my $self = shift; croak "this object is read-only" if @_; exists $self->{$k} ? $self->{$k} : undef }
  22 100       527  
  14         66  
392 84 100   83   240 : sub { my $self = shift; $self->{$k} = shift if @_; $self->{$k} };
  83 100       8243  
  83         173  
  83         276  
393 84 100 100     177 if ( $meth && ref $$hash{$k} eq 'CODE' )
394 18 100       51 { $sub = delete $$hash{$k}; $ak{$k} or delete $keys{$k} }
  18         37  
395 1     1   711 { no strict 'refs'; *{"${pack}::$k"} = $sub } ## no critic (ProhibitNoStrict)
  1         2  
  1         127  
  84         83  
  84         89  
  84         425  
396             }
397 69 100 100     183 if ( $destroy || $clean ) {
398             my $sub = sub {
399 53 100 100 53   30153 $destroy and ( eval { $destroy->($_[0]); 1 } or carp $@ ); ## no critic (ProhibitMixedBooleanOperators)
  6         17  
  5         645  
400 53 100       231 if ( $clean ) {
401 51 100       86 if ( $pack eq 'main' ) { carp "h2o refusing to delete package \"main\"" }
  1         82  
402 50         94 else { delete_package($pack) }
403             }
404 54         145 };
405 1     1   6 { no strict 'refs'; *{$pack.'::DESTROY'} = $sub } ## no critic (ProhibitNoStrict)
  1         2  
  1         172  
  54         66  
  54         50  
  54         211  
406             }
407 69 100       110 if ( $new ) {
408             my $sub = sub {
409 17     17   2985 my $class = shift;
410 17 100       50 $class = ref $class if ref $class;
411 17 100       180 croak "Odd number of elements in argument list" if @_%2;
412 16         28 my $self = {@_};
413 16   100     128 exists $keys{$_} or croak "Unknown argument '$_'" for keys %$self;
414 15         21 bless $self, $class;
415 15 100       29 if ($ro) { lock_hashref $self }
  2 100       4  
416 12         28 elsif ($lock) { lock_ref_keys $self, keys %keys }
417 15         251 return $self;
418 15         40 };
419 1     1   5 { no strict 'refs'; *{$pack.'::new'} = $sub } ## no critic (ProhibitNoStrict)
  1         2  
  1         43  
  15         20  
  15         14  
  15         40  
420             }
421 1 100   1   12 if ($isa) { no strict 'refs'; @{$pack.'::ISA'} = @$isa } ## no critic (ProhibitNoStrict)
  1         1  
  1         192  
  69         97  
  6         7  
  6         96  
422 69         142 bless $hash, $pack;
423 69 100       122 if ($ro) { lock_hashref $hash }
  8 100       19  
424 56         139 elsif ($lock) { lock_ref_keys $hash, keys %keys }
425 69         1469 return $hash;
426             }
427              
428             =head2 C>
429              
430             This function takes an object as created by C and turns it back into a
431             hashref by making shallow copies of the object hash and any nested objects that
432             may have been created via C<-recurse> (or created manually). This function is
433             recursive by default because for a non-recursive operation you can simply
434             write: C<{%$h2object}> (making a shallow copy). Unlike C, this function
435             returns a new hashref instead of modifying the given variable in place (unless
436             what you give this function is not an C object, in which case it will just
437             be returned unchanged).
438              
439             B that this function operates only on objects in the default package - it
440             does not step into plain arrayrefs or hashrefs, nor does it operate on objects
441             created with the C<-class> or C<-classify> options. Also be aware that because
442             methods created via C<-meth> are removed from the object hash, these will
443             disappear in the resulting hashref.
444              
445             This function was added in v0.18.
446              
447             =cut
448              
449             sub o2h {
450 12     12 1 533 my $h2o = shift;
451 12 100       63 return ref($h2o) =~ $_PACKAGE_REGEX ? { map { $_ => o2h($h2o->{$_}) } keys %$h2o } : $h2o;
  9         18  
452             }
453              
454             1;
455             __END__