File Coverage

blib/lib/Data/Util/PurePerl.pm
Criterion Covered Total %
statement 383 388 98.7
branch 174 184 94.5
condition 80 81 98.7
subroutine 94 94 100.0
pod 32 34 94.1
total 763 781 97.7


line stmt bran cond sub pod time code
1             package Data::Util::PurePerl;
2              
3             die qq{Don't use Data::Util::PurePerl directly, use Data::Util instead.\n} # ' for poor editors
4             if caller() ne 'Data::Util';
5              
6             package
7             Data::Util;
8              
9 22     22   71 use strict;
  22         26  
  22         506  
10 22     22   67 use warnings;
  22         22  
  22         446  
11              
12             #use warnings::unused;
13              
14 22     22   66 use Scalar::Util ();
  22         21  
  22         205  
15 22     22   982 use overload ();
  22         833  
  22         15458  
16              
17             sub _croak{
18 75     75   4077 require Data::Util::Error;
19 75         192 goto &Data::Util::Error::croak;
20             }
21             sub _fail{
22 64     64   263 my($name, $value) = @_;
23 64         101 _croak(sprintf 'Validation failed: you must supply %s, not %s', $name, neat($value));
24             }
25              
26             sub _overloaded{
27 423   100 423   1795 return Scalar::Util::blessed($_[0])
28             && overload::Method($_[0], $_[1]);
29             }
30              
31             sub is_scalar_ref{
32 92   100 92 1 4386 return ref($_[0]) eq 'SCALAR' || ref($_[0]) eq 'REF' || _overloaded($_[0], '${}');
33             }
34             sub is_array_ref{
35 164   100 164 1 2620 return ref($_[0]) eq 'ARRAY' || _overloaded($_[0], '@{}');
36             }
37             sub is_hash_ref{
38 194   100 194 1 2813 return ref($_[0]) eq 'HASH' || _overloaded($_[0], '%{}');
39             }
40             sub is_code_ref{
41 106   100 106 1 1238 return ref($_[0]) eq 'CODE' || _overloaded($_[0], '&{}');
42             }
43             sub is_glob_ref{
44 65   100 65 1 1177 return ref($_[0]) eq 'GLOB' || _overloaded($_[0], '*{}');
45             }
46             sub is_regex_ref{
47 3     3 0 214 return ref($_[0]) eq 'Regexp';
48             }
49             sub is_rx{
50 2     2 1 9 return ref($_[0]) eq 'Regexp';
51             }
52              
53             sub is_instance{
54 31     31 1 3471 my($obj, $class) = @_;
55 31 100       58 _fail('a class name', $class)
56             unless is_string($class);
57              
58 27   100     186 return Scalar::Util::blessed($obj) && $obj->isa($class);
59             }
60             sub is_invocant{
61 13     13 1 388 my($x) = @_;
62 13 100       26 if(ref $x){
63 3         11 return !!Scalar::Util::blessed($x);
64             }
65             else{
66 10         11 return !!get_stash($x);
67             }
68             }
69              
70              
71             sub scalar_ref{
72 10 100 100 10 1 1268 return ref($_[0]) eq 'SCALAR' || ref($_[0]) eq 'REF' || _overloaded($_[0], '${}')
73             ? $_[0] : _fail('a SCALAR reference', $_[0]);
74              
75             }
76             sub array_ref{
77 94 100 100 94 1 2180 return ref($_[0]) eq 'ARRAY' || _overloaded($_[0], '@{}')
78             ? $_[0] : _fail('an ARRAY reference', $_[0]);
79             }
80             sub hash_ref{
81 6 100 100 6 1 1063 return ref($_[0]) eq 'HASH' || _overloaded($_[0], '%{}')
82             ? $_[0] : _fail('a HASH reference', $_[0]);
83             }
84             sub code_ref{
85 279 100 100 279 1 1420 return ref($_[0]) eq 'CODE' || _overloaded($_[0], '&{}')
86             ? $_[0] : _fail('a CODE reference', $_[0]);
87             }
88             sub glob_ref{
89 5 100 100 5 1 739 return ref($_[0]) eq 'GLOB' || _overloaded($_[0], '*{}')
90             ? $_[0] : _fail('a GLOB reference', $_[0]);
91             }
92             sub regex_ref{
93 1 50   1 0 704 return ref($_[0]) eq 'Regexp'
94             ? $_[0] : _fail('a regular expression reference', $_[0]);
95             }
96             sub rx{
97 2 100   2 1 37 return ref($_[0]) eq 'Regexp'
98             ? $_[0] : _fail('a regular expression reference', $_[0]);
99             }
100              
101             sub instance{
102 11     11 1 429 my($obj, $class) = @_;
103              
104 11 100       16 _fail('a class name', $class)
105             unless is_string($class);
106              
107 7 100 100     48 return Scalar::Util::blessed($obj) && $obj->isa($class)
108             ? $obj : _fail("an instance of $class", $obj);
109             }
110              
111             sub invocant{
112 15     15 1 148 my($x) = @_;
113 15 100       34 if(ref $x){
    100          
114 3 100       10 if(Scalar::Util::blessed($x)){
115 2         9 return $x;
116             }
117             }
118             elsif(is_string($x)){
119 9 100       13 if(get_stash($x)){
120 7         10 $x =~ s/^:://;
121 7         12 $x =~ s/(?:main::)+//;
122 7         37 return $x;
123             }
124             }
125 6         11 _fail('an invocant', $x);
126             }
127              
128             sub is_value{
129 11   100 11 1 4579 return defined($_[0]) && !ref($_[0]) && ref(\$_[0]) ne 'GLOB';
130             }
131             sub is_string{
132 22     22   102 no warnings 'uninitialized';
  22         32  
  22         4201  
133 450   100 450 1 5608 return !ref($_[0]) && ref(\$_[0]) ne 'GLOB' && length($_[0]) > 0;
134             }
135              
136             sub is_number{
137 125 100 100 125 1 7436 return 0 if !defined($_[0]) || ref($_[0]);
138              
139 122         763 return $_[0] =~ m{
140             \A \s*
141             [+-]?
142             (?= \d | \.\d)
143             \d*
144             (\.\d*)?
145             (?: [Ee] (?: [+-]? \d+) )?
146             \s* \z
147             }xms;
148             }
149              
150             sub is_integer{
151 54 100 100 54 1 7339 return 0 if !defined($_[0]) || ref($_[0]);
152              
153 50         240 return $_[0] =~ m{
154             \A \s*
155             [+-]?
156             \d+
157             \s* \z
158             }xms;
159             }
160              
161             sub get_stash{
162 71     71 1 6635 my($invocant) = @_;
163              
164 71 100       224 if(Scalar::Util::blessed($invocant)){
    100          
165 22     22   91 no strict 'refs';
  22         23  
  22         6571  
166 1         1 return \%{ref($invocant) . '::'};
  1         4  
167             }
168             elsif(!is_string($invocant)){
169 7         17 return undef;
170             }
171              
172 63         121 $invocant =~ s/^:://;
173              
174 63         103 my $pack = *main::;
175 63         134 foreach my $part(split /::/, $invocant){
176 75 100       250 return undef unless $pack = $pack->{$part . '::'};
177             }
178 52         56 return *{$pack}{HASH};
  52         160  
179             }
180              
181             sub anon_scalar{
182 12     12 1 2864 my($s) = @_;
183 12         33 return \$s; # not \$_[0]
184             }
185              
186             sub neat{
187 170     170 1 1985 my($s) = @_;
188              
189 170 100       360 if(ref $s){
    100          
190 48 100       139 if(ref($s) eq 'CODE'){
    100          
191 3         9 return sprintf '\\&%s(0x%x)', scalar(get_code_info($s)), Scalar::Util::refaddr($s);
192             }
193             elsif(ref($s) eq 'Regexp'){
194 1         5 return qq{qr{$s}};
195             }
196 44         120 return overload::StrVal($s);
197             }
198             elsif(defined $s){
199 98 100       123 return "$s" if is_number($s);
200 57 100       107 return "$s" if is_glob_ref(\$s);
201              
202 50         218 require B;
203 50         287 return B::perlstring($s);
204             }
205             else{
206 24         115 return 'undef';
207             }
208             }
209              
210              
211             sub install_subroutine{
212 28 100   28 1 10082 _croak('Usage: install_subroutine(package, name => code, ...)') unless @_;
213              
214 27         24 my $into = shift;
215 27 100       47 is_string($into) or _fail('a package name', $into);
216              
217 26 100       71 my $param = mkopt_hash(@_ == 1 ? shift : \@_, 'install_subroutine', 'CODE');
218              
219 23         22 while(my($as, $code) = each %{$param}){
  49         118  
220 29 100       41 defined($code) or _fail('a CODE reference', $code);
221              
222 22     22   84 my $slot = do{ no strict 'refs'; \*{ $into . '::' . $as } };
  22         36  
  22         1207  
  28         20  
  28         20  
  28         71  
223              
224 28 100       27 if(defined &{$slot}){
  28         46  
225 8         247 warnings::warnif(redefine => "Subroutine $as redefined");
226             }
227              
228 22     22   72 no warnings 'redefine';
  22         23  
  22         5131  
229 28         336 *{$slot} = \&{$code};
  26         56  
  28         104  
230             }
231 20         94 return;
232             }
233             sub uninstall_subroutine {
234 17 100   17 1 10009 _croak('Usage: uninstall_subroutine(package, name, ...)') unless @_;
235              
236 16         16 my $package = shift;
237              
238 16 100       43 is_string($package) or _fail('a package name', $package);
239 15 100       25 my $stash = get_stash($package) or return 0;
240              
241 14 100 100     59 my $param = mkopt_hash(@_ == 1 && is_hash_ref($_[0]) ? shift : \@_, 'install_subroutine', 'CODE');
242              
243 13         56 require B;
244              
245 13         14 while(my($name, $specified_code) = each %{$param}){
  36         364  
246 24         30 my $glob = $stash->{$name};
247              
248 24 100       46 if(ref(\$glob) ne 'GLOB'){
249 9 100       14 if(ref $glob) {
250 6 100       12 if(Scalar::Util::reftype $glob eq 'CODE'){
251 3 100 100     10 if(defined $specified_code &&
252             $specified_code != $glob) {
253 1         2 next;
254             }
255             }
256             else {
257 3         50 warnings::warnif(misc => "Constant subroutine $name uninstalled");
258             }
259             }
260 7         146 delete $stash->{$name};
261 7         7 next;
262             }
263              
264 15         19 my $code = *{$glob}{CODE};
  15         23  
265 15 100       28 if(not defined $code){
266 2         4 next;
267             }
268              
269 13 100 100     39 if(defined $specified_code && $specified_code != $code){
270 1         2 next;
271             }
272              
273 12 50       68 if(B::svref_2object($code)->CONST){
274 0         0 warnings::warnif(misc => "Constant subroutine $name uninstalled");
275             }
276              
277 12         21 delete $stash->{$name};
278              
279 22     22   82 my $newglob = do{ no strict 'refs'; \*{$package . '::' . $name} }; # vivify
  22         24  
  22         4360  
  12         9  
  12         12  
  12         54  
280              
281             # copy all the slot except for CODE
282 12         20 foreach my $slot( qw(SCALAR ARRAY HASH IO FORMAT) ){
283 60 100       37 *{$newglob} = *{$glob}{$slot} if defined *{$glob}{$slot};
  15         27  
  15         19  
  60         142  
284             }
285             }
286              
287 12         241 return;
288             }
289              
290             sub get_code_info{
291 11     11 1 1566 my($code) = @_;
292              
293 11 100       19 is_code_ref($code) or _fail('a CODE reference', $code);
294              
295 10         41 require B;
296 10         10 my $gv = B::svref_2object(\&{$code})->GV;
  10         60  
297 10 50       34 return unless $gv->isa('B::GV');
298 10 100       99 return wantarray ? ($gv->STASH->NAME, $gv->NAME) : join('::', $gv->STASH->NAME, $gv->NAME);
299             }
300              
301             sub get_code_ref{
302 30     30 1 16198 my($package, $name, @flags) = @_;
303              
304 30 100       51 is_string($package) or _fail('a package name', $package);
305 28 100       44 is_string($name) or _fail('a subroutine name', $name);
306              
307 27 100       50 if(@flags){
308 2 50       3 if(grep{ $_ eq '-create' } @flags){
  2         7  
309 22     22   87 no strict 'refs';
  22         21  
  22         1537  
310 2         2 return \&{$package . '::' . $name};
  2         22  
311             }
312             else{
313 0         0 _fail('a flag', @flags);
314             }
315             }
316              
317 25 100       36 my $stash = get_stash($package) or return undef;
318              
319 21 100       54 if(defined(my $glob = $stash->{$name})){
320 16 100       28 if(ref(\$glob) eq 'GLOB'){
321 12         9 return *{$glob}{CODE};
  12         68  
322             }
323             else{ # a stub or special constant
324 22     22   75 no strict 'refs';
  22         46  
  22         15744  
325 4         1 return *{$package . '::' . $name}{CODE};
  4         30  
326             }
327             }
328 5         20 return undef;
329             }
330              
331             sub curry{
332 42     42 1 15610 my $is_method = !is_code_ref($_[0]);
333              
334 42         1300 my $proc;
335 42 100       67 $proc = shift if !$is_method;
336              
337 42         44 my $args = \@_;
338              
339 42         32 my @tmpl;
340              
341 42         29 my $i = 0;
342 42         31 my $max_ph = -1;
343 42         26 my $min_ph = 0;
344              
345 42         49 foreach my $arg(@_){
346 79 100 100     77 if(is_scalar_ref($arg) && is_integer($$arg)){
    100 100        
347 25         55 push @tmpl, sprintf '$_[%d]', $$arg;
348              
349 25 50       25 if($$arg >= 0){
350 25 100       42 $max_ph = $$arg if $$arg > $max_ph;
351             }
352             else{
353 0 0       0 $min_ph = $$arg if $$arg < $min_ph;
354             }
355             }
356             elsif(defined($arg) && (\$arg) == \*_){
357 14         15 push @tmpl, '@_[$max_ph .. $#_ + $min_ph]';
358             }
359             else{
360 40         463 push @tmpl, sprintf '$args->[%d]', $i;
361             }
362 79         120 $i++;
363             }
364              
365 42         29 $max_ph++;
366              
367 42         81 my($pkg, $file, $line, $hints, $bitmask) = (caller 0 )[0, 1, 2, 8, 9];
368 42         661 my $body = sprintf <<'END_CXT', $pkg, $line, $file;
369             BEGIN{ $^H = $hints; ${^WARNING_BITS} = $bitmask; }
370             package %s;
371             #line %s %s
372             END_CXT
373              
374 42 100       50 if($is_method){
375 14         14 my $selfp = shift @tmpl;
376 14         14 $proc = shift @tmpl;
377 14 100       44 $body .= sprintf q{ sub {
378             my $self = %s;
379             my $method = %s;
380             $self->$method(%s);
381             } }, $selfp, defined($proc) ? $proc : 'undef', join(q{,}, @tmpl);
382             }
383             else{
384 28         55 $body .= sprintf q{ sub { $proc->(%s) } }, join q{,}, @tmpl;
385             }
386 42 50   4   2385 eval $body or die $@;
  5     3   10  
  5     2   857  
  4     2   7  
  4     2   338  
  3     2   3  
  3     2   255  
  2     2   3  
  2     2   132  
  2     2   3  
  2     2   137  
  2     2   4  
  2     2   142  
  2     2   2  
  2     2   136  
  2     2   3  
  2     2   139  
  2     2   4  
  2     2   136  
  2     2   3  
  2     2   147  
  2     2   4  
  2     2   145  
  2     2   3  
  2     2   147  
  2     2   4  
  2     2   138  
  2     2   2  
  2     2   137  
  2     2   5  
  2     2   130  
  2     2   4  
  2     2   143  
  2     2   4  
  2     2   160  
  2     2   4  
  2     2   170  
  2     2   4  
  2     2   150  
  2     1   5  
  2     1   148  
  2         4  
  2         133  
  2         4  
  2         125  
  2         4  
  2         129  
  2         5  
  2         133  
  2         4  
  2         120  
  2         4  
  2         113  
  2         4  
  2         126  
  2         3  
  2         116  
  2         4  
  2         118  
  2         4  
  2         163  
  2         4  
  2         135  
  2         3  
  2         127  
  2         3  
  2         134  
  2         4  
  2         127  
  2         4  
  2         130  
  2         4  
  2         142  
  2         4  
  2         134  
  2         4  
  2         121  
  2         3  
  2         120  
  2         4  
  2         133  
  1         2  
  1         40  
  1         2  
  1         41  
387             }
388              
389 0     5   0 BEGIN{
390 22     22   85 our %modifiers;
391              
392 22         21 my $initializer;
393             $initializer = sub{
394 4         1696 require Hash::Util::FieldHash::Compat;
395 4         5930 Hash::Util::FieldHash::Compat::fieldhash(\%modifiers);
396 4         60 undef $initializer;
397 22         72 };
398              
399             sub modify_subroutine{
400 51     51 1 20720 my $code = code_ref shift;
401              
402 50 100       96 if((@_ % 2) != 0){
403 1         3 _croak('Odd number of arguments for modify_subroutine()');
404             }
405 49         81 my %args = @_;
406              
407 49         35 my(@before, @around, @after);
408              
409 49 100       83 @before = map{ code_ref $_ } @{array_ref delete $args{before}} if exists $args{before};
  41         40  
  28         45  
410 48 100       72 @around = map{ code_ref $_ } @{array_ref delete $args{around}} if exists $args{around};
  41         35  
  28         35  
411 47 100       62 @after = map{ code_ref $_ } @{array_ref delete $args{after}} if exists $args{after};
  42         37  
  29         37  
412              
413 46 50       70 if(%args){
414 0         0 _fail('a modifier property', join ', ', keys %args);
415             }
416              
417 46         119 my %props = (
418             before => \@before,
419             around => \@around,
420             after => \@after,
421             current_ref => \$code,
422             );
423              
424             #$code = curry($_, (my $tmp = $code), *_) for @around;
425 46         67 for my $ar_code(reverse @around){
426 40         29 my $next = $code;
427 40     42   94 $code = sub{ $ar_code->($next, @_) };
  42         9766  
428             }
429 46         120 my($pkg, $file, $line, $hints, $bitmask) = (caller 0)[0, 1, 2, 8, 9];
430              
431 46         668 my $context = sprintf <<'END_CXT', $pkg, $line, $file;
432             BEGIN{ $^H = $hints; ${^WARNING_BITS} = $bitmask; }
433             package %s;
434             #line %s %s(modify_subroutine)
435             END_CXT
436              
437 46 50       2717 my $modified = eval $context . q{sub{
438             $_->(@_) for @before;
439             if(wantarray){ # list context
440             my @ret = $code->(@_);
441             $_->(@_) for @after;
442             return @ret;
443             }
444             elsif(defined wantarray){ # scalar context
445             my $ret = $code->(@_);
446             $_->(@_) for @after;
447             return $ret;
448             }
449             else{ # void context
450             $code->(@_);
451             $_->(@_) for @after;
452             return;
453             }
454             }} or die $@;
455              
456 46 100       476 $initializer->() if $initializer;
457              
458 46         260 $modifiers{$modified} = \%props;
459 46         209 return $modified;
460             }
461              
462 22         49 my %valid_modifiers = map{ $_ => undef } qw(before around after);
  66         8594  
463              
464             sub subroutine_modifier{
465 55     55 1 5972 my $modified = code_ref shift;
466              
467 55         63 my $props_ref = $modifiers{$modified};
468              
469 55 100       84 unless(@_){ # subroutine_modifier($subr) - only checking
470 6         20 return defined $props_ref;
471             }
472 49 100       61 unless($props_ref){ # otherwise, it should be modified subroutines
473 2         4 _fail('a modified subroutine', $modified);
474             }
475              
476 47         55 my($name, @subs) = @_;
477 47 100 66     51 (is_string($name) && exists $valid_modifiers{$name}) or _fail('a modifier property', $name);
478              
479              
480 44         43 my $property = $props_ref->{$name};
481 44 100       62 if(@subs){
482 32 100       37 if($name eq 'after'){
483 9         7 push @{$property}, map{ code_ref $_ } @subs;
  9         12  
  12         12  
484             }
485             else{
486 23         15 unshift @{$property}, reverse map{ code_ref $_ } @subs;
  23         28  
  29         30  
487             }
488              
489 31 100       45 if($name eq 'around'){
490 11         57 my $current_ref = $props_ref->{current_ref};
491 11         11 for my $ar(reverse @subs){
492 14         14 my $base = $$current_ref;
493 14     12   37 $$current_ref = sub{ $ar->($base, @_) };
  12         710  
494             }
495             }
496             }
497 43 100       67 return @{$property} if defined wantarray;
  12         49  
498              
499 31         44 return;
500             }
501             }
502             #
503             # mkopt() and mkopt_hash() are originated from Data::OptList
504             #
505              
506             my %test_for = (
507             CODE => \&is_code_ref,
508             HASH => \&is_hash_ref,
509             ARRAY => \&is_array_ref,
510             SCALAR => \&is_scalar_ref,
511             GLOB => \&is_glob_ref,
512             );
513              
514              
515             sub __is_a {
516 74     74   72 my ($got, $expected) = @_;
517              
518 74 100       104 return scalar grep{ __is_a($got, $_) } @{$expected} if ref $expected;
  15         46  
  7         36  
519              
520 67         62 my $t = $test_for{$expected};
521 67 100       132 return defined($t) ? $t->($got) : is_instance($got, $expected);
522             }
523              
524             sub mkopt{
525 88     88 1 9674 my($opt_list, $moniker, $require_unique, $must_be) = @_;
526              
527 88 100       153 return [] unless defined $opt_list;
528              
529             $opt_list = [
530 87 100       109 map { $_ => (ref $opt_list->{$_} ? $opt_list->{$_} : ()) } keys %$opt_list
  9 100       31  
531             ] if is_hash_ref($opt_list);
532              
533 86 100       143 is_array_ref($opt_list) or _fail('an ARRAY or HASH reference', $opt_list);
534              
535 83         102 my @return;
536             my %seen;
537              
538 83         86 my $vh = is_hash_ref($must_be);
539 83         150 my $validator = $must_be;
540              
541 83 100 100     230 if(defined($validator) && (!$vh && !is_array_ref($validator) && !is_string($validator))){
      100        
542 1         3 _fail('a type name, or ARRAY or HASH reference', $validator);
543             }
544              
545 82         216 for(my $i = 0; $i < @$opt_list; $i++) {
546 116         117 my $name = $opt_list->[$i];
547 116         78 my $value;
548              
549 116 100       125 is_string($name) or _fail("a name in $moniker opt list", $name);
550              
551 113 100 100     367 if($require_unique && $seen{$name}++) {
552 2         7 _croak("Validation failed: Multiple definitions provided for $name in $moniker opt list")
553             }
554              
555 111 100       270 if ($i == $#$opt_list) { $value = undef; }
  28 100       29  
    100          
556 2         2 elsif(not defined $opt_list->[$i+1]) { $value = undef; $i++ }
  2         3  
557 63         69 elsif(ref $opt_list->[$i+1]) { $value = $opt_list->[++$i] }
558 18         15 else { $value = undef; }
559              
560 111 100 100     361 if (defined $value and defined( $vh ? ($validator = $must_be->{$name}) : $validator )){
    100          
561 59 100       74 unless(__is_a($value, $validator)) {
562 6         17 _croak("Validation failed: ".ref($value)."-ref values are not valid for $name in $moniker opt list");
563             }
564             }
565              
566 105         1515 push @return, [ $name => $value ];
567             }
568              
569 71         264 return \@return;
570             }
571              
572             sub mkopt_hash {
573 57     57 1 1880 my($opt_list, $moniker, $must_be) = @_;
574 57 100       102 return {} unless $opt_list;
575              
576 56         45 my %hash = map { $_->[0] => $_->[1] } @{ mkopt($opt_list, $moniker, 1, $must_be) };
  75         171  
  56         75  
577 50         123 return \%hash;
578             }
579              
580             1;
581             __END__