File Coverage

blib/lib/List/Breakdown.pm
Criterion Covered Total %
statement 26 26 100.0
branch 6 6 100.0
condition n/a
subroutine 7 7 100.0
pod 1 1 100.0
total 40 40 100.0


line stmt bran cond sub pod time code
1             package List::Breakdown;
2              
3             # Force me to write this properly
4 5     5   354844 use strict;
  5         51  
  5         155  
5 5     5   27 use warnings;
  5         10  
  5         128  
6 5     5   26 use utf8;
  5         7  
  5         32  
7              
8             # Target reasonably old Perls
9 5     5   206 use 5.006;
  5         17  
10              
11             # Import required modules
12 5     5   34 use Carp;
  5         16  
  5         367  
13              
14             # Handle exporting in a way Perl v5.6 should tolerate
15 5     5   33 use base qw(Exporter); ## no critic (ProhibitUseBase)
  5         9  
  5         2918  
16             our @EXPORT_OK = qw(breakdown);
17              
18             # Specify package version
19             our $VERSION = '0.24';
20              
21             # Dispatch table of functions to handle different ref types for the spec
22             # hashref's values
23             my %types = (
24              
25             # If it's a hash, apply breakdown() again as if it were another root-level
26             # spec
27             ref {} => sub {
28             my $spec = shift;
29             return { breakdown( $spec, @_ ) };
30             },
31              
32             # If it's an array, we're doing numeric bounds checking [a,b)
33             ref [] => sub {
34             my $bounds = shift;
35             @{$bounds} == 2
36             or croak 'arrayref for bounds needs two items';
37             return [
38             grep {
39             ( not defined $bounds->[0] or $_ >= $bounds->[0] )
40             and ( not defined $bounds->[1] or $_ < $bounds->[1] )
41             } @_,
42             ];
43             },
44              
45             # If it's a subroutine, return a arrayref of all elements for which it
46             # returns true
47             ref sub { } => sub {
48             my $sub = shift;
49             return [ grep { $sub->() } @_ ];
50             },
51              
52             # If it's a regular expression, return an arrayref of all elements it
53             # matches
54             ref qr//msx => sub {
55             my $re = shift;
56             return [ grep { $_ =~ $re } @_ ];
57             },
58             );
59              
60             # Given a spec and a list of items, filter them into a hash of the same
61             # structure
62             sub breakdown {
63 14     14 1 3219 my ( $spec, @items ) = @_;
64              
65             # Check the spec is a hashref
66 14 100       237 ref $spec eq ref {}
67             or croak 'hashref expected for first argument';
68              
69             # Start building a results hash
70 13         26 my %results;
71 13         22 for my $key ( keys %{$spec} ) {
  13         47  
72              
73             # Check that the value for this key is a reference
74 28 100       301 my $ref = ref $spec->{$key}
75             or croak "Ref expected for '$key'";
76              
77             # Check it's a reference we understand
78 26 100       153 exists $types{$ref}
79             or croak "Unhandled ref type $ref for '$key'";
80              
81             # Apply the appropriate subroutine for this reference type to the list
82             # of items
83 25         61 $results{$key} = $types{$ref}->( $spec->{$key}, @items );
84             }
85              
86             # Return the constructed result set
87 7         66 return %results;
88             }
89              
90             1;
91              
92             __END__