File Coverage

blib/lib/Hash/Dirty.pm
Criterion Covered Total %
statement 72 86 83.7
branch 8 18 44.4
condition 6 8 75.0
subroutine 20 23 86.9
pod 8 8 100.0
total 114 143 79.7


line stmt bran cond sub pod time code
1             package Hash::Dirty;
2              
3 2     2   61811 use warnings;
  2         6  
  2         68  
4 2     2   11 use strict;
  2         5  
  2         125  
5              
6             =head1 NAME
7              
8             Hash::Dirty - Keep track of whether a hash is dirty or not
9              
10             =head1 VERSION
11              
12             Version 0.023
13              
14             =cut
15              
16             our $VERSION = '0.023';
17              
18             =head1 SYNOPSIS
19              
20             use Hash::Dirty;
21              
22             my %hash;
23             tie %hash, qw/Hash::Dirty/, { a => 1 };
24              
25             (tied %hash)->is_dirty; # Nope, not dirty yet.
26              
27             $hash{a} = 1;
28             (tied %hash)->is_dirty; # Still not dirty yet.
29              
30             $hash{b} = 2;
31             (tied %hash)->is_dirty; # Yes, now it's dirty
32              
33             (tied %hash)->dirty_keys; # ( b )
34              
35             $hash{a} = "hello";
36             (tied %hash)->dirty_keys; # ( a, b )
37              
38             (tied %hash)->dirty_values; # ( "hello", 2 )
39              
40             (tied %hash)->dirty } # { a => 1, b => 1 }
41              
42             (tied %hash)->reset;
43             (tied %hash)->is_dirty; # Nope, not dirty anymore.
44              
45             $hash{c} = 3;
46             (tied %hash)->is_dirty; # Yes, dirty again.
47              
48             # %hash is { a => "hello", b => 2, c => 3 }
49             (tied %hash)->dirty_slice } # { c => 3 }
50              
51             # Alternately:
52              
53             use Hash::Dirty;
54              
55             my $hash = Hash::Dirty::hash;
56              
57             # Also:
58            
59             my ($object, $hash) = Hash::Dirty->new;
60            
61             $hash->{a} = 1; # Etc., etc.
62             $object->is_dirty;
63              
64             =head1 DESCRIPTION
65              
66             Hash::Dirty will keep track of the dirty keys in a hash, letting you which values changed.
67              
68             Currently, Hash::Dirty will only inspect a hash shallowly, that is, it does not deeply compare
69             the contents of supplied values (say a HASH reference, ARRAY reference, or some other opaque object).
70              
71             This module was inspired by DBIx::Class::Row
72              
73             Currently, setting, deleting keys or clearing the hash means that the object will lose history, so it will know
74             that something has changed, but not if it is reset back at some later date:
75              
76             my ($object, $hash) = Hash::Dirty->new({ a => 1 });
77             $object->is_dirty; # Nope
78              
79             $hash->{a} = 2;
80             $object->is_dirty; # Yup
81            
82             $hash->{a} = 1;
83             $object->is_dirty; # Yup, still dirty, even though the original value was 1
84              
85             =cut
86              
87 2     2   11 use Scalar::Util qw/weaken/;
  2         6  
  2         273  
88             use Sub::Exporter -setup => {
89             exports => [
90             hash => sub { return sub {
91 1     1   5 my ($object, $hash) = __PACKAGE__->new(@_);
92 1         4 return $hash;
93 1         203 } },
94 2         25 ],
95 2     2   2121 };
  2         31298  
96 2     2   3051 use Tie::Hash;
  2         1930  
  2         59  
97              
98 2     2   12 use base qw/Tie::ExtraHash/;
  2         5  
  2         1100  
99              
100             =head1 EXPORTS
101              
102             =head2 hash( )
103              
104             Creates a new Hash::Dirty object and returns the tied hash reference, per Hash::Dirty->new.
105              
106             If supplied, will use as the storage (initializing the object accordingly)
107              
108             =cut
109              
110 2     2   13 use constant STORAGE => 0;
  2         5  
  2         128  
111 2     2   10 use constant DIRTY => 1;
  2         4  
  2         107  
112 2     2   20 use constant HASH => 2;
  2         6  
  2         3818  
