File Coverage

lib/CPANPLUS/Shell/Default/Plugins/Diff.pm
Criterion Covered Total %
statement 25 85 29.4
branch 0 30 0.0
condition 0 10 0.0
subroutine 9 11 81.8
pod 0 3 0.0
total 34 139 24.4


line stmt bran cond sub pod time code
1             package CPANPLUS::Shell::Default::Plugins::Diff;
2              
3 1     1   587 use strict;
  1         1  
  1         39  
4 1     1   12712 use Text::Diff ();
  1         23905  
  1         26  
5 1     1   1049 use Data::Dumper;
  1         8409  
  1         189  
6 1     1   11 use File::Basename;
  1         1  
  1         63  
7 1     1   985 use Params::Check qw[check];
  1         5475  
  1         98  
8 1     1   1009 use CPANPLUS::Error qw[error msg];
  1         32151  
  1         86  
9 1     1   12 use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
  1         2  
  1         6  
10              
11 1     1   497 use vars qw[$VERSION];
  1         3  
  1         1098  
12             $VERSION = '0.01';
13              
14             local $Data::Dumper::Indent = 1;
15              
16             =head1 NAME
17              
18             CPANPLUS::Shell::Default::Plugins::Diff
19              
20             =head1 SYNOPSIS
21              
22             ### diff version 1.3 and 1.4
23             CPAN Terminal> /diff DBI 1.3 1.4
24              
25             ### diff version 1.3 against the most recent on CPAN
26             CPAN Terminal> /diff DBI 1.3
27              
28             ### diff your installed version against the most
29             ### recent on CPAN
30             CPAN Terminal> /diff DBI
31              
32             ### use context style diff
33             ### other options are: Unified, OldStyle
34             CPAN Terminal> /diff DBI --style=Context
35              
36             ### list help from withing the shell:
37             CPAN Terminal> /? diff
38              
39             =head1 DESCRIPTION
40              
41             This plugin allows you to diff 2 versions of modules and see what
42             code changes have taken place.
43              
44             =cut
45              
46 1     1 0 744 sub plugins { return ( diff => 'diff' ) }
47              
48             sub diff {
49 0     0 0   my $class = shift;
50 0           my $shell = shift;
51 0           my $cb = shift;
52 0           my $cmd = shift;
53 0   0       my $input = shift || '';
54 0   0       my $opts = shift || {};
55 0           my $verbose = $cb->configure_object->get_conf('verbose');
56              
57 0           my($name, $from, $to) = split /\s+/, $input;
58              
59 0           my $style;
60 0           { my $tmpl = {
  0            
61             style => { default => "Unified", store => \$style,
62             allow => [qw|Unified Context OldStyle|] },
63             };
64            
65 0 0         check( $tmpl, $opts, 1 ) or return;
66             }
67            
68 0 0         error(loc("No module supplied")), return unless $name;
69            
70 0 0         my $mod = $cb->parse_module( module => $name ) or (
71             error(loc("Could not parse module name '%1'"), $name),
72             return
73             );
74            
75             ### no 'from'?
76 0 0 0       unless( defined $from && length $from ) {
77            
78             ### not installed?
79 0 0         unless( $mod->installed_file ) {
80 0           error(loc("'%1' is not installed, need %2 version", $name, 'FROM'));
81 0           return;
82             }
83            
84 0           $from = $mod->installed_version;
85             }
86            
87             ### no 'to'?
88 0 0 0       $to = $mod->version unless defined $to && length $to;
89            
90 0           msg(loc("Diffing '%1' version '%2' against version '%3'",
91             $name, $from, $to), $verbose);
92            
93 0 0         if( "$to" eq "$from" ) {
94 0           error(loc("TO ('%1') and FROM ('%2') are identical", $to, $from));
95 0           return;
96             }
97            
98             ### fetch them, extract, and store
99 0           my $href = {};
100 0           { my %map = ( FROM => $from, TO => $to );
  0            
101            
102 0           while ( my($txt,$ver) = each %map ) {
103 0           my $obj = $cb->parse_module(
104             module => $mod->package_name . '-' . $ver );
105 0 0         error(loc("Couldn't create '%1' object'",'FROM')), return
106             unless $obj;
107            
108 0 0         $obj->fetch
109             or error(loc("Could not fetch '%1'",$txt)), return;
110 0 0         $obj->extract
111             or error(loc("Could not extract '%1'",$txt)), return;
112            
113            
114 0           $href->{$txt} = $obj;
115             }
116             }
117            
118            
119             ### make 2 hashes of the files in each tree...
120             ### be sure to strip the leading extract dir, as that will
121             ### cause mismatches further down. IE:
122             ### foo-bar-0.1/README vs foo-bar-0.2/README
123             ### the 'foo-bar' part is also present in the 'extract' status
124             ### so one of the 2 has to be removed either way.
125             ### use index 1 rather than 0, as 0 will usually hold just a dirname
126             ### which will mess up dirname() and return undef...
127            
128 0           my $fstatus = $href->{FROM}->status;
129 0           my $fbase = dirname( $fstatus->files->[1] );
130              
131 0           my $tstatus = $href->{ TO }->status;
132 0           my $tbase = dirname( $tstatus->files->[1] );
133            
134 0           my %old = map { s/^$fbase//; $_ => $_ } @{ $fstatus->files };
  0            
  0            
  0            
135 0           my %new = map { s/^$tbase//; $_ => $_ } @{ $tstatus->files };
  0            
  0            
  0            
136            
137 0           my $diff;
138            
139 0           for my $file ( sort keys %old ) {
140            
141 0           my $exists = delete $new{$file};
142 0           my $from_file = File::Spec->catfile( $fstatus->extract, $file );
143 0           my $to_file = File::Spec->catfile( $tstatus->extract, $file );
144              
145 0 0         next if -d $from_file;
146            
147             ### if the file doesn't exist in the target 'to' dir,
148             ### pass a reference to 'undef'
149 0 0         $diff .= Text::Diff::diff(
    0          
150             $from_file,
151             $exists ? $to_file : \undef,
152             { FILENAME_A => $from_file,
153             FILENAME_B => $exists ? $to_file : '/dev/null',
154             STYLE => $style,
155             }
156             );
157             }
158            
159             ### any files left in 'new' are new files, treat 'm as such
160 0           for my $file ( sort keys %new ) {
161 0           my $to_file = File::Spec->catfile( $tstatus->extract, $file );
162              
163 0 0         next if -d $to_file;
164            
165 0           $diff .= Text::Diff::diff(
166             \undef,
167             $to_file,
168             { FILENAME_A => '/dev/null',
169             FILENAME_B => $file,
170             STYLE => $style,
171             }
172             );
173             }
174            
175 0 0         $shell->_pager_open if $diff =~ tr/\n/\n/ > $shell->_term_rowcount;
176 0           print $diff;
177 0           $shell->_pager_close;
178            
179             }
180              
181             sub diff_help {
182 0     0 0   return loc(
183             " /diff Module [[FROM] TO] [--style=STYLE]\n" .
184             " Diffs the contents of 2 releases\n".
185             " if TO is not supplied, the most recent release is used\n".
186             " if FROM is not supplied, the currently installed version,\n" .
187             " if any, is used\n".
188             " Valid values for STYLE are: 'Unified', 'Context', 'OldStyle'\n"
189             );
190             }
191              
192             1;
193              
194              
195             =pod
196              
197             =head1 AUTHOR
198              
199             This module by
200             Jos Boumans Ekane@cpan.orgE.
201              
202             =head1 COPYRIGHT
203              
204             Copyright (c) 2005, Jos Boumans Ekane@cpan.orgE.
205             All rights reserved.
206              
207             This library is free software;
208             you may redistribute and/or modify it under the same
209             terms as Perl itself.
210              
211             =head1 SEE ALSO
212              
213             L, L,
214             L
215              
216             =cut
217              
218             # Local variables:
219             # c-indentation-style: bsd
220             # c-basic-offset: 4
221             # indent-tabs-mode: nil
222             # End:
223             # vim: expandtab shiftwidth=4: