File Coverage

blib/lib/Hub/Data/Handlers.pm
Criterion Covered Total %
statement 72 93 77.4
branch 23 44 52.2
condition 5 12 41.6
subroutine 11 15 73.3
pod 5 5 100.0
total 116 169 68.6


line stmt bran cond sub pod time code
1             package Hub::Data::Handlers;
2 1     1   6 use strict;
  1         2  
  1         33  
3 1     1   7 use Hub qw/:lib/;
  1         2  
  1         5  
4             our $VERSION = '4.00043';
5             our @EXPORT = qw//;
6             our @EXPORT_OK = qw/fetch store getv setv delete/;
7              
8             # ------------------------------------------------------------------------------
9             # fetch - Get a nested value whose parent may need to be loaded from disk
10             # fetch \%data, $index
11             # ------------------------------------------------------------------------------
12              
13             sub fetch {
14 19     19 1 62 my $result = _traverse($_[0], $_[1]);
15 19 100       33 _transcend($result) if (@{$result->{'not_found'}});
  19         64  
16 19         113 $result->{'value'};
17             }#fetch
18              
19             # ------------------------------------------------------------------------------
20             # store - Store a nested value whose parent may need to be loaded from disk
21             # store \%data, $index, ?value
22             # ------------------------------------------------------------------------------
23              
24             sub store {
25 7     7 1 38 my $result = _traverse($_[0], $_[1]);
26 7 100       12 _transcend($result) if (@{$result->{'not_found'}});
  7         31  
27 7 100       10 _autovivify($result) if (@{$result->{'not_found'}});
  7         27  
28 7         22 _set($result->{'parent'}, $result->{'last_node'}, $_[2]);
29             }#store
30              
31             # ------------------------------------------------------------------------------
32             # getv - Get a nested value
33             # getv \%data, $index
34             # getv \@data, $index
35             # ------------------------------------------------------------------------------
36              
37             sub getv {
38 0     0 1 0 my $result = _traverse($_[0], $_[1]);
39 0         0 $result->{'value'};
40             }#getv
41              
42             # ------------------------------------------------------------------------------
43             # setv - Store a nested value
44             # setv \%data, $index, ?value
45             # setv \@data, $index, ?value
46             # ------------------------------------------------------------------------------
47              
48             sub setv {
49 0     0 1 0 my $result = _traverse($_[0], $_[1]);
50 0 0       0 _transcend($result) if (@{$result->{'not_found'}});
  0         0  
51 0 0       0 _autovivify($result) if (@{$result->{'not_found'}});
  0         0  
52 0         0 _set($result->{'parent'}, $result->{'last_node'}, $_[2]);
53             }#setv
54              
55             # ------------------------------------------------------------------------------
56             # delete - Remove a nested value
57             # delete \%data, $index
58             # delete \@data, $index
59             # ------------------------------------------------------------------------------
60              
61             sub delete {
62 4     4 1 11 my $result = _traverse($_[0], $_[1]);
63 4 50       7 return if @{$result->{'not_found'}};
  4         20  
64 0         0 _delete($result->{'parent'}, $result->{'last_node'});
65             }#delete
66              
67             # ------------------------------------------------------------------------------
68             # _get - Get node value from an array or hash
69             # _get \%data, $node
70             # _get \@data, $node
71             # ------------------------------------------------------------------------------
72              
73             sub _get {
74 54 50 33 54   160 Hub::is_bipolar($_[0])
    50          
    50          
    50          
75             ? $_[0]->get_data($_[1])
76             : ref($_[0]) eq 'ARRAY' && $_[1] =~ /^\d+$/
77             ? $_[0]->[$_[1]]
78             : $_[1] =~ /^\{(.*)\}$/
79             ? Hub::subset(@_)
80             : isa($_[0], 'HASH')
81             ? $_[0]->{$_[1]}
82             : Hub::subset(@_);
83              
84             # $_[1] =~ /^\{(.*)\}$/
85             # ? Hub::subset(@_)
86             # : ref($_[0]) eq 'ARRAY' && $_[1] =~ /^\d+$/
87             # ? $_[0]->[$_[1]]
88             # : ref($_[0]) eq 'HASH'
89             # ? Hub::subset(@_)
90             # : Hub::is_bipolar($_[0])
91             # ? $_[0]->get_data($_[1])
92             # : isa($_[0], 'HASH')
93             # ? Hub::subset(@_)
94             # : undef;
95              
96             }#_get
97              
98             # ------------------------------------------------------------------------------
99             # _set - Set a node value on an array or hash
100             # _set \%data, $node, $value
101             # _set \@data, $node, $value
102             # ------------------------------------------------------------------------------
103              
104             sub _set {
105 7 50 33 7   52 if ((ref($_[0]) eq 'ARRAY') && ($_[1] =~ /^\d+$/)) {
    50          
106 0         0 $_[0]->[$_[1]] = $_[2];
107             } elsif (isa($_[0], 'HASH')) {
108 7         30 $_[0]->{$_[1]} = $_[2];
109             } else {
110 0         0 confess "Type mismatch";
111             }
112 7         28 return $_[2];
113             }#_set
114              
115             # ------------------------------------------------------------------------------
116             # _delete - Remove a node from an array or hash
117             # _delete - \%data, $node
118             # _delete - \@data, $node
119             # ------------------------------------------------------------------------------
120              
121             sub _delete {
122 0 0 0 0   0 if ($_[1] =~ /^\d+$/ && ref($_[0]) eq 'ARRAY') {
    0          
123 0         0 delete $_[0]->[$_[1]];
124             } elsif (isa($_[0], 'HASH')) {
125 0         0 delete $_[0]->{$_[1]};
126             }
127             }#_delete
128              
129             # ------------------------------------------------------------------------------
130             # _autovivify - Create missing parent nodes
131             # _autovivify \%result
132             # ------------------------------------------------------------------------------
133              
134             sub _autovivify {
135 5     5   9 my $result = shift;
136 5         6 my $not_found = $result->{'not_found'};
137             # autovivify (create parents) if needed
138 5         15 while (@$not_found) {
139 5         9 my $node = shift @$not_found;
140 5 50       22 if (@$not_found) {
141             # fill intermediates as hashes, unless the next node is an array index
142 0 0       0 $result->{'parent'} =
143             _set($result->{'parent'}, $node,
144             $$not_found[0] =~ /^\d+$/ ? [] : {});
145 0         0 push @{$result->{'found'}}, $node;
  0         0  
146             }
147             }
148             }#_autovivify
149              
150             # ------------------------------------------------------------------------------
151             # _traverse - Step into the nested data structure one index node at a time
152             # _traverse \%data, $index
153             # _traverse \@data, $index
154             # ------------------------------------------------------------------------------
155              
156             sub _traverse {
157 38     38   49 my $ptr = $_[0];
158 38         51 my $parent = $_[0];
159 38         56 my @found = ();
160 38         86 my @nodes = _split($_[1]);
161 38 50       98 my $last_node = @nodes ? $nodes[-1] : $_[1];
162 38         92 while (@nodes) {
163 54         73 $parent = $ptr;
164 54         104 $ptr = _get($ptr, $nodes[0]);
165 54 100       131 last unless defined $ptr;
166 19         54 push @found, shift @nodes;
167             }
168             return {
169 38         246 'value' => $ptr,
170             'parent' => $parent,
171             'found' => \@found,
172             'not_found' => \@nodes,
173             'last_node' => $last_node,
174             };
175             }#_traverse
176              
177             # ------------------------------------------------------------------------------
178             # _transcend - Extend the search to the file system
179             # _transcend \%result
180             # ------------------------------------------------------------------------------
181              
182             sub _transcend {
183 23     23   30 my $result = shift;
184 23         31 my $base = join '/', @{$result->{'found'}};
  23         55  
185 23         32 my $ptr = $result->{'parent'};
186 23         30 my $continue = 1;
187 23   100     59 while ($continue && @{$result->{'not_found'}}) {
  30         108  
188 23         49 my $node = $result->{'not_found'}[0];
189 23         34 $result->{'parent'} = $ptr;
190 23 100       46 my $path = $base ? "$base/$node" : $node;
191 23 100       459 if (-e $path) {
192 15         64 $ptr->{$node} = Hub::mkhandler($path);
193 15         211 $continue = -d $path;
194 15         20 $base = $path;
195 15         30 $ptr = $ptr->{$node};
196 15         17 push @{$result->{'found'}}, shift @{$result->{'not_found'}};
  15         32  
  15         77  
197             } else {
198 8         26 $continue = 0;
199             }
200             }
201 23 100       32 if (@{$result->{'not_found'}}) {
  23         64  
202 8         10 my $result2 = _traverse($ptr, join('/', @{$result->{'not_found'}}));
  8         24  
203 8         20 $result->{'value'} = $result2->{'value'};
204 8         15 $result->{'parent'} = $result2->{'parent'};
205 8         9 push @{$result->{'found'}}, @{$result2->{'found'}};
  8         14  
  8         14  
206 8         27 $result->{'not_found'} = $result2->{'not_found'};
207             } else {
208 15         30 $result->{'value'} = $ptr;
209             }
210 23         43 $result;
211             }#_transcend
212              
213             # ------------------------------------------------------------------------------
214             # _get_parser - Get the parser for a given file
215             # ------------------------------------------------------------------------------
216              
217             sub _get_parser {
218 0     0   0 my $parser = 'File';
219 0 0       0 if ($_[0] =~ /\.(dat|hf|metadata)$/) {
220 0         0 $parser = 'HashFile';
221             }
222 0         0 Hub::mkinst($parser, $_[0]);
223             }#_get_parser
224              
225             # ------------------------------------------------------------------------------
226             # _split - Split an index into nodes, removing empty ones
227             # _split - $index
228             # ------------------------------------------------------------------------------
229              
230             sub _split {
231 38     38   106 grep {length $_ > 0} split '/', $_[0];
  69         204  
232             }#_split
233              
234             1;
235              
236             =pod:summary Access nested data
237              
238             =pod:synopsis
239              
240             =pod:description
241              
242             =cut