File Coverage

blib/lib/Data/KeyDiff.pm
Criterion Covered Total %
statement 63 68 92.6
branch 19 30 63.3
condition 7 16 43.7
subroutine 5 7 71.4
pod 1 1 100.0
total 95 122 77.8


line stmt bran cond sub pod time code
1             package Data::KeyDiff;
2              
3 2     2   60687 use warnings;
  2         4  
  2         67  
4 2     2   9 use strict;
  2         4  
  2         139  
5              
6             =head1 NAME
7              
8             Data::KeyDiff - Diff one set/list against another with a key basis
9              
10             =head1 VERSION
11              
12             Version 0.021
13              
14             =cut
15              
16             $Data::KeyDiff::VERSION = '0.021';
17              
18             =head1 SYNOPSIS
19              
20             # For each item in the list, the number is the item "key", the letter is the item "data"
21             my @A = qw/1a 2b 3c 4d 5e 6f/;
22             my @B = qw/5e 1f 2b 3r 4d 7q j n/;
23              
24             use Data::KeyDiff qw/diff/;
25            
26             diff( \@A, \@B,
27             key =>
28             sub($item) {
29             # Return the leading number from $item
30             },
31              
32             is_different =>
33             sub($a, $b) {
34             # Is the letter on $a different from $b?
35             },
36              
37             is_new =>
38             sub($item) {
39             # Does $item already have a key?
40             },
41              
42             # "j" and "n" are new!
43             new => sub($element) {
44             # Handle a new $element
45             },
46              
47             # "7q" was inserted (already had a key)
48             insert => sub($element) {
49             # $element was "inserted" into @B
50             },
51              
52             # "1f" and "3r" were updated
53             update => sub($element) {
54             # $element was "update" in @B
55             },
56              
57             # "6f" was deleted
58             delete => sub($element) {
59             # $element was "deleted" in @B
60             },
61            
62             # "5e", "2b", and "4d" changed rank
63             update_rank => sub($element) {
64             # $element had it's rank changed in @B
65             },
66             );
67              
68             =head1 DESCRIPTION
69              
70             Data::KeyDiff performs a diff-like operation on sets that have unique keys associated with each element.
71             Instead of looking at the whole list, C looks at each element on a case-by-case basis to see whether it's state or
72             inclusion has changed from the "before" set to the "after" set.
73              
74             =head1 METHODS
75              
76             =head2 Data::KeyDiff->diff( , , )
77              
78             Compare the before-set to the after-set. Call handlers in as defined.
79              
80             Besides the before-set and after-set, this method accepts the following:
81              
82             =over
83              
84             =item ignore($item) OPTIONAL
85              
86             A subroutine that returns true if $item should be ignored (e.g. commented). If an item ignored, the rank counter is not incremented, but the position counter still is.
87              
88             =item prepare($item) OPTIONAL
89              
90             A subroutine that returns a replacement for $item in further processing. Basically, this allows you to preprocess the $item before passing it to C, C, etc.
91              
92             =item is_new($item)
93              
94             A subroutine that returns true if $item is "new" and so doesn't already have a key.
95             Note, this subroutine is not run on the before-set (every item in that set should already have a key).
96              
97             =item key($item)
98              
99             A subroutine that returns the key of $item.
100              
101             =item is_different($before_item, $after_item, $before_element, $after_element)
102              
103             =item compare($before_item, $after_item, $before_element, $after_element)
104              
105             A subroutine that returns true if $before_item is different from $after_item.
106              
107             =item new($element) OPTIONAL
108              
109             Called for each new $element
110              
111             =item insert($element) OPTIONAL
112              
113             Called for each $element that should be inserted
114              
115             =item update($element) OPTIONAL
116              
117             Called for each $element that should be updated
118              
119             =item update_rank($element) OPTIONAL
120              
121             Called for each $element that is otherwise the same, but has a different rank
122              
123             =item delete($element) OPTIONAL
124              
125             Called for each $element that should be deleted
126              
127             =back
128              
129             =head1 EXPORTS
130              
131             =head2 diff( ... )
132              
133             Same syntax as above. See above for more information.
134              
135             =head1 AUTHOR
136              
137             Robert Krimen, C<< >>
138              
139             =head1 BUGS
140              
141             Please report any bugs or feature requests to C, or through
142             the web interface at L. I will be notified, and then you'll
143             automatically be notified of progress on your bug as I make changes.
144              
145              
146              
147              
148             =head1 SUPPORT
149              
150             You can find documentation for this module with the perldoc command.
151              
152             perldoc Data::KeyDiff
153              
154              
155             You can also look for information at:
156              
157             =over 4
158              
159             =item * RT: CPAN's request tracker
160              
161             L
162              
163             =item * AnnoCPAN: Annotated CPAN documentation
164              
165             L
166              
167             =item * CPAN Ratings
168              
169             L
170              
171             =item * Search CPAN
172              
173             L
174              
175             =back
176              
177              
178             =head1 ACKNOWLEDGEMENTS
179              
180              
181             =head1 COPYRIGHT & LICENSE
182              
183             Copyright 2007 Robert Krimen, all rights reserved.
184              
185             This program is free software; you can redistribute it and/or modify it
186             under the same terms as Perl itself.
187              
188              
189             =cut
190              
191 2     2   1027 use Data::KeyDiff::Element;
  2         5  
  2         16  
