File Coverage

blib/lib/Config/Hierarchical/Delta.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1              
2             package Config::Hierarchical::Delta ;
3 1     1   1944 use base Exporter ;
  1         3  
  1         99  
4              
5 1     1   7 use strict;
  1         2  
  1         38  
6 1     1   5 use warnings ;
  1         2  
  1         39  
7              
8             BEGIN 
9             {
10 1     1   6 use Exporter ();
  1         2  
  1         30  
11              
12 1     1   5 use vars qw ($VERSION @ISA @EXPORT_OK %EXPORT_TAGS);
  1         3  
  1         182  
13              
14 1     1   3 $VERSION     = '0.01' ;
15 1         3 @EXPORT_OK   = qw (GetConfigDelta GetConfigHierarchicalDelta DumpConfigHierarchicalDelta Get_NoIdentical_Filter);
16 1         28 %EXPORT_TAGS = ();
17             }
18              
19             #-------------------------------------------------------------------------------
20              
21 1     1   12 use Carp ;
  1         2  
  1         144  
22 1     1   6 use Data::Compare ;
  1         2  
  1         11  
23 1     1   2121 use Data::TreeDumper ;
  0            
  0            
24             use Sub::Install ;
25              
26             use English qw( -no_match_vars ) ;
27              
28             use Readonly ;
29             Readonly my $EMPTY_STRING => q{} ;
30              
31             =head1 NAME
32            
33             Config::Hierarchical::Delta - Comparator for hashes and Config::Hierarchical objects
34            
35             =head1 SYNOPSIS
36            
37             # comparing hashes:
38            
39             use Config::Hierarchical ;
40             use Config::Hierarchical::Delta qw (GetConfigDelta GetConfigHierarchicalDelta DumpConfigHierarchicalDelta Get_NoIdentical_Filter) ;
41            
42             my $delta = GetConfigDelta
43             (
44             {name => {A => 1, COMMON => 0}},
45             {name_2 => {B => 2, COMMON => 0}}
46             ) ;
47            
48             $delta is a reference to the following hash:
49            
50             {
51             'in \'name\' only' => {'A' => 1},
52             'in \'name_2\' only' => {'B' => 2},
53             'identical' => {'COMMON' => 0},
54             },
55            
56             # comparing Config Hierarchical objects:
57            
58             my $config_0 = new Config::Hierarchical
59             (
60             NAME => 'config 0',
61             INITIAL_VALUES =>
62             [
63             {NAME => 'CC1', VALUE => '1'},
64             {NAME => 'CC2', VALUE => '2'},
65             ] ,
66             ) ;
67            
68             my $config_1 = new Config::Hierarchical
69             (
70             NAME => 'config 1',
71             CATEGORY_NAMES => ['A', 'B',],
72             DEFAULT_CATEGORY => 'A',
73            
74             INITIAL_VALUES =>
75             [
76             {CATEGORY => 'B', ALIAS => $config_0},
77            
78             {NAME => 'CC1', VALUE => '1'},
79             {NAME => 'CC2', VALUE => '2'},
80             {NAME => 'CC3', VALUE => '3'},
81             ] ,
82             ) ;
83            
84             $config_1->Set(NAME => 'CC1', VALUE => '1.1') ;
85            
86             my $config_2 = new Config::Hierarchical
87             (
88             NAME => 'config 2',
89            
90             CATEGORY_NAMES => ['<A>', 'B',],
91             DEFAULT_CATEGORY => 'A',
92             INITIAL_VALUES =>
93             [
94             {CATEGORY => 'B', ALIAS => $config_1},
95             ] ,
96             ) ;
97            
98             $config_2->Set(CATEGORY => 'A', NAME => 'CC1', VALUE => 'A', OVERRIDE => 1) ;
99             $config_2->Set(CATEGORY => 'A', NAME => 'XYZ', VALUE => 'xyz') ;
100            
101             my $dump = DumpConfigHierarchicalDelta($config_2, $config_0) ;
102            
103             $dump contains the following string:
104            
105             Delta between 'config 2' and 'config 0'':
106             |- different
107             | `- CC1
108             | |- config 0 = 1
109             | `- config 2 = A
110             |- identical
111             | `- CC2 = 2
112             `- in 'config 2' only
113             |- CC3 = 3
114             `- XYZ = xyz
115            
116             =head1 DESCRIPTION
117            
118             This module lets you compare hashes and Config::Hierarchical objects.
119            
120             =head1 DOCUMENTATION
121            
122            
123             =head1 SUBROUTINES/METHODS
124            
125             =cut
126              
127             #-------------------------------------------------------------------------------
128              
129             sub GetConfigDelta
130             {
131              
132             =head2 GetConfigDelta
133            
134             my $delta = GetConfigDelta
135             (
136             {name => {A => 1, COMMON => 0}},
137             {name_2 => {B => 2, COMMON => 0}}
138             ) ;
139            
140             B<GetConfigDelta> compares two hashes and returns a reference to a hash containing up to 4 elements.
141             It takes as argument two hash reference which contain a single element. The key is used as name for the hash
142             while the value is a reference to the hash to be compared.
143            
144             Returned elements:
145            
146             =over 2
147            
148             =item * identical
149            
150             Contains all the elements that are identical in both hashes as well as the value they have
151            
152             =item * different
153            
154             Contains the elements that are common in both hashes but with different values
155            
156             =item * in 'lhs' only
157            
158             Contains the elements that exists in the first hash but not in the second hash .
159            
160             =item * in 'rhs' only
161            
162             Contains the elements that exists in the first hash but not in the second hash .
163            
164             =back
165            
166             =cut
167              
168             my ($lhs, $rhs) = @_ ;
169              
170             die "GetConfigDelta: Error, wrong argument type on the left hand side, expected hash with a single element!\n" unless 'HASH' eq ref $lhs ;
171             die "GetConfigDelta: Error, wrong argument type on the right hand side, expected hash with a single element!\n" unless 'HASH' eq ref $rhs ;
172              
173             die "GetConfigDelta: Error, only one element expected on left hand side\n" unless 1 == scalar(keys %{$lhs}) ;
174             die "GetConfigDelta: Error, only one element expected on right hand side\n" unless 1 == scalar(keys %{$rhs}) ;
175              
176             my $lhs_name = (keys %{$lhs})[0] ;
177             my $rhs_name = (keys %{$rhs})[0] ;
178              
179             die "GetConfigDelta: Error, expected a HASH as a config on the left hand side\n" unless 'HASH' eq ref $lhs->{$lhs_name} ;
180             die "GetConfigDelta: Error, expected a HASH as a config on the right hand side\n" unless 'HASH' eq ref $rhs->{$rhs_name} ;
181              
182             # make lhs and rhs point to the configs
183             ($lhs,  $rhs) = ($lhs->{$lhs_name}, $rhs->{$rhs_name}) ;
184              
185             my %delta ;
186              
187             for my $key( keys %{$lhs})
188             {
189             if(exists $rhs->{$key})
190             {
191             if(!Compare($rhs->{$key}, $lhs->{$key}))
192             {
193             $delta{different}{$key} = {$lhs_name => $lhs->{$key}, $rhs_name => $rhs->{$key}}
194             }
195             else
196             {
197             $delta{identical}{$key} = $lhs->{$key} ;
198             }
199             }
200             else
201             {
202             $delta{"in '$lhs_name' only"}{$key} = $lhs->{$key} ;
203             }
204             }
205            
206             for my $key( keys %{$rhs})
207             {
208             unless(exists $lhs->{$key})
209             {
210             $delta{"in '$rhs_name' only"}{$key} = $rhs->{$key} ;
211             }
212             }
213              
214             return(\%delta) ;
215             }
216               
217             #-------------------------------------------------------------------------------
218              
219             sub GetConfigHierarchicalDelta
220             {
221              
222             =head2 GetConfigHierarchicalDelta
223            
224             my $config_1 = new Config::Hierarchical(...) ;
225             my $config_2 = new Config::Hierarchical(...) ;
226            
227             GetConfigHierarchicalDelta($config_1, $config_2) ;
228            
229             Compares two B<Config::Hierarchical> objects and returns a reference to hash containing the delta between the
230             objects. See L<GetConfigDeleta> for a description of the returned hash.
231            
232             The name of the Config::Variable object is extracted from the objects.
233            
234             =cut
235              
236              
237             my ($lhs, $rhs) = @_ ;
238              
239             die "GetConfigHierarchicalDelta: Error, expected a 'Config::Hierarchical' on the left hand side\n" unless 'Config::Hierarchical' eq ref $lhs ;
240             die "GetConfigHierarchicalDelta: Error, expected a 'Config::Hierarchical' on the right hand side\n" unless 'Config::Hierarchical' eq ref $rhs ;
241              
242             my ($lhs_name) = $lhs->GetInformation() ;
243             my ($rhs_name) = $rhs->GetInformation() ;
244              
245             my $lhs_hash_ref = $lhs->GetHashRef() ;
246             my $rhs_hash_ref = $rhs->GetHashRef() ;
247              
248             return( GetConfigDelta({$lhs_name=> $lhs_hash_ref} ,{$rhs_name=> $rhs_hash_ref}) );
249             }
250              
251             #-------------------------------------------------------------------------------
252              
253             sub DumpConfigHierarchicalDelta
254             {
255              
256             =head2 DumpConfigHierarchicalDelta
257            
258             my $config_1 = new Config::Hierarchical(...)
259             my $config_2 = new Config::Hierarchical(...) ;
260            
261             print DumpConfigHierarchicalDelta($config_1, $config_2, QUOTE_VALUES => 1) ;
262            
263            
264             The first two arguments a L<Config::Hierarchical> objects, the rest of the arguments are passed
265             as is to L<Data::TreeDumper>.
266            
267             This sub returns a string containing the dump of the delta. See L<Synopsis> for an output example.
268            
269             =cut
270              
271             my ($lhs, $rhs, @other_arguments_to_data_treedumper) = @_ ;
272              
273             die "GetConfigHierarchicalDelta: Error, expected a 'Config::Hierarchical' on the left hand side\n" unless 'Config::Hierarchical' eq ref $lhs ;
274             die "GetConfigHierarchicalDelta: Error, expected a 'Config::Hierarchical' on the right hand side\n" unless 'Config::Hierarchical' eq ref $rhs ;
275              
276             my ($lhs_name) = $lhs->GetInformation() ;
277             my ($rhs_name) = $rhs->GetInformation() ;
278              
279             return
280             (
281             DumpTree GetConfigHierarchicalDelta($lhs ,$rhs) ,
282             "Delta between '$lhs_name' and '$rhs_name'':", 
283             DISPLAY_ADDRESS => 0,
284             @other_arguments_to_data_treedumper 
285             ) ;
286             }
287              
288             #-------------------------------------------------------------------------------
289              
290             sub Get_NoIdentical_Filter
291             {
292              
293             =head2 Get_NoIdentical_Filter
294            
295             Dumping a config delta with:
296            
297             print DumpConfigHierarchicalDelta($config_2, $config_0) ;
298            
299             Gives:
300            
301             $expected_dump = <<EOD ;
302             Delta between 'config 2' and 'config 0'':
303             |- different
304             | `- CC1
305             | |- config 0 = 1
306             | `- config 2 = A
307             |- identical
308             | `- CC2 = 2
309             `- in 'config 2' only
310             |- CC3 = 3
311             `- XYZ = xyz
312            
313            
314             if you do not want to display the configuration variables that are identical, use:
315            
316             print DumpConfigHierarchicalDelta($config_2, $config_0, Get_NoIdentical_Filter()) ;
317            
318             which gives:
319            
320             my $expected_dump = <<EOD ;
321             Delta between 'config 2' and 'config 0'':
322             |- different
323             | `- CC1
324             | |- config 0 = 1
325             | `- config 2 = A
326             `- in 'config 2' only
327             |- CC3 = 3
328             `- XYZ = xyz
329             EOD
330            
331             Returns a L<Data::TreeDumper> filter you can use to remove the 'identical' element from the delta hash.
332            
333             =cut
334              
335             return (LEVEL_FILTERS => {0 => sub {my $s = shift ; return('HASH', undef, grep {$_ ne 'identical'} keys %{$s})}}) ;
336             }
337              
338             #-------------------------------------------------------------------------------
339              
340             1 ;
341              
342             =head1 BUGS AND LIMITATIONS
343            
344             None so far.
345            
346             =head1 AUTHOR
347            
348             Khemir Nadim ibn Hamouda
349             CPAN ID: NKH
350             mailto:nadim@khemir.net
351            
352             =head1 LICENSE AND COPYRIGHT
353            
354             Copyright 2006-2007 Khemir Nadim. All rights reserved.
355            
356             This program is free software; you can redistribute
357             it and/or modify it under the same terms as Perl itself.
358            
359             =head1 SUPPORT
360            
361             You can find documentation for this module with the perldoc command.
362            
363             perldoc Config::Hierarchical
364            
365             You can also look for information at:
366            
367             =over 4
368            
369             =item * AnnoCPAN: Annotated CPAN documentation
370            
371             L<http://annocpan.org/dist/Config-Hierarchical>
372            
373             =item * RT: CPAN's request tracker
374            
375             Please report any bugs or feature requests to L <bug-config-hierarchical@rt.cpan.org>.
376            
377             We will be notified, and then you'll automatically be notified of progress on
378             your bug as we make changes.
379            
380             =item * Search CPAN
381            
382             L<http://search.cpan.org/dist/Config-Hierarchical>
383            
384             =back
385            
386             =head1 SEE ALSO
387            
388            
389             =cut
390