File Coverage

blib/lib/Data/Miscellany.pm
Criterion Covered Total %
statement 51 72 70.8
branch 31 48 64.5
condition 15 29 51.7
subroutine 12 17 70.5
pod 11 11 100.0
total 120 177 67.8


line stmt bran cond sub pod time code
1 1     1   28559 use 5.008;
  1         4  
  1         39  
2 1     1   6 use strict;
  1         2  
  1         43  
3 1     1   7 use warnings;
  1         1  
  1         55  
4              
5             package Data::Miscellany;
6             our $VERSION = '1.100850';
7             # ABSTRACT: Collection of miscellaneous subroutines
8              
9 1     1   5 use Exporter qw(import);
  1         1  
  1         1719  
10             our %EXPORT_TAGS = (
11             util => [
12             qw/
13             set_push flex_grep flatten is_deeply eq_array eq_hash
14             is_defined value_of str_value_of class_map trim
15             /
16             ],
17             );
18             our @EXPORT_OK = @{ $EXPORT_TAGS{all} = [ map { @$_ } values %EXPORT_TAGS ] };
19              
20             # Like push, but only pushes the item(s) onto the list indicated by the list
21             # ref (first param) if the list doesn't already contain it.
22             # Originally, I used Storable::freeze to see whether two structures where the
23             # same, but this didn't work for all cases, so I switched to is_deeply().
24             sub set_push (\@@) {
25 4     4 1 1748 my ($list, @items) = @_;
26             ITEM:
27 4         10 for my $item (@items) {
28 10         24 for my $el (@$list) {
29 29 100       56 next ITEM if is_deeply($item, $el);
30             }
31 5         16 push @$list, $item;
32             }
33             }
34              
35             sub flatten {
36 21 100   21 1 654 ref $_[0] eq 'ARRAY' ? @{ $_[0] }
  7 100       30  
37             : defined $_[0] ? @_
38             : ();
39             }
40              
41             # Start of code adapted from Test::More
42             #
43             # In set_push and other places within the framework, we need to compare
44             # structures deeply, so here are the relevant methods copied from Test::More
45             # with the test-specific code removed.
46             sub is_deeply {
47 101     101 1 135 my ($this, $that) = @_;
48 101 100 66     3368 return _deep_check($this, $that) if ref $this && ref $that;
49 63 100 66     410 return $this eq $that if defined $this && defined $that;
50              
51             # undef only matches undef and nothing else
52 3   33     20 return !defined $this && !defined $that;
53             }
54              
55             sub _deep_check {
56 44     44   74 my ($e1, $e2) = @_;
57              
58             # Quiet uninitialized value warnings when comparing undefs.
59 44         114 local $^W = 0;
60 44 100       132 return 1 if $e1 eq $e2;
61 42 100 100     176 return eq_array($e1, $e2)
62             if UNIVERSAL::isa($e1, 'ARRAY') && UNIVERSAL::isa($e2, 'ARRAY');
63 35 100 100     297 return eq_hash($e1, $e2)
64             if UNIVERSAL::isa($e1, 'HASH') && UNIVERSAL::isa($e2, 'HASH');
65 10 100 66     54 return _deep_check($$e1, $$e2)
66             if UNIVERSAL::isa($e1, 'REF') && UNIVERSAL::isa($e2, 'REF');
67 8 100 66     50 return _deep_check($$e1, $$e2)
68             if UNIVERSAL::isa($e1, 'SCALAR') && UNIVERSAL::isa($e2, 'SCALAR');
69 4         56 return 0;
70             }
71              
72             sub eq_array {
73 7     7 1 13 my ($a1, $a2) = @_;
74 7 50       21 return 1 if $a1 eq $a2;
75 7 100       28 return 0 unless $#$a1 == $#$a2;
76 5         17 for (0 .. $#$a1) {
77 31 50       67 return 0 unless is_deeply($a1->[$_], $a2->[$_]);
78             }
79 5         28 return 1;
80             }
81              
82             sub eq_hash {
83 25     25 1 34 my ($a1, $a2) = @_;
84 25 50       72 return 1 if $a1 eq $a2;
85 25 100       107 return 0 unless keys %$a1 == keys %$a2;
86 18         44 foreach my $k (keys %$a1) {
87 25 100       84 return 0 unless exists $a2->{$k};
88 20 100       53 return 0 unless is_deeply($a1->{$k}, $a2->{$k});
89             }
90 7         49 return 1;
91             }
92              
93             # End of code adapted from Test::More
94             # Handle value objects as well as normal scalars
95             sub is_defined ($) {
96 0     0 1 0 my $value = shift;
97              
98             # restrict the method call to objects of type Class::Value, because we
99             # want to avoid deep recursion that could happen if is_defined() is
100             # imported into a package and then someone else calls is_defined() on an
101             # object of that package.
102 0 0 0     0 ref($value)
103             && UNIVERSAL::isa($value, 'Class::Value')
104             && UNIVERSAL::can($value, 'is_defined')
105             ? $value->is_defined
106             : defined($value);
107             }
108              
109             sub value_of ($) {
110 0     0 1 0 my $value = shift;
111              
112             # Explicitly return undef unless the value is_defined, because it could
113             # still be a value object, in which case the value we want isn't the value
114             # object itself, but 'undef'
115 0 0       0 is_defined $value ? "$value" : undef;
116             }
117              
118             sub str_value_of ($) {
119 0     0 1 0 my $value = shift;
120 0 0       0 is_defined $value ? "$value" : '';
121             }
122              
123             sub flex_grep {
124 4     4 1 10 my $wanted = shift;
125 21         52 return grep { $_ eq $wanted }
  14         24  
126 4         9 map { flatten($_) } @_;
127             }
128              
129             sub class_map {
130 0     0 1   my ($class, $map, $seen) = @_;
131              
132             # circularities
133 0   0       $seen ||= {};
134              
135             # so we can pass an object as well as a class name:
136 0 0         $class = ref $class if ref $class;
137 0 0         return if $seen->{$class}++;
138 0           my $val = $map->{$class};
139 0 0         return $val if defined $val;
140              
141             # If there's no direct mapping for an exception class, check its
142             # superclasses. Assumes that the classes are loaded, of course.
143 1     1   7 no strict 'refs';
  1         1  
  1         236  
144 0           for my $super (@{"$class\::ISA"}) {
  0            
145 0           my $found = class_map($super, $map, $seen);
146              
147             # we will return UNIVERSAL if everything fails - so skip it.
148 0 0 0       return $found if defined $found && $found ne $map->{UNIVERSAL};
149             }
150 0           return $map->{UNIVERSAL};
151             }
152              
153             sub trim {
154 0     0 1   my $s = shift;
155 0           $s =~ s/^\s+//;
156 0           $s =~ s/\s+$//;
157 0           $s;
158             }
159             1;
160              
161              
162              
163             __END__