File Coverage

blib/lib/CPAN/Diff.pm
Criterion Covered Total %
statement 27 98 27.5
branch 0 36 0.0
condition 0 11 0.0
subroutine 9 24 37.5
pod 0 7 0.0
total 36 176 20.4


line stmt bran cond sub pod time code
1             package CPAN::Diff;
2 1     1   1724 use Moo;
  1         15279  
  1         6  
3              
4 1     1   1610 use Config;
  1         1  
  1         31  
5 1     1   845 use ExtUtils::Installed;
  1         121629  
  1         39  
6 1     1   1135 use HTTP::Tiny;
  1         51391  
  1         48  
7 1     1   873 use Module::Extract::Namespaces;
  1         153295  
  1         38  
8 1     1   1015 use Module::Metadata;
  1         5719  
  1         35  
9 1     1   838 use Parse::CPAN::Packages::Fast;
  1         50509  
  1         33  
10 1     1   9 use version;
  1         2  
  1         9  
11 1     1   623 use CPAN::Diff::Module;
  1         3  
  1         1320  
12              
13             our $VERSION = "0.02";
14              
15             has mirror => (is => 'ro', builder => 1);
16             has exclude_core => (is => 'rw');
17             has local_lib => (is => 'rw');
18             has self_contained => (is => 'rw');
19              
20             has extra_modules => (is => 'rw', lazy => 1, builder => 1);
21             has newer_modules => (is => 'rw', lazy => 1, builder => 1);
22             has older_modules => (is => 'rw', lazy => 1, builder => 1);
23              
24             has core_modules => (is => 'rw', lazy => 1, builder => 1);
25             has tmp_dir => (is => 'rw', builder => 1);
26             has cache => (is => 'rw', lazy => 1, builder => 1);
27              
28 0     0     sub _build_mirror { "http://cpan.org" };
29 0     0     sub _build_tmp_dir { "/tmp" };
30             sub _build_core_modules {
31 0 0   0     return undef unless shift->exclude_core;
32 0           require Module::CoreList;
33 0           $Module::CoreList::version{$]};
34             }
35              
36 0     0     sub _build_extra_modules { shift->cache->{extra} }
37 0     0     sub _build_newer_modules { shift->cache->{newer} }
38 0     0     sub _build_older_modules { shift->cache->{older} }
39              
40 0     0 0   sub case_insensitive { lc($a) cmp lc($b) }
41              
42             sub _build_cache {
43 0     0     my ($self) = @_;
44 0           my @inc = $self->make_inc;
45 0           my $cpan = $self->cpan;
46 0           my @local_pkgs = $self->get_local_pkgs(@inc);
47 0           my (@extra, @newer, @older);
48              
49 0           for my $local_pkg (sort case_insensitive @local_pkgs) {
50 0           my $pkg = $cpan->package($local_pkg);
51 0   0       my $local_version = $self->local_version_for($local_pkg, \@inc) || next;
52 0 0         next unless $local_version =~ /[0-9]/;
53 0 0 0       next if $self->core_modules && $self->core_modules->{$local_pkg};
54 0 0         my $metadata = CPAN::Diff::Module->new(
    0          
55             name => $local_pkg,
56             local_version => $local_version,
57             cpan_version => $pkg ? $pkg->version : undef,
58             cpan_dist => $pkg ? $pkg->distribution : undef,
59             );
60              
61 0 0         if (!$pkg) {
62 0           push @extra, $metadata;
63             }
64             else {
65 0           my $result = $self->compare_version($local_version, $pkg->version);
66 0 0         next if $result == 0;
67 0 0         push @newer, $metadata if $result == 1;
68 0 0         push @older, $metadata if $result == -1;
69             }
70             }
71              
72 0           return { extra => \@extra, newer => \@newer, older => \@older };
73             }
74              
75             sub cpan {
76 0     0 0   my $self = shift;
77 0           my $file;
78              
79 0 0         if ($self->mirror =~ m|^file\://(.*)|i) {
80 0           $file = $1;
81             }
82             else {
83 0           my $uri = $self->mirror;
84 0           $uri =~ s|/$||;
85 0           $uri = "$uri/modules/02packages.details.txt.gz";
86              
87 0           my $unique = $uri;
88 0           $unique =~ s|/|_|g;
89 0           $file = $self->tmp_dir . "/" . $unique;
90              
91 0           my $res = HTTP::Tiny->new->mirror($uri, $file);
92             die "failed to download $uri to $file:\n$res->{status} $res->{reason}\n"
93 0 0         unless $res->{success};
94             }
95 0           return Parse::CPAN::Packages::Fast->new($file);
96             }
97              
98             sub compare_version {
99 0     0 0   my ($self, $local_version, $version) = @_;
100 0 0         return 0 if $local_version eq $version;
101              
102 0   0       my $local_version_obj = eval { version->new($local_version) } || version->new(permissive_filter($local_version));
103 0   0       my $version_obj = eval { version->new($version) } || version->new(permissive_filter($version));
104              
105 0 0         return 1 if $local_version_obj > $version_obj;
106 0 0         return -1 if $local_version_obj < $version_obj;
107 0 0         return 0 if $local_version_obj == $version_obj;
108             }
109              
110             # for broken packages.
111             sub permissive_filter {
112 0     0 0   local $_ = $_[0];
113 0           s/^[Vv](\d)/$1/; # Bioinf V2.0
114 0           s/^(\d+)_(\d+)$/$1.$2/; # VMS-IndexedFile 0_02
115 0           s/-[a-zA-Z]+$//; # Math-Polygon-Tree 0.035-withoutworldwriteables
116 0           s/([a-j])/ord($1)-ord('a')/gie; # DBD-Solid 0.20a
  0            
117 0           s/[_h-z-]/./gi; # makepp 1.50.2vs.070506
118 0           s/\.{2,}/./g;
119 0           $_;
120             }
121              
122             sub local_version_for {
123 0     0 0   my ($self, $pkg, $inc) = @_;
124              
125 0     0     local $SIG{__WARN__} = sub {};
126 0           my $meta = Module::Metadata->new_from_module($pkg, inc => $inc);
127 0 0         $meta ? $meta->version($pkg) : undef;
128             }
129              
130             sub get_local_pkgs {
131 0     0 0   my ($self, @inc) = @_;
132             # TODO: if you want to filter the target modules, you can change them here.
133 0           ExtUtils::Installed->new(skip_cwd => 1, inc_override => \@inc)->modules;
134             }
135              
136             sub make_inc {
137 0     0 0   my ($self) = @_;
138              
139 0 0         if ($self->local_lib) {
140 0           require local::lib;
141 0           my @modified_inc = (
142             local::lib->install_base_perl_path($self->local_lib),
143             local::lib->install_base_arch_path($self->local_lib),
144             );
145 0 0         if ($self->self_contained) {
146 0           push @modified_inc, @Config{qw(privlibexp archlibexp)};
147             } else {
148 0           push @modified_inc, @INC;
149             }
150 0           return @modified_inc;
151             } else {
152 0           return @INC;
153             }
154             }
155              
156             1;
157             __END__