File Coverage

blib/lib/Data/StackedHash.pm
Criterion Covered Total %
statement 91 107 85.0
branch 16 20 80.0
condition 3 5 60.0
subroutine 15 17 88.2
pod 6 6 100.0
total 131 155 84.5


line stmt bran cond sub pod time code
1             package Data::StackedHash;
2             our $VERSION = '0.99';
3              
4             #
5             # Copyright (C) 2003 Riccardo Murri, . All
6             # rights reserved.
7              
8             # This package is free software and is provided "as is" without express
9             # or implied warranty. It may be used, redistributed and/or modified
10             # under the same terms as Perl itself.
11             #
12              
13             =pod
14              
15             =head1 NAME
16              
17             Data::StackedHash - Stack of PERL Hashes
18              
19             =head1 SYNOPSIS
20              
21             use Data::StackedHash;
22              
23             tie %h, Data::StackedHash;
24              
25             $h{'a'}=1;
26             $h{'b'}=2;
27             tied(%h)->push; # put a new hash on the stack
28             $h{'a'}=3; # override value of key 'a'
29             ...
30             tied(%h)->pop; # remove top hash from the stack,
31             # $h{'a'} == 1 again
32              
33              
34             =head1 DESCRIPTION
35              
36             The Data::StackedHash module implements a stack of hashes; the whole
37             stack acts collectively and transparently as a single PERL hash, that
38             is, you can perform the usual operations (fetching/storing values,
39             I, I, etc.) on it. All the PERL buitlins which operate
40             on hashes are supported.
41              
42             Assigning a value to a key, as in C<< $h{'a'}=1 >>, puts the key/value
43             pair into the hash at the top of the stack. Reading a key off the
44             stack of hashes searches the whole stack, from the topmost hash to the
45             bottom one, until it finds a hash which holds some value associated to
46             the given key; returns C<< undef >> if no match was found.
47              
48             The built-in functions I, I, I act on the whole
49             collection of all key/value defined in any hash of the stack.
50              
51             You can add a hash on top of the stack by the method I, and
52             remove the topmost hash by the method I.
53              
54             Clearing a stack of hashes only clears the topmost one: that is,
55              
56             use Data::StackedHash;
57             tie %h, Data::StackedHash, {'a'=>1};
58              
59             # put some hash on top of the stack
60             tied(%h)->push({'a'=>2});
61              
62             print $h{'a'}; # prints 2
63              
64             %h = {}; # clear topmost hash
65              
66             print $h{'a'}; # prints 1
67              
68             =cut
69              
70 1     1   13249 use 5.006;
  1         3  
  1         36  
71 1     1   6 use strict;
  1         2  
  1         35  
72 1     1   5 use warnings;
  1         6  
  1         1054  