192 2     2   46 use Carp;
  2         3  
  2         1479  
193              
194             require Exporter;
195             @Data::KeyDiff::ISA = qw/Exporter/;
196             @Data::KeyDiff::EXPORT_OK = qw/diff/;
197              
198             sub diff {
199 6 50 33 6 1 145229 shift if $_[0] && $_[0] eq __PACKAGE__;
200 6         214 my $before = shift;
201 6         15 my $after = shift;
202 6         113 my %in = @_;
203              
204             my $get_key = $in{key} || $in{get_key} || sub {
205 0     0     return shift;
206 6   50     42 };
207 6         18 my $is_new = $in{is_new};
208 6         15 my $prepare = $in{prepare};
209             my $is_different = $in{is_different} || $in{compare} || sub {
210 0     0     my $left = shift;
211 0           my $right = shift;
212 0           return ((defined $left ^ defined $right) || (defined $left && $left ne $right));
213 6   50     21 };
214 6         15 my $ignore = $in{ignore};
215 6         25 my ($on_new, $on_insert, $on_update, $on_update_rank, $on_delete) = @in{qw/new insert update update_rank delete/};
216              
217 6         8 my %before;
218             my %after;
219 0         0 my (@new, %insert, %update, %update_rank, %delete);
220              
221 6         12 my $position = my $rank = 0;
222 6         11 $position--;
223 6         9 my $item;
224 6         18 for $item (@$before) {
225 24         29 $position++;
226 24 50 33     56 next if $ignore && $ignore->($item);
227 24 50       50 my $value = $prepare ? $prepare->($item) : $item;
228 24         61 my $key = $get_key->($value, $item);
229 24         183 my $element = Data::KeyDiff::Element->new(key => $key, value => $value, position => $position, rank => $rank++, item => $item, in_before => 1);
230 24         336 $before{$key} = $element;
231             }
232              
233 6         16 $position = $rank = 0;
234 6         9 $position--;
235 6         17 for $item (@$after) {
236 32         49 $position++;
237 32 50 33     84 next if $ignore && $ignore->($item);
238 32 50       64 my $value = $prepare ? $prepare->($item) : $item;
239 32 100 66     126 if ($is_new && $is_new->($value, $item)) {
240 8         88 my $element = Data::KeyDiff::Element->new(value => $value, position => $position, rank => $rank++, item => $item, is_new => 1);
241 8         89 push @new, $element;
242 8         25 next;
243             }
244 24         192 my $key = $get_key->($value, $item);
245 24         157 my $element = Data::KeyDiff::Element->new(key => $key, value => $value, position => $position, rank => $rank++, item => $item, in_after => 1);
246 24         244 $after{$key} = $element;
247 24 100       1048 if (! $before{$key}) {
    100          
    50          
248 7         24 $insert{$key}++;
249             }
250             elsif ($is_different->($before{$key}->value, $element->value, $before{$key}, $element)) {
251 8         409 $update{$key}++;
252             }
253             elsif ($before{$key}->rank != $after{$key}->rank) {
254 9         429 $update_rank{$key}++;
255             }
256             }
257              
258 6         51 for my $key (keys %before) {
259 24 100       54 next if exists $after{$key};
260 7         13 $delete{$key}++;
261             }
262              
263 6 50       22 if ($on_new) {
264 6         21 $on_new->($_) for @new;
265             }
266              
267 6 50       419 if ($on_insert) {
268 6         26 $on_insert->($after{$_}) for keys %insert;
269             }
270              
271 6 50       202 if ($on_update) {
272 6         27 $on_update->($after{$_}, $before{$_}) for keys %update;
273             }
274              
275 6 50       202 if ($on_update_rank) {
276 6         30 $on_update_rank->($after{$_}, $before{$_}) for keys %update_rank;
277             }
278              
279 6 50       267 if ($on_delete) {
280 6         74 $on_delete->($before{$_}) for keys %delete;
281             }
282             }
283              
284             1; # End of Data::KeyDiff