File Coverage

blib/lib/Tie/Subset/Hash.pm
Criterion Covered Total %
statement 65 66 100.0
branch 23 24 100.0
condition 3 3 100.0
subroutine 14 14 100.0
pod n/a
total 105 107 100.0


line stmt bran cond sub pod time code
1             #!perl
2             package Tie::Subset::Hash;
3 2     2   113149 use warnings;
  2         18  
  2         69  
4 2     2   12 use strict;
  2         4  
  2         40  
5 2     2   9 use warnings::register;
  2         5  
  2         252  
6 2     2   15 use Carp;
  2         5  
  2         1748  
7              
8             # For AUTHOR, COPYRIGHT, AND LICENSE see the bottom of this file
9              
10             =head1 Name
11              
12             Tie::Subset::Hash - Tie a hash to a subset of another hash
13              
14             =head1 Synopsis
15              
16             use Tie::Subset::Hash;
17             my %hash = ( foo=>11, bar=>22, quz=>33 );
18             tie my %subset, 'Tie::Subset::Hash', \%hash, ['bar','quz'];
19             print "$subset{bar}\n"; # prints "22"
20             $subset{quz}++; # modifies $hash{quz}
21              
22             =head1 Description
23              
24             This class for tied hashes provides a "view" of a subset of a hash.
25              
26             =over
27              
28             =cut
29              
30             our $VERSION = '0.02';
31              
32             =item Cing
33              
34             tie my %subset, 'Tie::Subset::Hash', \%hash, \@keys;
35              
36             You must specify which subset of keys from the original hash can
37             be accessed via the tied hash. (Keys that do not yet exist in the
38             original hash may be specified.)
39              
40             =cut
41              
42             sub TIEHASH { ## no critic (RequireArgUnpacking)
43 8 100   8   4759 @_==3 or croak "bad number of arguments to tie";
44 7         23 my ($class, $hash, $keys) = @_;
45 7 100       111 ref $hash eq 'HASH' or croak "must provide hashref to tie";
46 6 100       113 ref $keys eq 'ARRAY' or croak "must provide key list to tie";
47 5 100       17 for (@$keys) { croak "bad hash key '$_'" if ref; croak "bad hash key undef" if !defined }
  18 100       120  
  17         118  
48 3         11 my $self = { hash => $hash, keys => { map {$_=>1} @$keys } };
  16         38  
49 3         15 return bless $self, $class;
50             }
51              
52             =item Fetching
53              
54             If the key is in the subset, the value from the underlying hash is
55             returned, otherwise returns nothing (undef).
56              
57             =cut
58              
59             sub FETCH {
60 36     36   7279 my ($self,$key) = @_;
61 36 100       111 return unless exists $self->{keys}{$key};
62 30         85 return $self->{hash}{$key};
63             }
64              
65             =item Storing
66              
67             If the key is in the subset, the new value will be stored in the
68             underlying hash, otherwise the operation is ignored and a warning
69             issued.
70              
71             =cut
72              
73             sub STORE {
74 5     5   1598 my ($self,$key,$val) = @_;
75 5 100       18 if (exists $self->{keys}{$key}) {
76 3         19 return $self->{hash}{$key} = $val;
77             } # else
78 2         382 warnings::warnif("assigning to unknown key '$key' not (yet) supported in ".ref($self).", ignoring");
79 2         122 return;
80             }
81              
82             =item C
83              
84             Will return true only if the key is in the subset I it exists
85             in the underlying hash.
86              
87             =cut
88              
89             sub EXISTS {
90 25     25   286 my ($self,$key) = @_;
91             # need to write this in this slightly strange way because otherwise
92             # the code coverage tool isn't picking it up correctly...
93 25 100 100     113 if ( exists $self->{keys}{$key} && exists $self->{hash}{$key} )
94 23         64 { return !!1 } else { return !!0 }
  2         13  
95             }
96              
97             =item Iterating (C, C, etc.)
98              
99             Only keys that exist both in the subset I the underlying hash
100             are iterated over. The iterator of the underlying hash is utilized,
101             so iterating over the tied hash will affect the state of the iterator
102             of the underlying hash.
103              
104             =cut
105              
106             sub FIRSTKEY {
107 9     9   3435 my ($self) = @_;
108 9         14 my $dummy = keys %{$self->{keys}}; # reset iterator
  9         27  
109 9         21 return $self->NEXTKEY;
110             }
111             sub NEXTKEY {
112 36     36   64 my ($self,$lkey) = @_;
113 36         61 my $next;
114             SEEK: {
115 36         44 $next = each %{$self->{keys}};
  55         63  
  55         131  
116 55 100       127 return unless defined $next;
117 46 100       102 redo SEEK unless exists $self->{hash}{$next};
118             }
119 27         64 return $next;
120             }
121              
122             =item Cing
123              
124             If the key is in the subset, the key will be deleted from the
125             underlying hash, but not the subset. Otherwise, the operation is
126             ignored and a warning issued.
127              
128             =cut
129              
130             sub DELETE {
131 6     6   2289 my ($self,$key) = @_;
132 6 100       19 if (exists $self->{keys}{$key}) {
133 4         25 return delete $self->{hash}{$key};
134             } # else
135 2         228 warnings::warnif("deleting unknown key '$key' not (yet) supported in ".ref($self).", ignoring");
136 2         95 return;
137             }
138              
139             =item Clearing
140              
141             Not (yet) supported (because it is ambiguous whether this operation
142             should delete keys from the underlying hash or not). Attempting to
143             clear the tied hash currently does nothing and causes a warning
144             to be issued.
145              
146             A future version of this module may lift this limitation (if a
147             useful default behavior exists).
148              
149             =cut
150              
151             sub CLEAR {
152 1     1   350 my ($self) = @_;
153 1         129 warnings::warnif("clearing of ".ref($self)." not (yet) supported, ignoring");
154 1         36 return;
155             }
156              
157             sub SCALAR {
158 1     1   4 my ($self) = @_;
159             # I'm not sure why the following counts as two statements in the coverage tool
160             # uncoverable branch true
161             # uncoverable statement count:2
162 1 50       5 return scalar %{$self->{keys}} if $] lt '5.026';
  0         0  
163 1         3 my %keys = map {$_=>1} grep {exists $self->{hash}{$_}} keys %{$self->{keys}};
  3         8  
  5         12  
  1         4  
164 1         6 return scalar keys %keys;
165             }
166              
167             sub UNTIE {
168 1     1   319 my ($self) = @_;
169 1         2 $self->{hash} = undef;
170 1         3 $self->{keys} = undef;
171 1         4 return;
172             }
173              
174             1;
175             __END__