73              
74             sub TIEHASH {
75 3     3   102 my $proto = shift;
76 3         5 my $initial = shift;
77 3   33     18 my $class = ref($proto) || $proto;
78 3         6 my $self = {};
79 3         9 $self->{KEYS} = {};
80 3 100       8 if ($initial) {
81 2         5 $self->{STACK} = [$initial];
82 2         3 my $key;
83 2         6 foreach $key (keys %$initial) {
84 4         11 $self->{KEYS}->{$key}++;
85             }
86             } else {
87 1         4 $self->{STACK} = [{}];
88             }
89 3         7 bless($self, $class);
90 3         10 return $self;
91             };
92              
93             sub STORE {
94 1     1   2 my $self = shift;
95 1         3 my $key = shift;
96 1         3 my $value = shift;
97 1 50       2 $self->{KEYS}->{$key}++ unless exists @{$self->{STACK}}[0]->{$key};
  1         18  
98 1         2 @{$self->{STACK}}[0]->{$key} = $value;
  1         6  
99             };
100              
101             sub CLEAR {
102 0     0   0 my $self = shift;
103 0         0 @{$self->{STACK}}[0] = {};
  0         0  
104             # rebuild the KEYS hash...
105 0         0 %{$self->{KEYS}} = ();
  0         0  
106 0         0 my $hash;
107             my $key;
108 0         0 foreach $hash (@{$self->{STACK}}) {
  0         0  
109 0         0 foreach $key (keys %{$hash}) {
  0         0  
110 0         0 $self->{KEYS}->{$key} = 1;
111             };
112             };
113             };
114              
115             =pod
116              
117             =head2 METHODS
118              
119             =head3 push()
120              
121             The I method puts a new hash on top of the stack: you can
122             either pass to it a reference to the hash to put on top, or call
123             I with no arguments, in which case an empty hash is pushed
124             onto the stack.
125              
126             use Data::StackedHash;
127             tie %h, Data::StackedHash;
128              
129             # put some hash on top of the stack
130             tied(%h)->push({'a'=>1, 'b'=>2});
131            
132             # put an empty hash on top of the stack
133             tied(%h)->push;
134              
135             =cut
136            
137             sub push {
138 3     3 1 107 my $self = shift;
139 3   100     5 unshift @{$self->{STACK}}, $_[0] || {};
  3         18  
140 3 100       10 if ($_[0]) {
141 2         3 my $key;
142 2         19 foreach $key (keys %{$_[0]}) {
  2         7  
143 3         9 $self->{KEYS}->{$key}++;
144             };
145             };
146             };
147              
148             =pod
149              
150             =head3 pop()
151              
152             The I method removes the hash on top of the stack and returns a
153             reference to it; all key/value pairs defined only in that hash are
154             lost.
155              
156             =cut
157              
158             sub pop {
159 1     1 1 2 my $self = shift;
160 1         2 my $hash = shift @{$self->{STACK}};
  1         3  
161 1         2 my $key;
162 1         3 foreach $key (keys %$hash) {
163 1         4 $self->{KEYS}->{$key}--;
164             }
165 1         4 return $hash;
166             };
167              
168             =pod
169              
170             =head3 delete(), delete_all()
171              
172             A call to the built-in I will remove only the first-found key,
173             and return the associated value, or C<< undef >> if no such key was
174             found.
175              
176             use Data::StackedHash;
177             tie %h, Data::StackedHash, { 'a'=>1 };
178              
179             # put one more hash on top of the stack
180             tied(%h)->push();
181             $h{'a'}=2;
182             print "$h{a}\n"; # 2
183              
184             # delete the topmost occurrence of the 'a' key
185             delete $h{'a'};
186             print "$h{a}\n"; # 1
187              
188             The I method deletes the specified key from all hashes in
189             the stack; it returns the array of values found in the stack, or the
190             empty array if no value was associated with the given key. Values
191             from the topmost stack are first in the returned array.
192              
193             use Data::StackedHash;
194             tie %h, Data::StackedHash, { 'a'=>1 };
195              
196             # put one more hash on top of the stack
197             tied(%h)->push();
198             $h{'a'}=2;
199             print "$h{a}\n"; # 2
200              
201             # delete all occurrences of the 'a' key
202             tied(%h)->delete_all('a');
203             print "$h{a}\n"; # undef
204              
205             =cut
206              
207             sub DELETE {
208 1     1   52 my $self = shift;
209 1         3 my $key = shift;
210            
211 1 50       4 return undef unless exists $self->{KEYS}->{$key};
212            
213 1         2 $self->{KEYS}->{$key}--;
214 1 50       5 delete $self->{KEYS}->{$key} if $self->{KEYS}->{$key} == 0;
215 1         1 my $hash;
216 1         3 foreach $hash (@{$self->{STACK}}) {
  1         3  
217 1 50       4 next unless exists $hash->{$key};
218             # From perltie(3): ``If you want to emulate the
219             # normal behavior of delete(), you should return what-
220             # ever FETCH would have returned for this key.''
221 1         6 return delete $hash->{$key};
222             }
223 0         0 return undef;
224             };
225              
226             sub delete_all {
227 1     1 1 60 my $self = shift;
228 1         3 my $key = shift;
229 1         26 my $hash;
230 1         3 my @value = ();
231 1         2 foreach $hash (@{$self->{STACK}}) {
  1         15  
232 3 100       11 CORE::push @value, $hash->{$key} if exists $hash->{$key};
233 3         5 delete $hash->{$key};
234             }
235 1         3 delete $self->{KEYS}->{$key};
236             # From perltie(3): ``If you want to emulate the
237             # normal behavior of delete(), you should return what-
238             # ever FETCH would have returned for this key.''
239 1         4 return @value;
240             };
241              
242             =pod
243              
244             =head3 fetch_all(key)
245              
246             Returns all values associated with the given key; values from topmost
247             hash are first in the returned array.
248              
249             =cut
250              
251             sub FETCH {
252 8     8   93 my $self = shift;
253 8         11 my $key = shift;
254 8         9 my $hash;
255 8         8 foreach $hash (@{$self->{STACK}}) {
  8         17  
256 12 100       46 return $hash->{$key} if exists $hash->{$key};
257             };
258             # PERL hashes return the "undefined empty string" if
259             # one requests a non-existing key...
260 0         0 return undef;
261             };
262              
263             sub fetch_all {
264 1     1 1 48 my $self = shift;
265 1         2 my $key = shift;
266 1         1 my $hash;
267             my @values;
268 1         2 foreach $hash (@{$self->{STACK}}) {
  1         3  
269 3 100       13 CORE::push @values, $hash->{$key} if exists $hash->{$key};
270             };
271 1         6 return @values;
272             };
273              
274             =pod
275              
276             =head3 keys(), values(), each()
277              
278             The built-in functions I, I and I operate on the
279             union of all key/value pairs defined in any hash of the stack.
280              
281             use Data::StackedHash;
282             tie %h, Data::StackedHash, { 'a'=>1 };
283              
284             # put one more hash on top of the stack
285             tied(%h)->push();
286             $h{'b'}=2;
287              
288             # print all defined keys
289             print keys %h; # ab
290              
291             =cut
292              
293             sub EXISTS {
294 3     3   72 my $self = shift;
295 3         4 my $key = shift;
296 3 100       23 return exists ($self->{KEYS}->{$key}) ? 1 : 0;
297             };
298              
299             sub FIRSTKEY {
300 2     2   73 my $self = shift;
301             # reset the 'each' internal iterator
302 2         3 keys %{$self->{KEYS}};
  2         3  
303 2         3 return each %{$self->{KEYS}};
  2         10  
304             };
305              
306             sub NEXTKEY {
307 6     6   7 my $self = shift;
308 6         7 return each %{$self->{KEYS}};
  6         22  
309             }
310              
311             =pod
312              
313             =head3 height()
314              
315             The I method returns the current height of the stack of hashes.
316              
317             use Data::StackedHash;
318             tie %h, Data::StackedHash, { 'a'=>1 };
319              
320             # put one more hash on top of the stack
321             tied(%h)->push();
322              
323             print tied(%h)->height; # prints 2
324              
325             =cut
326              
327             sub height {
328 0     0 1 0 my $self = shift;
329 0         0 return $#{$self->{STACK}};
  0         0  
330             }
331              
332             =pod
333              
334             =head3 count(key)
335              
336             Given a key, the I method returns the number of hashes in which
337             that key is associated to a value.
338              
339             use Data::StackedHash;
340             tie %h, Data::StackedHash, { 'a'=>1 };
341              
342             # put one more hash on top of the stack
343             tied(%h)->push({'b'=>2});
344              
345             print tied(%h)->count('a'); # prints 1
346              
347             =cut
348              
349             sub count {
350 1     1 1 67 my $self = shift;
351 1         2 my $key = shift;
352 1         5 return $self->{KEYS}->{$key};
353             }
354              
355             1; # so the require or use succeeds
356              
357             __END__