File Coverage

lib/Tie/Proxy/Hash.pm
Criterion Covered Total %
statement 95 95 100.0
branch 37 48 77.0
condition 21 36 58.3
subroutine 15 15 100.0
pod 2 3 66.6
total 170 197 86.2


line stmt bran cond sub pod time code
1             # (X)Emacs mode: -*- cperl -*-
2              
3             package Tie::Proxy::Hash;
4              
5             =head1 NAME
6              
7             Tie::Proxy::Hash - Effieciently merge & translate hashes.
8              
9             =head1 SYNOPSIS
10              
11             my (%hash, $ref);
12             $ref = tie %hash, 'Tie::Proxy::Hash', (bart => +{a => 1,
13             b => 2},
14             maggie => +{a => 5,
15             c => 6,
16             e => 10},
17             );
18             $hash{a} == 1; # true
19             $hash{b} == 2; # true (bart supercedes maggie)
20             $hash{c} == 6; # true
21             ! defined $hash{d}; # true
22             $hash{e} == 10; # true
23              
24             $hash{c} = 9; # set in maggie
25             $hash{d} = 12; # set in default
26             $hash{f} = 11; # set in default
27              
28             $ref->add_hash('lisa', +{d => 3, b => 4});
29             $hash{c} == 9; # true
30             $hash{b} == 2; # true (bart overrides lisa)
31             $hash{d} == 3; # true (lisa overrides default)
32             $hash{f} == 11; # true (only default knows 'f')
33              
34              
35             =head1 DESCRIPTION
36              
37             Proxy hash requests for one or more other hashes, with intermediate value
38             translation.
39              
40             Tie::Proxy::Hash 'merges' hashes by maintaining a list of hashes to look up,
41             and each key requested is looked up in each hash in order until a hit is
42             found. Resultant values may be subject to a translating subr. In this way,
43             hashes may be merged without the cost of by-value copying.
44              
45             A default backing hash is provided to store values not present in other
46             hashes.
47              
48             =cut
49              
50             # ----------------------------------------------------------------------------
51              
52             # Pragmas -----------------------------
53              
54             require 5.005_62;
55 2     2   223639 use strict;
  2         14  
  2         164  
56 2     2   13 use warnings;
  2         15  
  2         177  
57              
58             # Inheritance -------------------------
59              
60 2     2   16 use base qw( Exporter );
  2         9  
  2         568  
61             our @EXPORT_OK = qw( $PACKAGE $VERSION );
62              
63             # Utility -----------------------------
64              
65 2     2   20 use Carp qw( carp croak );
  2         3  
  2         6425  
