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   72 use strict;
  22         29  
  22         522  
10 22     22   61 use warnings;
  22         20  
  22         429  
11              
12             #use warnings::unused;
13              
14 22     22   104 use Scalar::Util ();
  22         19  
  22         209  
15 22     22   922 use overload ();
  22         740  
  22         15259  
16              
17             sub _croak{
18 75     75   3575 require Data::Util::Error;
19 75         193 goto &Data::Util::Error::croak;
20             }
21             sub _fail{
22 64     64   225 my($name, $value) = @_;
23 64         93 _croak(sprintf 'Validation failed: you must supply %s, not %s', $name, neat($value));
24             }
25              
26             sub _overloaded{
27 423   100 423   1991 return Scalar::Util::blessed($_[0])
28             && overload::Method($_[0], $_[1]);
29             }
30              
31             sub is_scalar_ref{
32 92   100 92 1 4385 return ref($_[0]) eq 'SCALAR' || ref($_[0]) eq 'REF' || _overloaded($_[0], '${}');
33             }
34             sub is_array_ref{
35 164   100 164 1 2594 return ref($_[0]) eq 'ARRAY' || _overloaded($_[0], '@{}');
36             }
37             sub is_hash_ref{
38 194   100 194 1 3285 return ref($_[0]) eq 'HASH' || _overloaded($_[0], '%{}');
39             }
40             sub is_code_ref{
41 106   100 106 1 1365 return ref($_[0]) eq 'CODE' || _overloaded($_[0], '&{}');
42             }
43             sub is_glob_ref{
44 65   100 65 1 1258 return ref($_[0]) eq 'GLOB' || _overloaded($_[0], '*{}');
45             }
46             sub is_regex_ref{
47 3     3 0 292 return ref($_[0]) eq 'Regexp';
48             }
49             sub is_rx{
50 2     2 1 6 return ref($_[0]) eq 'Regexp';
51             }
52              
53             sub is_instance{
54 31     31 1 3496 my($obj, $class) = @_;
55 31 100       64 _fail('a class name', $class)
56             unless is_string($class);
57              
58 27   100     215 return Scalar::Util::blessed($obj) && $obj->isa($class);
59             }
60             sub is_invocant{
61 13     13 1 386 my($x) = @_;
62 13 100       26 if(ref $x){
63 3         11 return !!Scalar::Util::blessed($x);
64             }
65             else{
66 10         12 return !!get_stash($x);
67             }
68             }
69              
70              
71             sub scalar_ref{
72 10 100 100 10 1 1034 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 2544 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 1284 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 1468 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 805 return ref($_[0]) eq 'GLOB' || _overloaded($_[0], '*{}')
90             ? $_[0] : _fail('a GLOB reference', $_[0]);
91             }
92             sub regex_ref{
93 1 50   1 0 752 return ref($_[0]) eq 'Regexp'
94             ? $_[0] : _fail('a regular expression reference', $_[0]);
95             }
96             sub rx{
97 2 100   2 1 34 return ref($_[0]) eq 'Regexp'
98             ? $_[0] : _fail('a regular expression reference', $_[0]);
99             }
100              
101             sub instance{
102 11     11 1 419 my($obj, $class) = @_;
103              
104 11 100       39 _fail('a class name', $class)
105             unless is_string($class);
106              
107 7 100 100     53 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 124 my($x) = @_;
113 15 100       31 if(ref $x){
    100          
114 3 100       10 if(Scalar::Util::blessed($x)){
115 2         10 return $x;
116             }
117             }
118             elsif(is_string($x)){
119 9 100       11 if(get_stash($x)){
120 7         6 $x =~ s/^:://;
121 7         11 $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 4526 return defined($_[0]) && !ref($_[0]) && ref(\$_[0]) ne 'GLOB';
130             }
131             sub is_string{
132 22     22   94 no warnings 'uninitialized';
  22         30  
  22         4171  
133 450   100 450 1 5557 return !ref($_[0]) && ref(\$_[0]) ne 'GLOB' && length($_[0]) > 0;
134             }
135              
136             sub is_number{
137 125 100 100 125 1 7448 return 0 if !defined($_[0]) || ref($_[0]);
138              
139 122         717 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 7807 return 0 if !defined($_[0]) || ref($_[0]);
152              
153 50         252 return $_[0] =~ m{
154             \A \s*
155             [+-]?
156             \d+
157             \s* \z
158             }xms;
159             }
160              
161             sub get_stash{
162 71     71 1 6543 my($invocant) = @_;
163              
164 71 100       220 if(Scalar::Util::blessed($invocant)){
    100          
165 22     22   90 no strict 'refs';
  22         28  
  22         6640  
166 1         1 return \%{ref($invocant) . '::'};
  1         4  
167             }
168             elsif(!is_string($invocant)){
169 7         15 return undef;
170             }
171              
172 63         93 $invocant =~ s/^:://;
173              
174 63         99 my $pack = *main::;
175 63         130 foreach my $part(split /::/, $invocant){
176 75 100       256 return undef unless $pack = $pack->{$part . '::'};
177             }
178 52         46 return *{$pack}{HASH};
  52         157  
179             }
180              
181             sub anon_scalar{
182 12     12 1 2623 my($s) = @_;
183 12         52 return \$s; # not \$_[0]
184             }
185              
186             sub neat{
187 170     170 1 1489 my($s) = @_;
188              
189 170 100       332 if(ref $s){
    100          
190 48 100       135 if(ref($s) eq 'CODE'){
    100          
191 3         5 return sprintf '\\&%s(0x%x)', scalar(get_code_info($s)), Scalar::Util::refaddr($s);
192             }
193             elsif(ref($s) eq 'Regexp'){
194 1         4 return qq{qr{$s}};
195             }
196 44         112 return overload::StrVal($s);
197             }
198             elsif(defined $s){
199 98 100       122 return "$s" if is_number($s);
200 57 100       95 return "$s" if is_glob_ref(\$s);
201              
202 50         194 require B;
203 50         271 return B::perlstring($s);
204             }
205             else{
206 24         116 return 'undef';
207             }
208             }
209              
210              
211             sub install_subroutine{
212 28 100   28 1 10659 _croak('Usage: install_subroutine(package, name => code, ...)') unless @_;
213              
214 27         30 my $into = shift;
215 27 100       48 is_string($into) or _fail('a package name', $into);
216              
217 26 100       89 my $param = mkopt_hash(@_ == 1 ? shift : \@_, 'install_subroutine', 'CODE');
218              
219 23         23 while(my($as, $code) = each %{$param}){
  49         116  
220 29 100       44 defined($code) or _fail('a CODE reference', $code);
221              
222 22     22   89 my $slot = do{ no strict 'refs'; \*{ $into . '::' . $as } };
  22         32  
  22         1280  
  28         17  
  28         22  
  28         72  
223              
224 28 100       26 if(defined &{$slot}){
  28         46  
225 8         253 warnings::warnif(redefine => "Subroutine $as redefined");
226             }
227              
228 22     22   72 no warnings 'redefine';
  22         21  
  22         4959  
229 28         343 *{$slot} = \&{$code};
  26         58  
  28         97  
230             }
231 20         89 return;
232             }
233             sub uninstall_subroutine {
234 17 100   17 1 7929 _croak('Usage: uninstall_subroutine(package, name, ...)') unless @_;
235              
236 16         17 my $package = shift;
237              
238 16 100       22 is_string($package) or _fail('a package name', $package);
239 15 100       22 my $stash = get_stash($package) or return 0;
240              
241 14 100 100     52 my $param = mkopt_hash(@_ == 1 && is_hash_ref($_[0]) ? shift : \@_, 'install_subroutine', 'CODE');
242              
243 13         54 require B;
244              
245 13         13 while(my($name, $specified_code) = each %{$param}){
  36         262  
246 24         30 my $glob = $stash->{$name};
247              
248 24 100       47 if(ref(\$glob) ne 'GLOB'){
249 9 100       16 if(ref $glob) {
250 6 100       11 if(Scalar::Util::reftype $glob eq 'CODE'){
251 3 100 100     10 if(defined $specified_code &&
252             $specified_code != $glob) {
253 1         1 next;
254             }
255             }
256             else {
257 3         46 warnings::warnif(misc => "Constant subroutine $name uninstalled");
258             }
259             }
260 7         137 delete $stash->{$name};
261 7         9 next;
262             }
263              
264 15         13 my $code = *{$glob}{CODE};
  15         24  
265 15 100       26 if(not defined $code){
266 2         4 next;
267             }
268              
269 13 100 100     33 if(defined $specified_code && $specified_code != $code){
270 1         2 next;
271             }
272              
273 12 50       69 if(B::svref_2object($code)->CONST){
274 0         0 warnings::warnif(misc => "Constant subroutine $name uninstalled");
275             }
276              
277 12         22 delete $stash->{$name};
278              
279 22     22   83 my $newglob = do{ no strict 'refs'; \*{$package . '::' . $name} }; # vivify
  22         30  
  22         4162  
  12         11  
  12         7  
  12         53  
280              
281             # copy all the slot except for CODE
282 12         13 foreach my $slot( qw(SCALAR ARRAY HASH IO FORMAT) ){
283 60 100       39 *{$newglob} = *{$glob}{$slot} if defined *{$glob}{$slot};
  15         24  
  15         17  
  60         136  
284             }
285             }
286              
287 12         233 return;
288             }
289              
290             sub get_code_info{
291 11     11 1 1482 my($code) = @_;
292              
293 11 100       19 is_code_ref($code) or _fail('a CODE reference', $code);
294              
295 10         42 require B;
296 10         9 my $gv = B::svref_2object(\&{$code})->GV;
  10         60  
297 10 50       39 return unless $gv->isa('B::GV');
298 10 100       123 return wantarray ? ($gv->STASH->NAME, $gv->NAME) : join('::', $gv->STASH->NAME, $gv->NAME);
299             }
300              
301             sub get_code_ref{
302 30     30 1 15326 my($package, $name, @flags) = @_;
303              
304 30 100       39 is_string($package) or _fail('a package name', $package);
305 28 100       39 is_string($name) or _fail('a subroutine name', $name);
306              
307 27 100       48 if(@flags){
308 2 50       3 if(grep{ $_ eq '-create' } @flags){
  2         9  
309 22     22   85 no strict 'refs';
  22         22  
  22         1681  
310 2         2 return \&{$package . '::' . $name};
  2         18  
311             }
312             else{
313 0         0 _fail('a flag', @flags);
314             }
315             }
316              
317 25 100       30 my $stash = get_stash($package) or return undef;
318              
319 21 100       48 if(defined(my $glob = $stash->{$name})){
320 16 100       26 if(ref(\$glob) eq 'GLOB'){
321 12         10 return *{$glob}{CODE};
  12         58  
322             }
323             else{ # a stub or special constant
324 22     22   79 no strict 'refs';
  22         29  
  22         16004  
325 4         4 return *{$package . '::' . $name}{CODE};
  4         27  
326             }
327             }
328 5         19 return undef;
329             }
330              
331             sub curry{
332 42     42 1 12861 my $is_method = !is_code_ref($_[0]);
333              
334 42         1451 my $proc;
335 42 100       61 $proc = shift if !$is_method;
336              
337 42         38 my $args = \@_;
338              
339 42         33 my @tmpl;
340              
341 42         28 my $i = 0;
342 42         28 my $max_ph = -1;
343 42         23 my $min_ph = 0;
344              
345 42         50 foreach my $arg(@_){
346 79 100 100     80 if(is_scalar_ref($arg) && is_integer($$arg)){
    100 100        
347 25         51 push @tmpl, sprintf '$_[%d]', $$arg;
348              
349 25 50       31 if($$arg >= 0){
350 25 100       41 $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         14 push @tmpl, '@_[$max_ph .. $#_ + $min_ph]';
358             }
359             else{
360 40         475 push @tmpl, sprintf '$args->[%d]', $i;
361             }
362 79         92 $i++;
363             }
364              
365 42         34 $max_ph++;
366              
367 42         83 my($pkg, $file, $line, $hints, $bitmask) = (caller 0 )[0, 1, 2, 8, 9];
368 42         660 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       49 if($is_method){
375 14         15 my $selfp = shift @tmpl;
376 14         14 $proc = shift @tmpl;
377 14 100       42 $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         57 $body .= sprintf q{ sub { $proc->(%s) } }, join q{,}, @tmpl;
385             }
386 42 50   4   2469 eval $body or die $@;
  5     3   9  
  5     2   809  
  4     2   8  
  4     2   336  
  3     2   4  
  3     2   235  
  2     2   3  
  2     2   132  
  2     2   4  
  2     2   140  
  2     2   4  
  2     2   138  
  2     2   4  
  2     2   134  
  2     2   3  
  2     2   132  
  2     2   4  
  2     2   135  
  2     2   4  
  2     2   153  
  2     2   4  
  2     2   149  
  2     2   4  
  2     2   156  
  2     2   4  
  2     2   141  
  2     2   3  
  2     2   141  
  2     2   5  
  2     2   137  
  2     2   4  
  2     2   147  
  2     2   5  
  2     2   172  
  2     2   6  
  2     2   166  
  2     2   4  
  2     2   153  
  2     1   4  
  2     1   150  
  2         4  
  2         132  
  2         5  
  2         125  
  2         4  
  2         131  
  2         4  
  2         123  
  2         4  
  2         121  
  2         4  
  2         116  
  2         4  
  2         164  
  2         4  
  2         115  
  2         4  
  2         121  
  2         4  
  2         196  
  2         4  
  2         129  
  2         4  
  2         131  
  2         4  
  2         130  
  2         4  
  2         144  
  2         3  
  2         135  
  2         5  
  2         117  
  2         5  
  2         135  
  2         3  
  2         121  
  2         4  
  2         120  
  2         4  
  2         131  
  1         2  
  1         40  
  1         2  
  1         39  
387             }
388              
389 0     5   0 BEGIN{
390 22     22   55 our %modifiers;
391              
392 22         35 my $initializer;
393             $initializer = sub{
394 4         1578 require Hash::Util::FieldHash::Compat;
395 4         5663 Hash::Util::FieldHash::Compat::fieldhash(\%modifiers);
396 4         55 undef $initializer;
397 22         68 };
398              
399             sub modify_subroutine{
400 51     51 1 20531 my $code = code_ref shift;
401              
402 50 100       98 if((@_ % 2) != 0){
403 1         2 _croak('Odd number of arguments for modify_subroutine()');
404             }
405 49         80 my %args = @_;
406              
407 49         42 my(@before, @around, @after);
408              
409 49 100       101 @before = map{ code_ref $_ } @{array_ref delete $args{before}} if exists $args{before};
  41         40  
  28         47  
410 48 100       70 @around = map{ code_ref $_ } @{array_ref delete $args{around}} if exists $args{around};
  41         35  
  28         35  
411 47 100       74 @after = map{ code_ref $_ } @{array_ref delete $args{after}} if exists $args{after};
  42         37  
  29         34  
412              
413 46 50       68 if(%args){
414 0         0 _fail('a modifier property', join ', ', keys %args);
415             }
416              
417 46         117 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         65 for my $ar_code(reverse @around){
426 40         29 my $next = $code;
427 40     42   89 $code = sub{ $ar_code->($next, @_) };
  42         9265  
428             }
429 46         114 my($pkg, $file, $line, $hints, $bitmask) = (caller 0)[0, 1, 2, 8, 9];
430              
431 46         660 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       2623 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       477 $initializer->() if $initializer;
457              
458 46         275 $modifiers{$modified} = \%props;
459 46         198 return $modified;
460             }
461              
462 22         47 my %valid_modifiers = map{ $_ => undef } qw(before around after);
  66         8438  
463              
464             sub subroutine_modifier{
465 55     55 1 5809 my $modified = code_ref shift;
466              
467 55         70 my $props_ref = $modifiers{$modified};
468              
469 55 100       74 unless(@_){ # subroutine_modifier($subr) - only checking
470 6         18 return defined $props_ref;
471             }
472 49 100       65 unless($props_ref){ # otherwise, it should be modified subroutines
473 2         3 _fail('a modified subroutine', $modified);
474             }
475              
476 47         54 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         48 my $property = $props_ref->{$name};
481 44 100       59 if(@subs){
482 32 100       35 if($name eq 'after'){
483 9         8 push @{$property}, map{ code_ref $_ } @subs;
  9         9  
  12         10  
484             }
485             else{
486 23         16 unshift @{$property}, reverse map{ code_ref $_ } @subs;
  23         28  
  29         28  
487             }
488              
489 31 100       42 if($name eq 'around'){
490 11         10 my $current_ref = $props_ref->{current_ref};
491 11         10 for my $ar(reverse @subs){
492 14         9 my $base = $$current_ref;
493 14     12   36 $$current_ref = sub{ $ar->($base, @_) };
  12         491  
494             }
495             }
496             }
497 43 100       58 return @{$property} if defined wantarray;
  12         46  
498              
499 31         42 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   62 my ($got, $expected) = @_;
517              
518 74 100       105 return scalar grep{ __is_a($got, $_) } @{$expected} if ref $expected;
  15         44  
  7         31  
519              
520 67         64 my $t = $test_for{$expected};
521 67 100       113 return defined($t) ? $t->($got) : is_instance($got, $expected);
522             }
523              
524             sub mkopt{
525 88     88 1 8594 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       99 map { $_ => (ref $opt_list->{$_} ? $opt_list->{$_} : ()) } keys %$opt_list
  9 100       28  
531             ] if is_hash_ref($opt_list);
532              
533 86 100       132 is_array_ref($opt_list) or _fail('an ARRAY or HASH reference', $opt_list);
534              
535 83         91 my @return;
536             my %seen;
537              
538 83         85 my $vh = is_hash_ref($must_be);
539 83         156 my $validator = $must_be;
540              
541 83 100 100     232 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         211 for(my $i = 0; $i < @$opt_list; $i++) {
546 116         115 my $name = $opt_list->[$i];
547 116         82 my $value;
548              
549 116 100       124 is_string($name) or _fail("a name in $moniker opt list", $name);
550              
551 113 100 100     348 if($require_unique && $seen{$name}++) {
552 2         6 _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       24  
    100          
556 2         2 elsif(not defined $opt_list->[$i+1]) { $value = undef; $i++ }
  2         1  
557 63         64 elsif(ref $opt_list->[$i+1]) { $value = $opt_list->[++$i] }
558 18         15 else { $value = undef; }
559              
560 111 100 100     348 if (defined $value and defined( $vh ? ($validator = $must_be->{$name}) : $validator )){
    100          
561 59 100       82 unless(__is_a($value, $validator)) {
562 6         16 _croak("Validation failed: ".ref($value)."-ref values are not valid for $name in $moniker opt list");
563             }
564             }
565              
566 105         1524 push @return, [ $name => $value ];
567             }
568              
569 71         193 return \@return;
570             }
571              
572             sub mkopt_hash {
573 57     57 1 1513 my($opt_list, $moniker, $must_be) = @_;
574 57 100       96 return {} unless $opt_list;
575              
576 56         42 my %hash = map { $_->[0] => $_->[1] } @{ mkopt($opt_list, $moniker, 1, $must_be) };
  75         160  
  56         80  
577 50         119 return \%hash;
578             }
579              
580             1;
581             __END__