113              
114             sub TIEHASH {
115 3     3   20 my ($class, $storage) = @_;
116 3   100     14 $storage ||= {};
117 3         6 my $self = [];
118 3         7 $self->[STORAGE()] = $storage;
119 3         6 $self->[DIRTY()] = {};
120 3         11 return bless $self, $class;
121             }
122              
123             =head1 METHODS
124              
125             =cut
126              
127             =head2 Hash::Dirty->new( )
128              
129             Creates and returns a new Hash::Dirty object
130              
131             If supplied, will use as the storage (initializing the object accordingly)
132              
133             In list context, new will return both the object and the "regular" hash:
134              
135             my ($object, $hash) = Hash::Dirty->new;
136             $hash->{a} = 1;
137             $object->is_dirty; # Yup, it's dirty
138              
139             =cut
140              
141             sub new {
142 2     2 1 3 my $class = shift;
143 2         4 my %hash;
144 2         10 my $self = tie %hash, $class, @_;
145 2         5 my $hash = \%hash;
146 2         4 $self->[HASH()] = $hash;
147 2         7 weaken $self->[HASH()];
148 2 50       9 return wantarray ? ($self, \%hash) : $self;
149             }
150              
151             =head2 $object->hash
152              
153             Returns a reference to the overlying hash
154              
155             =cut
156              
157             sub hash {
158 2     2 1 3 my $self = shift;
159 2         9 return $self->[HASH()];
160             }
161              
162             =head2 $object->is_dirty
163              
164             Returns 1 if the hash is dirty at all, 0 otherwise
165              
166             =head2 $object->is_dirty ( )
167              
168             Returns 1 if is dirty, 0 otherwise
169              
170             =head2 $object->is_dirty ( $key, $key, ..., )
171              
172             Returns 1 if any is dirty, 0 otherwise
173              
174             =cut
175              
176             sub is_dirty {
177 9     9 1 33 my $self = shift;
178 9 50       21 if (@_) {
179 0         0 for my $key (@_) {
180 0 0       0 return 1 if exists $self->[DIRTY()]->{$key};
181             }
182             }
183             else {
184 9 100       21 return 1 if $self->dirty_keys;
185             }
186 5         24 return 0;
187             }
188              
189             =head2 $object->reset
190              
191             Resets the hash to non-dirty status
192              
193             This method affects the dirtiness only, it does not erase or alter the hash in anyway
194              
195             =cut
196              
197             sub reset {
198 2     2 1 6 my $self = shift;
199 2         6 $self->[DIRTY()] = {};
200             }
201              
202             =head2 $object->dirty
203              
204             Returns a hash indicating which keys are dirty
205              
206             In scalar context, returns a hash reference
207              
208             =cut
209              
210             sub dirty {
211 1     1 1 2 my $self = shift;
212 1         3 my %dirty = %{ $self->[DIRTY()] };
  1         4  
213 1 50       9 return wantarray ? %dirty : \%dirty;
214             }
215              
216             sub _storage {
217 0     0   0 my $self = shift;
218 0         0 my %storage = %{ $self->[STORAGE()] };
  0         0  
219 0 0       0 return wantarray ? %storage : \%storage;
220             }
221              
222             =head2 $object->dirty_slice
223              
224             Returns a hash slice containg only the dirty keys and values
225              
226             In scalar context, returns a hash reference
227              
228             =cut
229              
230             sub dirty_slice {
231 1     1 1 2 my $self = shift;
232 1         3 my %slice = map { $_ => $self->[STORAGE()]{$_} } $self->dirty_keys;
  1         6  
233 1 50       10 return wantarray? %slice : \%slice;
234             }
235              
236             =head2 $object->dirty_keys
237              
238             Returns a list of dirty keys
239              
240             =cut
241              
242             sub dirty_keys {
243 13     13 1 20 my $self = shift;
244 13         17 return keys %{ $self->[DIRTY()] };
  13         76  
245             }
246              
247             =head2 $object->dirty_values
248              
249             Returns a list of dirty values
250              
251             =cut
252              
253             sub dirty_values {
254 1     1 1 3 my $self = shift;
255 1         3 return map { $self->[STORAGE()]{$_} } $self->dirty_keys;
  2         12  
256             }
257              
258             sub STORE {
259 6     6   24 my ($self, $key, $value) = @_;
260              
261 6         11 my $storage = $self->[STORAGE()];
262 6         7 my $new = $value;
263 6         10 my $old = $storage->{$key};
264 6         12 $storage->{$key} = $new;
265             # Taken from DBIx::Class::Row::set_column
266 6 100 66     44 $self->[DIRTY()]{$key} = 1 if (defined $old ^ defined $new) || (defined $old && $old ne $new);
      66        
267 6         19 return $new;
268             }
269              
270             =head2 $object->set( , )
271              
272             =head2 $object->store( , )
273              
274             =cut
275              
276             *set = \&STORE;
277             *store = \&STORE;
278              
279             =head2 $object->get( )
280              
281             =head2 $object->fetch( )
282              
283             =cut
284              
285             *get = \&Tie::ExtraHash::FETCH;
286             *fetch = \&Tie::ExtraHash::FETCH;
287              
288             sub CLEAR {
289 0     0     my $self = shift;
290              
291 0           my $storage = $self->[STORAGE()];
292 0           $self->[DIRTY()]{$_} = 1 for keys %$storage;
293 0           %$storage = ();
294             }
295              
296             =head2 $object->clear
297              
298             =cut
299              
300             *clear = \&Tie::ExtraHash::CLEAR;
301              
302             sub DELETE {
303 0     0     my ($self, $key) = @_;
304              
305 0           my $storage = $self->[STORAGE()];
306 0 0         $self->[DIRTY()]{$key} = 1 if exists $storage->{$key};
307 0           return delete $storage->{$key};
308             }
309              
310             =head2 $object->delete( )
311              
312             =cut
313              
314             *delete = \&Tie::ExtraHash::DELETE;
315              
316             =head1 AUTHOR
317              
318             Robert Krimen, C<< >>
319              
320             =head1 BUGS
321              
322             Please report any bugs or feature requests to
323             C, or through the web interface at
324             L.
325             I will be notified, and then you'll automatically be notified of progress on
326             your bug as I make changes.
327              
328             =head1 SUPPORT
329              
330             You can find documentation for this module with the perldoc command.
331              
332             perldoc Hash::Dirty
333              
334             You can also look for information at:
335              
336             =over 4
337              
338             =item * AnnoCPAN: Annotated CPAN documentation
339              
340             L
341              
342             =item * CPAN Ratings
343              
344             L
345              
346             =item * RT: CPAN's request tracker
347              
348             L
349              
350             =item * Search CPAN
351              
352             L
353              
354             =back
355              
356             =head1 ACKNOWLEDGEMENTS
357              
358             =head1 COPYRIGHT & LICENSE
359              
360             Copyright 2007 Robert Krimen, all rights reserved.
361              
362             This program is free software; you can redistribute it and/or modify it
363             under the same terms as Perl itself.
364              
365             =cut
366              
367             1; # End of Hash::Dirty