66              
67             # ----------------------------------------------------------------------------
68              
69             # CLASS METHODS --------------------------------------------------------------
70              
71             # -------------------------------------
72             # CLASS CONSTANTS
73             # -------------------------------------
74              
75             our $PACKAGE = 'Tie-Proxy-Hash';
76             our $VERSION = '1.01';
77              
78             # -------------------------------------
79             # CLASS CONSTRUCTION
80             # -------------------------------------
81              
82             # -------------------------------------
83             # CLASS COMPONENTS
84             # -------------------------------------
85              
86             # -------------------------------------
87             # CLASS HIGHER-LEVEL FUNCTIONS
88             # -------------------------------------
89              
90             =head2 Tying
91              
92             $ref = tie %hash, 'Tie::Proxy::Hash',
93             bart => +{a => 1, b => 2},
94             maggie => +{a => 5, c => 6, e => 10} => sub {10*$_[0]},
95             ;
96              
97             Any arguments passed to C are palmed off onto L.
98              
99             =cut
100              
101             sub TIEHASH {
102 6     6   701 my $instance = $_[0]->new;
103              
104 6         26 for (my $i=1; $i < @_; $i+=2) {
105 2 50 0     6 croak sprintf('TIEHASH (%s): trailing arg found: %s',
106             $_[0],
107             ref($_[$i]) ||
108             (defined($_[$i]) ? "Simple value '$_[$i]'" : '*undef*'))
109             if $i+1 >= @_;
110              
111 2 100 66     21 if ( $i+2 <= $#_ and UNIVERSAL::isa($_[$i+2], 'CODE') ) {
112 1         6 $instance->add_hash(@_[$i..$i+2]);
113 1         3 $i++;
114             } else {
115 1         4 $instance->add_hash(@_[$i..$i+1]);
116             }
117             }
118              
119 6         26 return $instance;
120             }
121              
122             # -------------------------------------
123             # CLASS HIGHER-LEVEL PROCEDURES
124             # -------------------------------------
125              
126             # INSTANCE METHODS -----------------------------------------------------------
127              
128             # -------------------------------------
129             # INSTANCE CONSTRUCTION
130             # -------------------------------------
131              
132             sub new {
133 6   33 6 0 29 my $class = ref $_[0] || $_[0];
134              
135 6         44 return bless +{' default' => +{},' order' => [],' translate' => +{}}, $class;
136             }
137              
138             # -------------------------------------
139             # INSTANCE FINALIZATION
140             # -------------------------------------
141              
142             # -------------------------------------
143             # INSTANCE COMPONENTS
144             # -------------------------------------
145              
146             # -------------------------------------
147             # INSTANCE HIGHER-LEVEL FUNCTIONS
148             # -------------------------------------
149              
150             =head2 Retrieving Values
151              
152             Values are retrieved by checking each hash in the order of insertion; the
153             first hash found in which a given key exists supplies the value. The value is
154             subject to translation if the given hash has an associated translator.
155              
156             =cut
157              
158             sub FETCH {
159 70     70   3084 for (@{$_[0]->{' order'}}, ' default') {
  70         201  
160 106 100       535 return exists $_[0]->{' translate'}->{$_} ?
    100          
161             $_[0]->{' translate'}->{$_}->($_[0]->{$_}->{$_[1]}) :
162             $_[0]->{$_}->{$_[1]}
163             if exists $_[0]->{$_}->{$_[1]};
164             }
165              
166 5         18 return;
167             }
168              
169             sub EXISTS {
170 15     15   241 for (@{$_[0]->{' order'}}, ' default') {
  15         55  
171 17 100       84 return 1
172             if exists $_[0]->{$_}->{$_[1]};
173             }
174              
175 12         51 return;
176             }
177              
178             sub FIRSTKEY {
179 3 50   3   727 $_[0]->{' iterate'} = @{$_[0]->{' order'}} ? 0 : -1;
  3         18  
180 3         9 $_[0]->{' keys'} = +{};
181 3         25 return $_[0]->NEXTKEY;
182             }
183              
184             sub NEXTKEY {
185 14     14   566 my $self = shift;
186              
187 14         20 my $counter = $self->{' iterate'};
188              
189 14 100       53 my $hash = ($counter > -1 ?
190             $self->{$self->{' order'}->[$counter]} :
191             $self->{' default'});
192              
193 14         15 my $key;
194 14 50       38 $key = each %$hash
195             if defined $hash;
196              
197 14   66     96 while ( ! defined $hash or
      100        
198             ! defined $key or
199             exists $self->{' keys'}->{$key} ) {
200 8 100 100     22 if ( $counter > -1 and $counter < @{$self->{' order'}} - 1 ) {
  6 100       29  
201 3         4 $counter++;
202 3         7 $hash = $self->{$self->{' order'}->[$counter]};
203             } elsif ( $counter != -1 ) {
204 3         4 $counter = -1;
205 3         6 $hash = $self->{' default'};
206             }
207              
208 8 50       17 if ( defined $hash ) {
209 8   100     8 do {
210 10         53 $key = each %$hash;
211             } until ((! defined $key) or (! exists $self->{' keys'}->{$key}));
212             }
213              
214 8 100 100     76 last if ! defined $key and $counter == -1;
215             }
216              
217 14 100       27 if ( ! defined $key ) {
218 2         4 delete $self->{' iterate'};
219             } else {
220 12         19 $self->{' iterate'} = $counter;
221 12         24 $self->{' keys'}->{$key} = undef;
222             }
223              
224 14         67 return $key;
225             }
226              
227             # -------------------------------------
228             # INSTANCE HIGHER-LEVEL PROCEDURES
229             # -------------------------------------
230              
231             =head1 INSTANCE HIGHER-LEVEL PROCEDURES
232              
233             Z<>
234              
235             =cut
236              
237             =head2 add_hash
238              
239             =over 4
240              
241             =item SYNOPSIS
242              
243             $ref->add_hash('bart', +{ a => 1, b => 2 });
244             $ref->add_hash('lisa', +{ c => 3, b => 4 }, sub { $_[0] * 20 });
245              
246             =item ARGUMENTS
247              
248             =over 4
249              
250             =item name
251              
252             The name by which to refer to the hash (for future manipulations, e.g.,
253             L). The name must be a valid perl identifier --- a
254             non-empty string of word characters not beginning with a digit.
255              
256             If a member with the given name already exists, the hash is updated (and the
257             translator is updated/inserted/removed accordingly), but the order does not
258             change. Hence, following the synopsis by calling
259              
260             $ref->add_hash('bart', +{ a => 5, b => 6 });
261              
262             (without an intervening C) will set the effective value of C
263             to 6, for the new 'bart' hash will still be checked before the 'lisa' hash.
264              
265             If a member with the given name does not already exist (including if it was
266             deleted with L), the hash is added at the end of the
267             queue.
268              
269             Hashes inserted with C are always checked before the default hash,
270             even if the default hash has values that were set prior to the named hash(es)
271             being inserted.
272              
273             =item hash
274              
275             The hash to add in, as a hashref. For efficiency, this hash is stored within
276             as is. Therefore, if a reference to the same hash is manipulated externally,
277             these manipulations will be visible to the Proxy Hash. Caveat Emptor.
278              
279             =item translator
280              
281             B. If defined, all values retrieved from this hash are run through
282             the given code ref before being returned to the caller. The subr is called
283             with a single argument, the hash value, and is expected to return a single
284             value (which is passed back to the caller).
285              
286             The translator is only called to translate values for which keys exist in the
287             given hash; the translator is never called to create new values.
288              
289             The presence of a translator prevents any values being set in the hash (via
290             the C interface) (since there is no reverse translation
291             facility). Therefore, if a value is set that would otherwise be stored in a
292             translated hash, the key in that hash is deleted instead (to maintain the
293             identity C<$h{c} = $x; $h{c} == $x>). The storage then falls through to the
294             next untranslated hash (possibly the default hash). This is why the default
295             hash has no translator.
296              
297             my ($ref, %hash);
298             $ref = tie %hash, 'Tie::Proxy::Hash';
299             $ref->add_hash('bart', +{ a => 1, b => 2 });
300             $ref->add_hash('lisa', +{ c => 3, b => 4 }, sub { $_[0] * 20 });
301             $hash{c} = 5; # Sets c in the default hash, deletes 3 from lisa.
302              
303             =back
304              
305             =back
306              
307             The order of calling C is relevant; each hash is checked in order of
308             insertion via C. Therefore, given the example in the synopsis, the
309             'bart' hash is checked for values before the 'lisa' hash. Hence the effective
310             value of C is 2.
311              
312             =cut
313              
314             sub add_hash {
315 20 50   20 1 883 croak "add_hash: Illegal hash name: '$_[1]'"
316             unless $_[1] =~ /^(?!\d)\w+$/;
317 20 0 0     74 croak sprintf('add_hash: Arg 2 must be a hashref (got %s)',
    50          
318             defined $_[2] ?
319             (ref $_[2] || "Simple value: '$_[2]'") : '*undef*')
320             unless UNIVERSAL::isa($_[2], 'HASH');
321 20 50 0     76 croak sprintf('add_hash: Arg 3 must be a code ref (if defined) (got %s)',
      66        
322             ref $_[2] || "Simple value: '$_[2]'")
323             if defined $_[3] and ! UNIVERSAL::isa($_[3], 'CODE');
324              
325 20         54 my $exists = exists $_[0]->{$_[1]};
326 20         45 $_[0]->{$_[1]} = $_[2];
327 20 100       51 unless ( $exists ) { # Don't re$-add existing hashes
328 15         19 push @{$_[0]->{' order'}}, $_[1];
  15         39  
329             }
330 20 100       59 if ( defined $_[3] ) {
331 5         20 $_[0]->{' translate'}->{$_[1]} = $_[3];
332             } else {
333 15         60 delete $_[0]->{' translate'}->{$_[1]};
334             }
335             }
336              
337             # -------------------------------------
338              
339             =head2 remove_hash
340              
341             =over 4
342              
343             =item SYNOPSIS
344              
345             $ref->remove_hash('bart');
346              
347             =item ARGUMENTS
348              
349             =over 4
350              
351             =item name
352              
353             Name of the member hash to remove. An exception will be raised if no such
354             member exists.
355              
356             =back
357              
358             Removing a hash wipes any present translation, and the named hash loses its
359             place in the queue.
360              
361             =back
362              
363             =cut
364              
365             sub remove_hash {
366 5 50   5 1 263 croak "remove_hash: Illegal hash name: '$_[1]'"
367             unless $_[1] =~ /^(?!\d)\w+$/;
368 5 50       19 croak "remove_hash: No such member: '$_[1]'"
369             unless exists $_[0]->{$_[1]};
370              
371 5         17 delete $_[0]->{$_[1]};
372 5         8 my $count = @{$_[0]->{' order'}};
  5         14  
373 5         35 for (grep $_[0]->{' order'}->[$_] eq $_[1], map $count - $_, 1..$count) {
374 5         16 splice @{$_[0]->{' order'}}, $_, 1;
  5         44  
375             }
376 5         31 delete $_[0]->{' translate'}->{$_[1]};
377             }
378              
379             sub STORE {
380 11     11   643 for (@{$_[0]->{' order'}}) {
  11         35  
381 18 100       64 if ( exists $_[0]->{$_}->{$_[1]} ) {
382 7 100       20 if ( exists $_[0]->{' translate'}->{$_} ) {
383 1         6 delete $_[0]->{$_}->{$_[1]};
384             } else {
385 6         14 $_[0]->{$_}->{$_[1]} = $_[2];
386 6         16 return;
387             }
388             }
389             }
390              
391 5         18 $_[0]->{' default'}->{$_[1]} = $_[2];
392 5         17 return;
393             }
394              
395             sub DELETE {
396 3     3   150 for (@{$_[0]->{' order'}}) {
  3         10  
397 3 100 66     22 delete $_[0]->{$_}->{$_[1]}, return
398             if exists $_[0]->{$_}->{$_[1]} and ! exists $_[0]->{' translate'}->{$_};
399             }
400              
401 2         6 delete $_[0]->{' default'}->{$_[1]};
402 2         7 return;
403             }
404              
405             sub CLEAR {
406 1     1   84 for (@{$_[0]->{' order'}}) {
  1         34  
407 2         8 delete $_[0]->{$_};
408             }
409              
410 1         4 $_[0]->{' order'} = [];
411 1         5 $_[0]->{' default'} = +{};
412 1         6 $_[0]->{' translate'} = +{};
413             }
414              
415             # ----------------------------------------------------------------------------
416              
417             =head1 EXAMPLES
418              
419             Z<>
420              
421             =head1 BUGS
422              
423             Z<>
424              
425             =head1 REPORTING BUGS
426              
427             Email the author.
428              
429             =head1 AUTHOR
430              
431             Martyn J. Pearce C
432              
433             =head1 COPYRIGHT
434              
435             Copyright (c) 2003 Martyn J. Pearce. This program is free software; you can
436             redistribute it and/or modify it under the same terms as Perl itself.
437              
438             =head1 SEE ALSO
439              
440             Z<>
441              
442             =cut
443              
444             1; # keep require happy.
445              
446             __END__