File Coverage

blib/lib/Basset/Container/Hash.pm
Criterion Covered Total %
statement 56 58 96.5
branch 16 20 80.0
condition n/a
subroutine 11 11 100.0
pod n/a
total 83 89 93.2


line stmt bran cond sub pod time code
1             package Basset::Container::Hash;
2              
3             #Basset::Container::Hash, copyright and (c) 2005, 2006 James A Thomason III
4             #Basset::Container::Hash is distributed under the terms of the Perl Artistic License.
5              
6             our $VERSION = '1.00';
7              
8             =pod
9              
10             =head1 Basset::Container::Hash
11              
12             Basset::Container::Hash implements a layered hash. The easiest way to explain is with an example:
13              
14             my %x = ('a' => 'b');
15            
16             tie my %y, 'Basset::Container::Hash', \%x; #<- %x is the parent of 'y'.
17            
18             print $x{'a'}; #prints b
19             print $y{'a'}; #prints b (inherited from x)
20             $y{'a'} = 'foo';
21             $y{'z'} = 'bar';
22             print $x{'a'}; #prints b
23             print $y{'a'}; #prints foo (overriden in y)
24             print $x{'z'}; #prints undef (not defined in x
25             print $y{'z'}; #prints bar (overridden from x)
26             delete $y{'a'};
27             print $x{'a'}; #prints b
28             print $y{'a'}; #prints b (inherited from x)
29             $x{'b'} = 'c';
30             print $x{'b'}; #prints c
31             print $y{'b'}; #prints c (inherited from x)
32              
33             =cut
34              
35 9     9   51246 use strict;
  9         50  
  9         407  
36 9     9   53 use warnings;
  9         16  
  9         8728  
37              
38             # we're going to use an array underneath and bypass Basset::Object & accessor, for speed reasons.
39             # since we only talk to it via the tie interface, we can get away with it.
40             our $internal_hash = 0;
41             our $parent = 1;
42             our $gotoparent = 2;
43              
44             sub TIEHASH {
45              
46 101     101   2691 my $class = shift;
47 101         152 my $parent = shift;
48            
49 101         669 return bless [
50             {},
51             $parent,
52             0
53             ], $class;
54             }
55              
56             sub STORE {
57 426     426   1156 my $self = shift;
58 426         543 my $key = shift;
59 426         615 my $value = shift;
60            
61 426         2170 $self->[$internal_hash]->{$key} = $value;
62             }
63              
64             sub FETCH {
65 602     602   2082 my $self = shift;
66 602         813 my $key = shift;
67            
68 602         1032 my $internal = $self->[$internal_hash];
69            
70 602 100       1812 if (exists $internal->{$key}) {
    50          
71 37         281 return $internal->{$key};
72             }
73             elsif (my $parent = $self->[$parent]) {
74 565         2588 return $parent->{$key};
75             }
76             else {
77 0         0 return;
78             }
79             }
80              
81             sub EXISTS {
82 3     3   694 my $self = shift;
83 3         7 my $key = shift;
84            
85 3         5 my $internal = $self->[$internal_hash];
86            
87 3 100       12 if (exists $internal->{$key}) {
    50          
88 1         8 return exists $internal->{$key};
89             }
90             elsif (my $parent = $self->[$parent]) {
91 2         12 return exists $parent->{$key};
92             }
93             else {
94 0         0 return;
95             }
96             }
97              
98             sub DELETE {
99 2     2   5 my $self = shift;
100 2         3 my $key = shift;
101            
102 2         9 delete $self->[$internal_hash]->{$key};
103             }
104              
105             sub CLEAR {
106 1     1   1022 shift->[$internal_hash] = {};
107             }
108              
109             sub FIRSTKEY {
110 13     13   610 my $self = shift;
111            
112 13         23 $self->[$gotoparent] = 0;
113            
114 13         18 my $internal = $self->[$internal_hash];
115 13         19 my $c = keys %$internal;
116            
117 13         29 my ($k, $v) = each %$internal;
118              
119 13 100       35 unless (defined $k) {
120 2 50       7 if (my $parent = $self->[$parent]) {
121 2         3 $self->[$gotoparent] = 1;
122 2         7 ($k, $v) = each %$parent;
123             }
124             }
125              
126 13         87 return $k;
127            
128             }
129              
130             sub NEXTKEY {
131 131     131   143 my $self = shift;
132            
133 131         153 my $internal = $self->[$internal_hash];
134            
135 131 100       293 unless ($self->[$gotoparent]) {
136 34         59 my ($k, $v) = each %$internal;
137 34 100       69 if (defined $k) {
138 23         72 return $k;
139             }
140             }
141            
142 108 50       196 if (my $parent = $self->[$parent]) {
143 108         129 $self->[$gotoparent] = 1;
144 108         289 while (my ($k, $v) = each %$parent) {
145 110 100       463 return $k unless exists $internal->{$k};
146             }
147 12         17 $self->[$gotoparent] = 0;
148             }
149            
150 12         71 return;
151             }
152              
153             sub SCALAR {
154 1     1   955 my $self = shift;
155            
156 1         3 my $internal = $self->[$internal_hash];
157            
158 1         2 my %flat = ();
159 1         6 @flat{keys %$internal} = values %$internal;
160            
161 1         10 return scalar %flat;
162            
163             }
164              
165             1;
166              
167             =pod
168              
169             =begin btest(Basset::Container::Hash)
170              
171             my %x = ('a' => 'b');
172              
173             tie my %y, 'Basset::Container::Hash', \%x; #<- %x is the parent of 'y'.
174              
175             $y{'a'} = 'c';
176             $y{'b'} = 'd';
177              
178             $test->is($x{'a'}, 'b', '$x{a} = b');
179             $test->is($y{'a'}, 'c', '$y{a} = c');
180             $test->is($y{'b'}, 'd', '$y{b} = d');
181             $test->is($x{'b'}, undef, '$x{b} is undef');
182             $test->is(scalar(%y), '2/8', 'scalar %y works');
183             delete $y{'a'};
184             $test->is($y{'a'}, 'b', '$y{a} is now b');
185             $test->ok(exists $y{'a'} != 0, '$y{a} exists');
186             $test->ok(exists $y{'b'} != 0, '$y{b} exists');
187             $test->ok(exists $y{'c'} == 0, '$y{c} does not exist');
188             delete $y{'b'};
189              
190             my ($key, $value) = each %y;
191              
192             $test->is($key, 'a', 'only key left is a');
193              
194             $y{'new'} = 'value';
195              
196             my ($key2, $value2) = (keys %y)[0];
197              
198             $test->is($key2, 'new', 'first set key is new');
199              
200             my @keys = sort keys %y;
201             $test->is($keys[0], 'a', 'first key is a');
202             $test->is($keys[1], 'new', 'second key is new');
203              
204             %y = ();
205             my @keys2 = sort keys %y;
206             $test->is(scalar @keys2, 1, 'only one key remains');
207              
208             =end btest(Basset::Container::Hash)
209              
210             =cut