File Coverage

blib/lib/Util/H2O/More.pm
Criterion Covered Total %
statement 141 152 92.7
branch 18 20 90.0
condition 2 5 40.0
subroutine 28 31 90.3
pod 14 17 82.3
total 203 225 90.2


line stmt bran cond sub pod time code
1 10     10   1115013 use strict;
  10         121  
  10         302  
2 10     10   52 use warnings;
  10         18  
  10         422  
3              
4             package Util::H2O::More;
5 10     10   4213 use parent q/Exporter/;
  10         2805  
  10         60  
6              
7             our $VERSION = q{0.3.1};
8              
9             our @EXPORT_OK = (qw/baptise opt2h2o h2o o2h d2o o2d o2h2o ini2h2o ini2o h2o2ini o2ini Getopt2h2o ddd dddie tr4h2o yaml2o/);
10              
11 10     10   6008 use Util::H2O ();
  10         69025  
  10         273  
12              
13 10     10   85 use feature 'state';
  10         20  
  10         12164  
14              
15             # quick hack to export h2o, uses proper
16             # Util::H2O::h2o called with full namespace
17             sub h2o {
18 64     64 1 2710 return Util::H2O::h2o @_;
19             }
20              
21             # maintains basically a count to create non-colliding
22             # unique $pkg names (basically what Util::H2O::h2o does
23             # if $pkg is not specified using -class
24             # monatomically increasing uuid
25             sub _uuid {
26 9     9   38 state $uuid = 0;
27 9         72 return ++$uuid;
28             }
29              
30             # non-recursive option
31             sub baptise ($$@) {
32 9     9 1 8543 my ( $ref, $pkg, @default_accessors );
33 9         18 my $pos0 = shift;
34              
35             # check pos0 for '-recurse'
36 9 100       30 if ( $pos0 eq q{-recurse} ) {
37 7         21 ( $ref, $pkg, @default_accessors ) = @_;
38             }
39             else {
40 2         4 $ref = $pos0;
41 2         7 ( $pkg, @default_accessors ) = @_;
42             }
43              
44 9         15 my $self;
45 9         24 my $real_pkg = sprintf qq{%s::_%s}, $pkg, _uuid;
46              
47             # uses -isa to inherit from $pkg; -class to bless with a package name
48             # derived from $pkg
49 9 100       36 if ( $pos0 eq q{-recurse} ) {
50 7         28 $self = h2o -recurse, -isa => $pkg, -class => $real_pkg, $ref, @default_accessors;
51             }
52             else {
53 2         7 $self = h2o -isa => $pkg, -class => $real_pkg, $ref, @default_accessors;
54             }
55              
56 9         2535 return $self;
57             }
58              
59             # make keys legal for use as accessor, provides original keys via "__og_keys" accessor
60             sub tr4h2o($) {
61 1     1 1 107 my $hash_ref = shift;
62 1         2 my $new_hashref = {};
63              
64             # List::Util::pairmap was not happy being require'd for some reason
65             # so iterate and replace keys explicitly; store original key in resulting
66             # hashref via __og_keys
67 1         5 foreach my $og_k ( keys %$hash_ref ) {
68 2         3 my $k = $og_k;
69 2         6 $k =~ tr/a-zA-Z0-9/_/c;
70 2         4 $new_hashref->{$k} = $hash_ref->{$og_k};
71              
72             # save old key via __og_keys
73 2         5 $new_hashref->{__og_keys}->{$k} = $og_k;
74             }
75 1         4 return $new_hashref;
76             }
77              
78             # preconditioner for use with Getopt::Long flags; returns just the flag name given
79             # a list of option descriptors, e.g., qw/option1=s option2=i option3/;
80              
81             # Getopt to keys
82             sub opt2h2o(@) {
83 2     2 1 120 my @getopt_def = @_;
84 2         5 my @flags_only = map { m/([^=|\s]+)/g; $1 } @getopt_def;
  8         27  
  8         21  
85 2         9 return @flags_only;
86             }
87              
88             # wrapper around opt2h2o (yeah!)
89             sub Getopt2h2o(@) {
90 1     1 1 4586 my ( $ARGV_ref, $defaults, @opts ) = @_;
91 1   50     4 $defaults //= {};
92 1         5 my $o = h2o $defaults, opt2h2o(@opts);
93 1         166 require Getopt::Long;
94 1         5 Getopt::Long::GetOptionsFromArray( $ARGV_ref, $o, @opts ); # Note, @ARGV is passed by reference
95 1         795 return $o;
96             }
97              
98             # general form of method used to give accessors to Config::Tiny in Util::H2O's
99             # POD documentation
100             sub o2h2o($) {
101 6     6 1 1714 my $ref = shift;
102 6         11 return h2o -recurse, { %{$ref} };
  6         34  
103             }
104              
105             # more specific helper app that uses Config::Tiny->read and o2h2o to get a config
106             # object back from an .ini; requries Config::Tiny
107             sub ini2h2o($) {
108 4     4 1 2214 my $filename = shift;
109 4         24 require Config::Tiny;
110 4         23 return o2h2o( Config::Tiny->read($filename) );
111             }
112              
113             # back compat
114             sub ini2o($) {
115 2     2 0 2243 return ini2h2o(shift);
116             }
117              
118             # write out the INI file
119             sub h2o2ini($$) {
120 2     2 1 2993 my ( $config, $filename ) = @_;
121 2         48 require Config::Tiny;
122 2         12 return Config::Tiny->new( Util::H2O::o2h $config)->write($filename);
123             }
124              
125             # back compat
126             sub o2ini($$) {
127 1     1 0 2993 return h2o2ini( shift, shift );
128             }
129              
130             # return a dereferences hash (non-recursive); reverse of `h2o'
131             sub o2h($) {
132 18     18 1 2598 $Util::H2O::_PACKAGE_REGEX = qr/::_[0-9A-Fa-f]+\z/; # makes internal package name more generic for baptise created references
133 18         47 my $ref = Util::H2O::o2h @_;
134 18 50       1221 if ( ref $ref ne q{HASH} ) {
135 0         0 die qq{Could not fully remove top-level reference. Probably an issue with \$Util::H2O_PACKAGE_REGEX\n};
136             }
137 18         93 return $ref;
138             }
139              
140             sub d2o($); # forward declaration to get rid of "too early" warning
141             sub a2o($);
142              
143             sub d2o($) {
144 171     171 1 4417 my $thing = shift;
145 171         258 my $isa = ref $thing;
146 171 100       414 if ( $isa eq q{ARRAY} ) {
    100          
147 24         54 a2o $thing;
148 24         129 foreach my $element (@$thing) {
149 76         148 d2o $element;
150             }
151             }
152             elsif ( $isa eq q{HASH} ) {
153 37         118 foreach my $keys ( keys %$thing ) {
154 74         142 d2o( $thing->{$keys} );
155             }
156              
157             # package level wrapper, so this can be monkey patched
158             # if so desired, per documentation
159 37         127 h2o $thing;
160             }
161 171         4771 return $thing;
162             }
163              
164             # blesses ARRAY ref as a container and gives it some virtual methods
165             # useful in the context of containing HASH refs that get objectified
166             # by h2o
167             sub a2o($) {
168 10     10   88 no strict 'refs';
  10         20  
  10         11393  
169              
170 24     24 1 34 my $array_ref = shift;
171              
172             # uses lexical scop of the 'if' to a bless $array_ref (an ARRAY ref)
173             # and assigns to it some virtual methods for making dealing with
174             # the "lists of C references easier, as a container
175              
176 24         187 my $a2o_pkg = sprintf( qq{%s::__a2o_%d::vmethods}, __PACKAGE__, int rand 100_000_000 ); # internal a2o
177              
178 24         188 bless $array_ref, $a2o_pkg;
179              
180             ## add vmethod to wrap around array_refs
181              
182             # return item at index INDEX
183 24     0   110 my $GET = sub { my ( $self, $i ) = @_; return $self->[$i]; };
  0         0  
  0         0  
184 24         35 *{"${a2o_pkg}::get"} = $GET;
  24         118  
185              
186             # return rereferenced ARRAY
187 24     2   74 my $ALL = sub { my $self = shift; return @$self; };
  2         2160  
  2         8  
188 24         29 *{"${a2o_pkg}::all"} = $ALL;
  24         98  
189              
190             # returns value returned by the 'scalar' keyword
191 24     14   77 my $SCALAR = sub { my $self = shift; return scalar @$self; };
  14         223  
  14         62  
192 24         37 *{"${a2o_pkg}::scalar"} = $SCALAR;
  24         101  
193              
194             # 'push' will apply "d2o" to all elements pushed
195 24     6   75 my $PUSH = sub { my ( $self, @i ) = @_; d2o \@i; push @$self, @i; return \@i };
  6         3354  
  6         23  
  6         20  
  6         16  
196 24         31 *{"${a2o_pkg}::push"} = $PUSH;
  24         100  
197              
198             # 'pop' intentionally does NOT apply "o2d" to anyarray_ref pop'd
199 24     4   56 my $POP = sub { my $self = shift; return pop @$self };
  4         10525  
  4         9  
200 24         34 *{"${a2o_pkg}::pop"} = $POP;
  24         84  
201              
202             # 'unshift' will apply "d2o" to all elements unshifted
203 24     6   88 my $UNSHIFT = sub { my ( $self, @i ) = @_; d2o \@i; unshift @$self, @i; return \@i };
  6         3728  
  6         21  
  6         13  
  6         18  
204 24         35 *{"${a2o_pkg}::unshift"} = $UNSHIFT;
  24         99  
205              
206             # 'shift' intentionally does NOT apply "o2d" to anyarray_ref shift'd
207 24     8   66 my $SHIFT = sub { my $self = shift; return shift @$self };
  8         17950  
  8         18  
208 24         33 *{"${a2o_pkg}::shift"} = $SHIFT;
  24         86  
209              
210 24         50 return $array_ref;
211             }
212              
213             # includes internal dereferencing so to be compatible
214             # with the behavior of Util::H2O::o2h
215             sub o2d($); # forward declaration to get rid of "too early" warning
216              
217             sub o2d($) {
218 31     31 1 811 my $thing = shift;
219 31 100       58 return $thing if not $thing;
220 30         43 my $isa = ref $thing;
221 30 100       73 if ( $isa =~ m/^Util::H2O::More::__a2o/ ) {
    100          
222 4         12 my @_thing = @$thing;
223 4         9 $thing = \@_thing;
224 4         7 foreach my $element (@$thing) {
225 22         256 $element = o2d $element;
226             }
227             }
228             elsif ( $isa =~ m/^Util::H2O::_/ ) {
229 3         10 foreach my $key ( keys %$thing ) {
230 6         61 $thing->{$key} = o2d $thing->{$key};
231             }
232 3         69 $thing = Util::H2O::o2h $thing;
233             }
234 30         217 return Util::H2O::o2h $thing;
235             }
236              
237             # handy, poor man's debug wrappers
238              
239             sub ddd(@) {
240 0     0 1 0 require Data::Dumper;
241 0         0 foreach my $ref (@_) {
242 0         0 print STDERR Data::Dumper::Dumper($ref);
243             }
244             }
245              
246             sub dddie(@) {
247 0     0 1 0 require Data::Dumper;
248 0         0 foreach my $ref (@_) {
249 0         0 print STDERR Data::Dumper::Dumper($ref);
250             }
251 0         0 die qq{died due to use of dddie};
252             }
253              
254             # YAML configuration support - may return more than 1 reference
255             sub yaml2o($) {
256 3     3 0 3354 require YAML;
257 3         7197 my $file_or_yaml = shift; # may be a file or a string
258 3         7 my @yaml = (); # yaml can have multiple objects serialized, via ---
259              
260             # determine if YAML or file name
261 3         20 my @lines = split /\n/, $file_or_yaml;
262              
263             # if a file, use YAML::LoadFile
264 3 50 33     16 if ( @lines == 1 and -e $file_or_yaml ) {
    100          
265 0         0 @yaml = YAML::LoadFile($file_or_yaml);
266             }
267              
268             # if not a file, assume YAML string and use YAML::Load
269             elsif ($lines[0] eq q{---}) {
270 2         7 @yaml = YAML::Load($file_or_yaml);
271             }
272              
273             # die because not supported content $file_or_yaml - it is neither
274             else {
275 1         9 die qq{Provided parameter looks like neither a file name nor a valid YAML snippet.\n};
276             }
277              
278             # iterate over 1 or more serialized objects that were deserialized
279             # from the YAML, applie C to it due to the potential presence
280             # of ARRAY references
281 2         31381 my @obs = ();
282 2         6 foreach my $y (@yaml) {
283 4         10 push @obs, d2o $y;
284             }
285              
286 2         14 return @obs;
287             }
288              
289             # NOTE: no o2yaml, but can add one if somebody needs it ... please file an issue on the tracker (GH these days)
290              
291             1;
292              
293             __END__