File Coverage

blib/lib/OrePAN2/Auditor.pm
Criterion Covered Total %
statement 71 83 85.5
branch 5 10 50.0
condition 0 3 0.0
subroutine 18 19 94.7
pod 2 3 66.6
total 96 118 81.3


line stmt bran cond sub pod time code
1              
2             use Moo 1.007000;
3 1     1   2172  
  1         11481  
  1         5  
4             use feature qw( say state );
5 1     1   1210 use version 0.9912;
  1         1  
  1         92  
6 1     1   342  
  1         1591  
  1         5  
7             use Carp qw( croak );
8 1     1   68 use List::Compare ();
  1         2  
  1         35  
9 1     1   731 use MooX::Options;
  1         18259  
  1         22  
10 1     1   450 use Parse::CPAN::Packages::Fast 0.09 ();
  1         3192  
  1         8  
11 1     1   107026 use Path::Tiny ();
  1         41577  
  1         22  
12 1     1   688 use Type::Params qw( compile );
  1         9699  
  1         26  
13 1     1   1176 use Type::Tiny::Enum ();
  1         85673  
  1         9  
14 1     1   1453 use Types::Standard qw( ArrayRef Bool InstanceOf Str );
  1         981  
  1         25  
15 1     1   6 use Types::URI qw( Uri );
  1         1  
  1         5  
16 1     1   1098 use LWP::UserAgent ();
  1         84418  
  1         9  
17 1     1   945  
  1         32270  
  1         1021  
18             my $SHOW = Type::Tiny::Enum->new(
19             name => 'Show',
20             values =>
21             [ 'cpan-only-modules', 'darkpan-only-modules', 'outdated-modules' ],
22             );
23              
24             option cpan => (
25             is => 'ro',
26             isa => Uri,
27             format => 's',
28             required => 1,
29             coerce => 1,
30             doc => 'the path to a CPAN 02packages file',
31             );
32              
33             option darkpan => (
34             is => 'ro',
35             isa => Uri,
36             format => 's',
37             required => 1,
38             coerce => 1,
39             doc => 'the path to your DarkPan 02packages file',
40             );
41              
42             option show => (
43             is => 'ro',
44             isa => $SHOW,
45             format => 's',
46             );
47              
48             option verbose => (
49             is => 'ro',
50             isa => Bool,
51             default => 0,
52             );
53              
54             has cpan_modules => (
55             is => 'ro',
56             isa => ArrayRef [Str],
57             lazy => 1,
58             default => sub {
59             my $self = shift;
60             return $self->_modules_from_parser( $self->_cpan_parser );
61             },
62             );
63              
64             has darkpan_modules => (
65             is => 'ro',
66             isa => ArrayRef [Str],
67             lazy => 1,
68             default => sub {
69             my $self = shift;
70             return $self->_modules_from_parser( $self->_darkpan_parser );
71             },
72             );
73              
74             has cpan_only_modules => (
75             is => 'ro',
76             isa => ArrayRef [Str],
77             lazy => 1,
78             default => sub {
79             return [ shift->_list_compare->get_complement ];
80             },
81             );
82              
83             has darkpan_only_modules => (
84             is => 'ro',
85             isa => ArrayRef [Str],
86             lazy => 1,
87             default => sub {
88             return [ shift->_list_compare->get_unique ];
89             },
90             );
91              
92             has outdated_modules => (
93             is => 'ro',
94             isa => ArrayRef [Str],
95             lazy => 1,
96             builder => '_build_outdated_modules',
97             );
98              
99             has ua => (
100             is => 'ro',
101             isa => InstanceOf ['LWP::UserAgent'],
102             default => sub {
103             return LWP::UserAgent->new();
104             },
105             );
106              
107             has _cpan_parser => (
108             is => 'ro',
109             isa => InstanceOf ['Parse::CPAN::Packages::Fast'],
110             lazy => 1,
111             default => sub {
112             my $self = shift;
113             return $self->_parser_for_url( $self->cpan );
114             },
115             );
116              
117             has _darkpan_parser => (
118             is => 'ro',
119             isa => InstanceOf ['Parse::CPAN::Packages::Fast'],
120             lazy => 1,
121             default => sub {
122             my $self = shift;
123             return $self->_parser_for_url( $self->darkpan );
124             },
125             );
126              
127             has _list_compare => (
128             is => 'ro',
129             isa => InstanceOf ['List::Compare'],
130             lazy => 1,
131             default => sub {
132             my $self = shift;
133             return List::Compare->new(
134             $self->darkpan_modules,
135             $self->cpan_modules
136             );
137             },
138             );
139              
140             my $self = shift;
141              
142 0     0 0 0 my $method = $self->show;
143             $method =~ s{-}{_}g;
144 0         0  
145 0         0 my $modules = $self->$method;
146              
147 0         0 if ( $method eq 'outdated_modules' && $self->verbose ) {
148             foreach my $module ( @{$modules} ) {
149 0 0 0     0 my @row = (
150 0         0 $module,
  0         0  
151 0         0 $self->darkpan_module($module)->distribution->distvname,
152             $self->cpan_module($module)->distribution->distvname,
153              
154             sprintf(
155             'https://metacpan.org/changes/distribution/%s',
156             $self->cpan_module($module)->distribution->dist
157             ),
158             );
159             say join "\t", @row;
160             }
161 0         0 return;
162             }
163 0         0  
164             say $_ for @{$modules};
165             }
166 0         0  
  0         0  
