File Coverage

blib/lib/Tie/StrictHash.pm
Criterion Covered Total %
statement 69 79 87.3
branch 12 24 50.0
condition 2 3 66.6
subroutine 15 16 93.7
pod 5 5 100.0
total 103 127 81.1


line stmt bran cond sub pod time code
1             ##==============================================================================
2             ## Tie::StrictHash - a hash with constraints on adding elements
3             ##==============================================================================
4             ## Copyright 2001 Kevin Michael Vail. All rights reserved.
5             ## This program is free software; you can redistribute it and/or modify it
6             ## under the same terms as Perl itself.
7             ##==============================================================================
8             ## $Id: StrictHash.pm,v 1.0 2001/03/17 17:03:02 kevin Exp $
9             ##==============================================================================
10             require 5.005;
11              
12             package Tie::StrictHash;
13 1     1   832 use strict;
  1         2  
  1         31  
14 1     1   1346 use Tie::Hash;
  1         1300  
  1         37  
15 1     1   8 use Carp qw(carp croak cluck confess);
  1         6  
  1         69  
16 1     1   6 use Exporter ();
  1         2  
  1         198  
17 1     1   7 use vars qw(@ISA $VERSION @EXPORT *croak);
  1         2  
  1         9680  
18             @ISA = qw(Tie::StdHash Exporter);
19             @EXPORT = qw(strict_hash);
20             ($VERSION) = q$Revision: 1.0 $ =~ /^Revision:\s+([\d.]+)\s*$/;
21              
22             my $locked = 0;
23              
24             =head1 NAME
25              
26             Tie::StrictHash - A hash with 'strict'-like semantics
27              
28             =head1 SYNOPSIS
29              
30             use Tie::StrictHash;
31              
32             C, I =E I, ...;>
33              
34             C = tie I<%hash>, 'Tie::StrictHash', I =E I , ...;>
35              
36             C-Eadd(I =E I, ...);>
37              
38             C = I<$hashctl>-Edelete(I, ...);>
39              
40             C-Eclear;>
41              
42             =head1 DESCRIPTION
43              
44             C is a module for implementing some of the same semantics for
45             hash members that 'use strict' gives to variables. The following constraints
46             are applied to a strict hash:
47              
48             =over 4
49              
50             =item o
51              
52             No new keys may be added to the hash except through the B method of the
53             hash control object.
54              
55             =item o
56              
57             No keys may be deleted except through the B method of the hash control
58             object.
59              
60             =item o
61              
62             The hash cannot be re-initialized (cleared) except through the B method
63             of the hash control object.
64              
65             =item o
66              
67             Attempting to retrieve the value for a key that doesn't exist is a fatal error.
68              
69             =item o
70              
71             Attempting to store a value for a key that doesn't exist is a fatal error.
72              
73             =back
74              
75             In order to make any changes or modifications to the hash, you must either keep
76             the return value from B (or B) or retrieve it by using C
77             I<%hash>>. Think of it as the "key" that "unlocks" the hash so that you can make
78             changes to it.
79              
80             The original reason for writing this module was for classes that implement
81             an object as a hash, using hash members as instance variables. It's all too
82             easy to use the wrong member name, with the same results as misspelling a
83             variable name when C isn't in effect.
84              
85             Note that just as C allows you to create new variables by either
86             specifying an explicit package name or by using C, a strict hash
87             allows you to create or delete members by using the appropriate methods.
88             However, it does prevent you from creating or deleting members accidentally.
89             This is in keeping with the general philosophy of Perl.
90              
91             If you import the pseudo-symbol C, Tie::StrictHash will only issue
92             warning messages rather than dying when an attempt is made to reference a hash
93             value that doesn't exist.
94              
95             If you import the pseudo-symbol C, either by itself or along with
96             C, you'll get a stack backtrace as well when something happens.
97              
98             =cut
99              
100             ##==============================================================================
101             ## import
102             ##==============================================================================
103             sub import {
104 1     1   12 my $class = shift;
105 1         3 my $confess = grep { $_ eq 'confess' } @_;
  0         0  
106 1 50       7 if (grep { $_ eq 'warn' } @_) {
  0         0  
107 0 0       0 *croak = $confess ? *Carp::cluck : *Carp::carp;
108             } else {
109 1 50       7 *croak = $confess ? *Carp::confess : *Carp::croak;
110             }
111 1 0       3 @_ = grep { $_ ne 'warn' && $_ ne 'confess' } @_;
  0         0  
112 1         3 unshift(@_, $class);
113 1         2504 goto &Exporter::import;
114             }
115              
116             =pod
117              
118             =head1 DEFINING A STRICT HASH
119              
120             Use the B subroutine, or call B directly. The B method
121             is provided to create a strict anonymous hash. This is both a hash reference
122             and its own hash control object.
123              
124             =over 4
125              
126             =item C = strict_hash I<%hash>, I =E I, ...;>
127              
128             This routine is exported by default, and simply performs the B statement
129             listed next. However, it also preserves the original contents of I<%hash>,
130             while calling B directly does not. However, if you call B I<%hash>,
131             anything added since the call to B is lost and only the original
132             contents will remain.
133              
134             =cut
135              
136             ##==============================================================================
137             ## strict_hash
138             ##==============================================================================
139             sub strict_hash (\%@) {
140 1     1 1 43 my $hash = shift;
141 1         5 my %original = %$hash;
142 1         8 return tie %$hash, 'Tie::StrictHash', %original, @_;
143             }
144              
145             =item C = tie I<%hash>, 'Tie::StrictHash', I =E I , ...;>
146              
147             Sets I<%hash> as a 'strict' hash, and defines its initial contents. The
148             returned value I<$hashctl> is used to make any modifications to the hash.
149             The original contents of the hash are lost when you call B directly
150             (although they come back if you B the hash later).
151              
152             =back
153              
154             =head1 METHODS
155              
156             Except for B, these must be invoked by using the I<$hashctl> object. This
157             is returned by B or B, or may be retrieved by using C
158             I<%hash>> at any time.
159              
160             =over 4
161              
162             =cut
163              
164             ##==============================================================================
165             ## TIEHASH
166             ##==============================================================================
167             sub TIEHASH {
168 1     1   3 my $class = shift;
169 1         4 my $hash = bless {}, $class;
170 1         9 return $hash->add(@_);
171             }
172              
173             =pod
174              
175             =item C = new Tie::StrictHash I =E I, ...;>
176              
177             Creates a new anonymous strict hash with the specified members as its initial
178             contents. The hash reference is both a reference to the hash and the hash
179             control object. It's possible to define an object with its 'instance variables'
180             implemented in terms of a strict hash, provided that the object inherits from
181             Tie::StrictHash...in this case, the object and its underlying hash would
182             effectively belong to different classes! This works because B applies to
183             the reference, while B applies to the actual thingy.
184              
185             =cut
186              
187             ##==============================================================================
188             ## new
189             ##==============================================================================
190             sub new {
191 0     0 1 0 my $class = shift;
192 0         0 my $hash = {};
193 0         0 tie %$hash, 'Tie::StrictHash', @_;
194 0         0 return bless $hash, $class;
195             }
196              
197             =item C-Eadd(I =E I, ...);>
198              
199             Adds the specified keys and values to I<%hash>.
200              
201             =cut
202              
203             ##==============================================================================
204             ## add
205             ##==============================================================================
206             sub add {
207 2     2 1 65 my $hash = shift;
208 2         3 my $old_locked = $locked;
209 2         3 $locked = 1;
210 2         4 eval {
211 2         6 while (@_) {
212 4 50       12 croak "odd number of elements passed to add" if @_ == 1;
213 4         5 my $key = shift;
214 4         7 my $value = shift;
215 4         25 $hash->{$key} = $value;
216             }
217             };
218 2         3 $locked = $old_locked;
219 2 50       5 die $@ if $@;
220 2         8 return $hash;
221             }
222              
223             =pod
224              
225             =item C = I<$hashctl>-Edelete(I, ...);>
226              
227             Deletes the named key(s) from I<%hash> and returns them in I<@values>. They
228             appear in I<@values> in the same order as the keys are specified in the method
229             call.
230              
231             =cut
232              
233             ##==============================================================================
234             ## delete
235             ##==============================================================================
236             sub delete {
237 1     1 1 70 my $hash = shift;
238 1         2 my $old_locked = $locked;
239 1         2 my @values;
240 1         2 $locked = 1;
241 1         2 eval {
242 1         3 foreach (@_) {
243 1         4 push(@values, delete $hash->{$_});
244             }
245             };
246 1         3 $locked = $old_locked;
247 1 50       5 die $@ if $@;
248 1         3 return @values;
249             }
250              
251             =pod
252              
253             =item C-Eclear;>
254              
255             Clears the entire hash.
256              
257             =cut
258              
259             ##==============================================================================
260             ## clear
261             ##==============================================================================
262             sub clear {
263 1     1 1 55 my $hash = shift;
264 1         2 my $old_locked = $locked;
265 1         3 $locked = 1;
266 1         23 eval {
267 1         4 %$hash = ();
268             };
269 1         2 $locked = $old_locked;
270 1 50       3 die $@ if $@;
271 1         3 return $hash;
272             }
273              
274             =pod
275              
276             =back
277              
278             =head1 EXAMPLES
279              
280             To create a hash with just three members in it that can't be added to except
281             by using the B method:
282              
283             use Tie::StrictHash;
284             use strict;
285             use vars qw(%hash $hashctl);
286            
287             $hashctl = strict_hash %hash,
288             member1 => 'a', member2 => 'b', member3 => 'c';
289            
290             print $hash{member1}, "\n"; ## prints "a"
291             print $hash{member4}, "\n"; ## gives error!
292            
293             $hash{member2} = 'C'; ## OK
294             $hash{member4} = 'D'; ## gives error
295            
296             ## BUT...
297            
298             $hashctl->add(member4 => 'D'); ## Adds new member to hash
299            
300             print $hash{member4}, "\n"; ## prints "D"
301             $hash{member4} = 'd'; ## OK
302              
303             To define an object that uses a strict hash to hold its instance
304             variables:
305              
306             package StrictObject;
307             use Tie::StrictHash;
308             use strict;
309             use vars qw(@ISA);
310             @ISA = qw(Tie::StrictHash);
311            
312             sub new {
313             my $class = shift;
314             ##
315             ## Create strict hash and define object variables
316             ##
317             my $obj = new Tie::StrictHash var1 => 1, var2 => 'A';
318             ##
319             ## Then bless it into the proper class.
320             ##
321             return bless $obj, $class;
322             }
323            
324             package main;
325            
326             use vars qw($obj);
327            
328             $obj = new StrictObject;
329            
330             print ref $obj, "\n"; ## prints "StrictObject"
331             print tied %$obj, "\n"; ## prints "Tie::StrictHash=HASH(...)"
332              
333             =head1 DIAGNOSTICS
334              
335             These are all fatal errors unless the pseudo-symbol C was imported on
336             the C line.
337              
338             =over 4
339              
340             =item odd number of elements passed to add
341              
342             Self-explanatory.
343              
344             =item invalid attempt to clear strict hash
345              
346             A statement such as
347              
348             %hash = ();
349              
350             was attempted. This is not allowed. Use the B method.
351              
352             =item key 'I' does not exist
353              
354             An attempt was made to access or modify a key that doesn't exist.
355              
356             =item invalid attempt to delete key 'I'
357              
358             A statement such as
359              
360             delete $hash{'key'};
361              
362             was executed. You must use the B method to delete from a strict hash.
363              
364             =back
365              
366             =head1 SEE ALSO
367              
368             L
369              
370             C
371              
372             =head1 AUTHOR
373              
374             Kevin Michael Vail
375              
376             =cut
377              
378             ##==============================================================================
379             ## Most of the methods for dealing with %hash are inherited from Tie::StdHash.
380             ## However, the following ones are not, because they implement the 'strict'
381             ## part of StrictHash.
382             ##------------------------------------------------------------------------------
383             ## CLEAR is blocked except from within clear.
384             ##==============================================================================
385             sub CLEAR {
386 1     1   110 my $hash = shift;
387 1 50       124 croak "invalid attempt to clear strict hash" unless $locked;
388 0         0 %$hash = ();
389             }
390              
391             ##==============================================================================
392             ## STORE is blocked unless the hash element already exists, except from add.
393             ##==============================================================================
394             sub STORE {
395 5     5   226 my ($hash, $key, $value) = @_;
396 5 100 66     140 croak "key '$key' does not exist" unless $locked || exists $hash->{$key};
397 4         7 $hash->{$key} = $value;
398 4         11 return $value;
399             }
400              
401             ##==============================================================================
402             ## DELETE is blocked except from within delete.
403             ##==============================================================================
404             sub DELETE {
405 2     2   96 my ($hash, $key) = @_;
406 2 50       596 croak "invalid attempt to delete key '$key'" unless $locked;
407 0         0 delete $hash->{$key};
408             }
409              
410             ##==============================================================================
411             ## FETCH fails if applied to a member that doesn't exist.
412             ##==============================================================================
413             sub FETCH {
414 8     8   126 my ($hash, $key) = @_;
415 8 100       242 croak "key '$key' does not exist" unless exists $hash->{$key};
416 7         27 return $hash->{$key};
417             }
418              
419             1;
420              
421             ##==============================================================================
422             ## $Log: StrictHash.pm,v $
423             ## Revision 1.0 2001/03/17 17:03:02 kevin
424             ## Initial revision
425             ##==============================================================================