File Coverage

blib/lib/Tie/InsideOut.pm
Criterion Covered Total %
statement 147 148 99.3
branch 14 20 70.0
condition 6 11 54.5
subroutine 24 24 100.0
pod 0 2 0.0
total 191 205 93.1


line stmt bran cond sub pod time code
1             package Tie::InsideOut;
2              
3 11     11   16960 use 5.006001;
  11         36  
  11         465  
4 11     11   57 use strict;
  11         21  
  11         337  
5 11     11   53 use warnings;
  11         30  
  11         324  
6              
7 11     11   58 use Carp qw( croak );
  11         18  
  11         890  
8 11     11   63 use Scalar::Util qw( refaddr );
  11         18  
  11         2663  
9              
10             our $VERSION = '0.11';
11              
12             our @ISA = qw( );
13              
14             my %NameSpaces; # default namespace for each hash
15             my %Keys; # tracks defined keys and namespaces
16              
17             =begin internal
18              
19             The %Keys hash is structured as follows:
20              
21             $Keys{$id}->{$key}->{$namespace} = $hash_ref
22              
23             C<$id> refers to the unique object identifier (returned by the L method).
24              
25             C<$key> refers to the name of the hash key, qhich corresponds to the name of a hash
26             variable in the C<$namespace>.
27              
28             C<$namespace> refers to the namespace that the value is in. Encapsulation means
29             that child classes can use the same key names without conflict.
30              
31             C<$hash_ref> is a reference to the hash variable that contains the value. Which is
32             accessible:
33              
34             $Keys{$id}->{$key}->{$namespace}->{$id} = $value
35              
36             We maintain a structure that incidcates where all of the keys are so that we can
37             clean up the data when the object is destroyed. It also allows us to serialize
38             and deserialize data.
39              
40             =end internal
41              
42             =cut
43              
44             sub TIEHASH {
45 8   50 8   1119 my $class = shift || __PACKAGE__;
46              
47 8         13 my $scalar;
48 8         14 my $self = \$scalar;
49 8         22 bless $self, $class;
50              
51 8         29 my $id = $self->_get_id;
52             {
53 8   33     16 my $caller = shift || (caller)[0];
  8         69  
54 11     11   63 no strict 'refs';
  11         27  
  11         906  
55 8         30 $NameSpaces{$id} = $caller;
56             }
57 8         28 $self->CLEAR;
58              
59 8         33 return $self;
60             }
61              
62             BEGIN {
63 11     11   9744 *new = \&TIEHASH;
64             }
65              
66             sub DESTROY {
67 12     12   3044 my $self = shift;
68 12         44 my $id = $self->_get_id;
69              
70 12         36 $self->CLEAR;
71              
72 12         30 delete $Keys{$id};
73 12         652 delete $NameSpaces{$id};
74             }
75              
76             sub CLEAR {
77 28     28   44 my $self = shift;
78 28         53 my $id = $self->_get_id;
79              
80 28         38 foreach my $key (keys %{$Keys{$id}}) {
  28         617  
81 96         103 foreach my $namespace (keys %{$Keys{$id}->{$key}}) {
  96         238  
82 117         217 delete $Keys{$id}->{$key}->{$namespace}->{$id};
83 117         330 delete $Keys{$id}->{$key}->{$namespace};
84             }
85 96         223 delete $Keys{$id}->{$key};
86             }
87 28         77 $Keys{$id} = { };
88             }
89              
90             sub SCALAR {
91 2     2   556 my $self = shift;
92 2         7 my $id = $self->_get_id;
93 2         4 return scalar (%{$Keys{$id}});
  2         14  
94             }
95              
96             sub FETCH {
97 173     173   26924 my $self = shift;
98 173         229 my $key = shift;
99              
100 173         321 my ($id, $hash_ref) = $self->_validate_key($key);
101 170         1255 $hash_ref->{$id};
102             }
103              
104             sub EXISTS {
105 1     1   3 my $self = shift;
106 1         3 my $key = shift;
107              
108 1         4 my ($id, $hash_ref) = $self->_validate_key($key);
109 1         14 exists $hash_ref->{$id};
110             }
111              
112             # Being able to iterate over the keys is useful, but limited. After version
113             # 0.04, encapsulation is enforced.
114              
115             sub FIRSTKEY {
116 4     4   301 my $self = shift;
117 4         12 my $id = $self->_get_id;
118 4         5 my $aux = keys %{$Keys{$id}}; # reset each iterator
  4         14  
119 4         6 return each %{$Keys{$id}};
  4         22  
120             }
121              
122             sub NEXTKEY {
123 21     21   24 my $self = shift;
124 21         36 my $id = $self->_get_id;
125 21         27 return each %{$Keys{$id}};
  21         94  
126             }
127              
128             sub DELETE {
129 1     1   468 my $self = shift;
130 1         2 my $key = shift;
131              
132 1         4 my ($id, $hash_ref) = $self->_validate_key($key);
133 1         4 delete $Keys{$id}->{$key};
134 1         6 delete $hash_ref->{$id};
135             }
136              
137             sub STORE {
138 62     62   20530 my $self = shift;
139 62         93 my $key = shift;
140 62         150 my $val = shift;
141              
142 62         142 my ($id, $hash_ref, $namespace) = $self->_validate_key($key);
143 58         492 $Keys{$id}->{$key}->{$namespace} = $hash_ref;
144 58         305 $hash_ref->{$id} = $val;
145             }
146              
147             sub STORABLE_freeze {
148 4     4 0 1727 my $self = shift;
149 4         6 my $deep = shift; # return if ($deep);
150 4         11 my $id = $self->_get_id;
151              
152 4         9 my $struc = { };
153 4         13 my $refs = [ $NameSpaces{$id}, $struc ];
154 4         7 my $index = @$refs;
155              
156 4         7 foreach my $key (keys %{$Keys{$id}}) {
  4         20  
157 50         54 foreach my $namespace (keys %{$Keys{$id}->{$key}}) {
  50         123  
158 60         59 my $package = *{$Keys{$id}->{$key}->{$namespace}}{PACKAGE};
  60         197  
159 60         187 $struc->{$key}->{$package} = $index;
160 60         353 $refs->[$index++] = $Keys{$id}->{$key}->{$namespace}->{$id};
161             }
162             }
163              
164 4         489 return ($index, $refs);
165             }
166              
167             sub STORABLE_thaw {
168 4     4 0 64 my $self = shift;
169 4         7 my $deep = shift; # return if ($deep);
170              
171 4         14 $self->CLEAR;
172 4         10 my $id = $self->_get_id;
173              
174 4         9 my ($size, $refs) = @_;
175              
176 4 50       20 $self->CLEAR if (exists $Keys{$id});
177              
178 4 50       20 $NameSpaces{$id} = $refs->[0] unless (defined $NameSpaces{$id}); # Storable just blesses
179 4 50       13 croak("Namespaces do not match: ", $NameSpaces{$id}, " and ", $refs->[0]),
180             unless ($NameSpaces{$id} eq $refs->[0]);
181              
182 11     11   107 no strict 'refs';
  11         19  
  11         3624  
183              
184 4         7 my $struc = $refs->[1];
185 4         17 foreach my $key (keys %$struc) {
186 50         542 foreach my $namespace (keys %{$struc->{$key}}) {
  50         119  
187 60         80 my $index = $struc->{$key}->{$namespace};
188 60 50       107 croak "No namespace defined" if ($namespace eq "");
189              
190 60         181 my $hash_ref = *{$namespace."::"};
  60         160  
191 60 50 33     128 if ((exists $hash_ref->{$key}) && (ref *{$hash_ref->{$key}}{HASH})) {
  60         332  
192 60         191 $Keys{$id}->{$key}->{$namespace} = $hash_ref->{$key};
193 60         269 $hash_ref->{$key}->{$id} = $refs->[$index];
194             }
195             else {
196 0         0 croak "Symbol \%".$key." not defined in namespace ".$namespace;
197             }
198             }
199             }
200 4         60 return $self;
201             }
202              
203             sub _get_id {
204 320     320   365 my $self = shift;
205 320         763 return refaddr($self);
206             }
207              
208             sub _validate_key {
209 237     237   328 my ($self, $key) = @_;
210 237         410 my $id = $self->_get_id;
211              
212             # We get the name of the subroutine that called us, and use its
213             # namespace to look for the hash that contains the key value.
214              
215             # Warning: Perl documentation notes that the caller information may
216             # be optimized away when the value is greater than 1.
217              
218 237         1207 my $caller_namespace = (caller(2))[3];
219 237         437 my $hash_ref;
220              
221 237 100       480 if (defined $caller_namespace) {
222 11     11   112 no strict 'refs';
  11         19  
  11         1534  
223              
224             # If we're in an eval, then we resort to using the caller package
225              
226 181 100       305 if ($caller_namespace eq "(eval)") {
227 7         25 $caller_namespace = (caller(2))[0];
228 7         17 $caller_namespace =~ s/\s(eval\(\s\d+)?\)$//; # remove eval
229             }
230             else {
231 174         1300 $caller_namespace =~ s/::(((?!::).)+)$//;
232             }
233 181         248 $hash_ref = *{$caller_namespace."::"};
  181         585  
234             }
235             else {
236 56 50       140 croak "Cannot determine namespace of caller"
237             unless (exists $NameSpaces{$id});
238 11     11   70 no strict 'refs';
  11         118  
  11         1785  
239 56         73 $hash_ref = *{$NameSpaces{$id}."::"};
  56         163  
240 56         54 $caller_namespace = *{$hash_ref}{PACKAGE};
  56         127  
241             }
242              
243 237 100 100     814 if ((exists $hash_ref->{$key}) && (ref *{$hash_ref->{$key}}{HASH})) {
  234         1564  
244 230         1191 return ($id, $hash_ref->{$key}, $caller_namespace);
245             }
246             else {
247              
248             # print STDERR "\n\x23 key=$key\n\x23",
249             # join(" ", map {$_||""} (caller(0))), "\n\x23",
250             # join(" ", map {$_||""} (caller(1))), "\n\x23",
251             # join(" ", map {$_||""} (caller(2))), "\n\x23",
252             # join(" ", map {$_||""} (caller(3))), "\n";
253              
254 7         18 my $err_msg = "Symbol \%".$key." not defined";
255 7 100       20 if ($caller_namespace ne "main") {
256 3         8 $err_msg .= " in namespace ".$caller_namespace;
257             }
258 7         2338 croak $err_msg;
259             }
260             }
261              
262             1;
263             __END__