167             my $self = shift;
168             state $check = compile(Str);
169             my ($module) = $check->(@_);
170 1     1 1 479  
171 1         6 return $self->_cpan_parser->package($module);
172 1         884 }
173              
174 1         31 my $self = shift;
175             state $check = compile(Str);
176             my ($module) = $check->(@_);
177              
178 1     1 1 1322 return $self->_darkpan_parser->package($module);
179 1         4 }
180 1         654  
181             my $self = shift;
182 1         31  
183             my $darkpan = $self->_darkpan_parser;
184             my $cpan = $self->_cpan_parser;
185              
186 1     1   547 my @outdated;
187             for my $module ( $self->_list_compare->get_intersection ) {
188 1         14 if ( version->parse( $darkpan->package($module)->version )
189 1         19 < version->parse( $cpan->package($module)->version ) ) {
190             push @outdated, $module;
191 1         16 }
192 1         16 }
193 1 50       32 return \@outdated;
194             }
195 1         62  
196             my $self = shift;
197             my $parser = shift;
198 1         27  
199             return [ sort { $a cmp $b } $parser->packages ];
200             }
201              
202 2     2   2278 my $self = shift;
203 2         4 my $url = shift;
204              
205 2         12 $url->scheme('file') if !$url->scheme;
  3         58  
206              
207             my $res = $self->ua->get($url);
208             croak "could not fetch $url" if !$res->is_success;
209 2     2   4  
210 2         3 # dumb hack to avoid having to uncompress this ourselves
211             my @path_segments = $url->path_segments;
212 2 100       9  
213             my $err = <<"EOF";
214 2         240 Path invalid for $url Please provide full path to 02packages file.
215 2 50       22970 EOF
216             croak $err if !@path_segments;
217              
218 2         21 my $tempdir = Path::Tiny->tempdir;
219             my $child = $tempdir->child( pop @path_segments );
220 2         128 $child->spew_raw( $res->content );
221              
222             return Parse::CPAN::Packages::Fast->new( $child->stringify );
223 2 50       14 }
224              
225 2         13 1;
226 2         9053  
227 2         70  
228             =pod
229 2         912  
230             =head1 SYNOPSIS
231              
232             my $auditor = OrePAN2::Auditor->new(
233             cpan => 'https://cpan.metacpan.org/modules/02packages.details.txt',
234             darkpan => '/full/path/to/darkpan/02packages.details.txt'
235             );
236              
237             # ArrayRef of module names
238             my $outdated_modules = $auditor->outdated_modules;
239              
240             =head1 DESCRIPTION
241              
242             If you have a local DarkPAN or MiniCPAN or something which has its own
243             C<02packages.txt> file, it can be helpful to know which files are outdated or
244             which files exist in your DarkPAN, but not on CPAN (or vice versa). This
245             module makes this easy for you.
246              
247             Think of it as a way of diffing C<02packages> files.
248              
249             =head2 new
250              
251             my $auditor = OrePAN2::Auditor->new(
252             cpan => 'https://cpan.metacpan.org/modules/02packages.details.txt',
253             darkpan => '/full/path/to/darkpan/02packages.details.txt'
254             );
255              
256             The C<cpan> and C<darkpan> args are the only required arguments. These can
257             either be a path on your filesystem or a full URL to the 02packages files which
258             you'd like to diff.
259              
260             =head2 cpan_modules
261              
262             An C<ArrayRef> of module names which exist currently on CPAN.
263              
264             =head2 cpan_only_modules
265              
266             An C<ArrayRef> of module names which exist currently on CPAN but not in your DarkPAN.
267              
268             =head2 darkpan_modules
269              
270             An C<ArrayRef> of module names which exist currently on your DarkPAN.
271              
272             =head2 darkpan_only_modules
273              
274             An C<ArrayRef> of module names which exist currently on your DarkPAN but not in CPAN.
275              
276             =head2 outdated_modules
277              
278             An C<ArrayRef> of module names which exist currently on both your DarkPAN and
279             on CPAN and for which the module in your DarkPAN has a lower version number.
280              
281             =head2 cpan_module( $module_name )
282              
283             my $module = $auditor->cpan_module( 'HTML::Restrict' );
284              
285             Returns a L<Parse::CPAN::Packages::Fast::Package> object.
286              
287             =head2 darkpan_module( $module_name )
288              
289             my $module = $auditor->cpan_module( 'HTML::Restrict' );
290              
291             Returns a L<Parse::CPAN::Packages::Fast::Package> object.
292              
293             =cut