File Coverage

blib/lib/Data/FetchPath.pm
Criterion Covered Total %
statement 55 55 100.0
branch 17 20 85.0
condition 12 12 100.0
subroutine 8 8 100.0
pod 1 1 100.0
total 93 96 96.8


line stmt bran cond sub pod time code
1             package Data::FetchPath;
2              
3 2     2   98007 use strict;
  2         5  
  2         61  
4 2     2   10 use warnings;
  2         4  
  2         51  
5 2     2   9 no warnings 'uninitialized';
  2         6  
  2         62  
6 2     2   9 use base 'Exporter';
  2         4  
  2         244  
7             our @EXPORT_OK = ('path');
8              
9             =head1 NAME
10              
11             Data::FetchPath - "eval"able paths to your complex data values
12              
13             =head1 VERSION
14              
15             Version 0.02
16              
17             =cut
18              
19             our $VERSION = '0.02';
20              
21             =head1 SYNOPSIS
22              
23             Quick summary of what the module does.
24              
25             use Data::FetchPath 'path';
26             use Test::Most;
27              
28             my $data = {
29             foo => 3,
30             bar => [qw/ this that 3 /],
31             3 => undef,
32             baz => {
33             trois => 3,
34             quatre => [qw/ 1 2 3 4 /],
35             }
36             };
37             my $target_value = 3;
38             ok $paths = path( $data, $target_value ), 'Fetching paths for matching data should succeed';
39             my @expected = sort qw(
40             {bar}[2]
41             {baz}{trois}
42             {baz}{quatre}[2]
43             {foo}
44             );
45             eq_or_diff $path, \@expected,
46             '... and it should return all paths to data values found';
47             for ( 0 .. $#expected ) {
48             my $found_value = eval "\$data->$expected[$_]";
49             is $found_value, $target_value,
50             '... and all values should match the value you looked for';
51             }
52             }
53              
54             =head1 EXPORT
55              
56             =head1 FUNCTIONS
57              
58             =head2 C
59              
60             Exported on demand via:
61              
62             use Data::FetchPath 'path';
63             my $paths = path($data_structure, $value);
64             my $paths = path($data_structure, $regex);
65              
66             Passed a data structure and either a scalar value or a regex
67             (C), this function will return an array reference to the paths
68             to said value. Each path is suitable for using C against said data
69             structure:
70              
71             my %data = (
72             one => 'uno',
73             two => 'dos',
74             three => 'tres',
75             );
76             # find values with the letter 'o' in them
77             my $paths = path(\%data, qr/o/);
78             foreach my $path (@$data) {
79             print eval "\$data$path\n";
80             }
81             __END__
82             uno
83             dos
84              
85             Currently the data structure must be an array or hash reference. The value
86             must be a scalar or a regular expression.
87              
88             =cut
89              
90 2     2   11 use Scalar::Util 'reftype';
  2         3  
  2         1244  
91              
92             my %path = (
93             ARRAY => \&_array_path,
94             HASH => \&_hash_path,
95             );
96              
97             sub path {
98 7     7 1 2189 my ( $data, $search_term ) = @_;
99 7         25 my $type = reftype $data;
100 7         17 my $find_paths = $path{$type};
101 7         30 return $find_paths->( $data, $search_term, { $data => 1 } );
102             }
103              
104             sub _array_path {
105 11     11   16 my ( $data, $search_term, $seen ) = @_;
106 11         13 my @paths;
107 11         26 foreach my $i ( 0 .. $#$data ) {
108 40         54 my $item = $data->[$i];
109 40         73 my $type = reftype $item;
110 40         58 my $current_index = "[$i]";
111 40         53 my $ref = ref $search_term;
112 40 100       71 if ( !$type ) {
    50          
113 36 100 100     222 if ( !$ref && $item eq $search_term ) { # XXX
    100 100        
114 11         48 push @paths => $current_index;
115             }
116             elsif ( 'Regexp' eq $ref && $item =~ $search_term ) {
117 2         5 push @paths => $current_index;
118             }
119             }
120             elsif ( my $find_paths = $path{$type} ) {
121 4 100       15 unless ( $seen->{$item} ) {
122 3         18 $seen->{$item} = 1;
123 5         16 my @current_paths =
124 3         36 map { "$current_index$_" }
125 3         4 @{ $find_paths->( $item, $search_term, $seen ) };
126 3         12 push @paths => @current_paths;
127             }
128             }
129             }
130 11         47 return \@paths;
131             }
132              
133             sub _hash_path {
134 5     5   8 my ( $data, $search_term, $seen ) = @_;
135 5         6 my @paths;
136 5         30 foreach my $key ( keys %$data ) {
137 15         19 my $item = $data->{$key};
138 15         28 my $type = reftype $item;
139 15         28 my $current_key = "{$key}";
140 15         19 my $ref = ref $search_term;
141 15 100       70 if ( !$type ) {
    50          
142 9 100 100     58 if ( !$ref && $item eq $search_term ) { # XXX
    100 100        
143 3         8 push @paths => $current_key;
144             }
145             elsif ( 'Regexp' eq $ref && $item =~ $search_term ) {
146 1         3 push @paths => $current_key;
147             }
148             }
149             elsif ( my $find_paths = $path{$type} ) {
150 6 50       15 unless ( $seen->{$item} ) {
151 6         13 $seen->{$item} = 1;
152 7         19 my @current_paths =
153 6         16 map { "$current_key$_" }
154 6         7 @{ $find_paths->( $item, $search_term, $seen ) };
155 6         19 push @paths => @current_paths;
156             }
157             }
158             }
159 5         21 return \@paths;
160             }
161              
162             =head1 AUTHOR
163              
164             Curtis "Ovid" Poe, C<< >>
165              
166             =head1 BUGS
167              
168             Please report any bugs or feature requests to C, or through
169             the web interface at L. I will be notified, and then you'll
170             automatically be notified of progress on your bug as I make changes.
171              
172              
173              
174              
175             =head1 SUPPORT
176              
177             You can find documentation for this module with the perldoc command.
178              
179             perldoc Data::FetchPath
180              
181              
182             You can also look for information at:
183              
184             =over 4
185              
186             =item * RT: CPAN's request tracker
187              
188             L
189              
190             =item * AnnoCPAN: Annotated CPAN documentation
191              
192             L
193              
194             =item * CPAN Ratings
195              
196             L
197              
198             =item * Search CPAN
199              
200             L
201              
202             =back
203              
204              
205             =head1 ACKNOWLEDGEMENTS
206              
207              
208             =head1 COPYRIGHT & LICENSE
209              
210             Copyright 2008 Curtis "Ovid" Poe, all rights reserved.
211              
212             This program is free software; you can redistribute it and/or modify it
213             under the same terms as Perl itself.
214              
215              
216             =cut
217              
218             1; # End of Data::FetchPath