File Coverage

blib/lib/Pinto/PackageExtractor.pm
Criterion Covered Total %
statement 70 79 88.6
branch 4 8 50.0
condition 2 4 50.0
subroutine 16 20 80.0
pod 0 3 0.0
total 92 114 80.7


line stmt bran cond sub pod time code
1             # ABSTRACT: Extract packages provided/required by a distribution archive
2              
3             package Pinto::PackageExtractor;
4              
5 51     51   343 use Moose;
  51         115  
  51         409  
6 51     51   330447 use MooseX::StrictConstructor;
  51         156  
  51         508  
7 51     51   163450 use MooseX::MarkAsMethods ( autoclean => 1 );
  51         116  
  51         464  
8              
9 51     51   176524 use Try::Tiny;
  51         134  
  51         3906  
10 51     51   20647 use Dist::Metadata;
  51         78471  
  51         1635  
11              
12 51     51   440 use Pinto::Types qw(File Dir);
  51         143  
  51         554  
13 51     51   311897 use Pinto::Util qw(debug throw whine);
  51         120  
  51         3634  
14 51     51   19748 use Pinto::ArchiveUnpacker;
  51         227  
  51         62505  
15              
16             #-----------------------------------------------------------------------------
17              
18             our $VERSION = '0.14'; # VERSION
19              
20             #-----------------------------------------------------------------------------
21              
22             has archive => (
23             is => 'ro',
24             isa => File,
25             required => 1,
26             coerce => 1,
27             );
28              
29             has unpacker => (
30             is => 'ro',
31             isa => 'Pinto::ArchiveUnpacker',
32             default => sub { Pinto::ArchiveUnpacker->new( archive => $_[0]->archive ) },
33             init_arg => undef,
34             lazy => 1,
35             );
36              
37             has work_dir => (
38             is => 'ro',
39             isa => Dir,
40             default => sub { $_[0]->unpacker->unpack },
41             init_arg => undef,
42             lazy => 1,
43             );
44              
45             has dm => (
46             is => 'ro',
47             isa => 'Dist::Metadata',
48             default => sub { Dist::Metadata->new( dir => $_[0]->work_dir, include_inner_packages => 1 ) },
49             init_arg => undef,
50             lazy => 1,
51             );
52              
53             #-----------------------------------------------------------------------------
54              
55             sub provides {
56 161     161 0 493 my ($self) = @_;
57              
58 161         4709 my $archive = $self->archive;
59 161         619 my $basename = $archive->basename;
60 161         1531 debug "Extracting packages provided by archive $basename";
61              
62             my $mod_info = try {
63              
64             # Some modules get their VERSION by loading some other
65             # module from lib/. So make sure that lib/ is in @INC
66 161     161   12387 my $lib_dir = $self->work_dir->subdir('lib');
67 161         9048 local @INC = ( $lib_dir->stringify, @INC );
68              
69             # TODO: Run this under Safe to protect ourselves
70             # from evil. See ANDK/pause/pmfile.pm for example
71 161         11394 $self->dm->module_info; # returned from try{}
72             }
73             catch {
74 0     0   0 throw "Unable to extract packages from $basename: $_";
75 161         1916 };
76              
77 161         1871840 my @provides;
78 161         630 for my $package ( sort keys %{$mod_info} ) {
  161         1158  
79              
80 194         769 my $info = $mod_info->{$package};
81 194         1833 my $version = version->parse( $info->{version} );
82 194         4561 debug "Archive $basename provides: $package-$version";
83              
84             push @provides, {
85             name => $package,
86             version => $version,
87             file => $info->{file},
88 194         1362 };
89             }
90              
91 161         1329 @provides = $self->__apply_workarounds(@provides);
92              
93 161 50       768 whine "$basename contains no packages and will not be in the index"
94             if not @provides;
95              
96 161         1524 return @provides;
97             }
98              
99             #-----------------------------------------------------------------------------
100              
101             sub requires {
102 161     161 0 510 my ($self) = @_;
103              
104 161         4487 my $archive = $self->archive;
105 161         997 debug "Extracting packages required by archive $archive";
106              
107 161     161   17832 my $prereqs_meta = try { $self->dm->meta->prereqs }
108 161     0   2812 catch { throw "Unable to extract prereqs from $archive: $_" };
  0         0  
109              
110 161         8356 my @prereqs;
111 161         436 for my $phase ( keys %{$prereqs_meta} ) {
  161         945  
112              
113             # TODO: Also capture the relation (suggested, requires, recomends, etc.)
114             # But that will require a schema change to add another column to the table.
115              
116 58   50     304 my $prereqs_for_phase = $prereqs_meta->{$phase} || {};
117 58   50     286 my $required_prereqs = $prereqs_for_phase->{requires} || {};
118              
119 58         126 for my $package ( sort keys %{$required_prereqs} ) {
  58         265  
120              
121 68         219 my $version = $required_prereqs->{$package};
122 68         344 debug "Archive $archive requires ($phase): $package~$version";
123              
124 68         603 push @prereqs, {
125             name => $package,
126             version => $version,
127             phase => $phase,
128             };
129              
130             }
131             }
132              
133 161         757 my $base = $archive->basename;
134              
135 161 50       1495 whine "$base appears to be a bundle. Prereqs for bundles cannot be determined automatically"
136             if $base =~ m/^ Bundle- /x;
137              
138             # whine "$base uses dynamic configuration so prereqs may be incomplete"
139             # if $self->dm->meta->dynamic_config;
140              
141 161         1013 return @prereqs;
142             }
143              
144             #-----------------------------------------------------------------------------
145              
146             sub metadata {
147 161     161 0 541 my ($self) = @_;
148              
149 161         5019 my $archive = $self->archive;
150 161         902 debug "Extracting metadata from archive $archive";
151              
152 161     78   2074 my $metadata = try { $self->dm->meta } catch { throw "Unable to extract metadata from $archive: $_" };
  161     23   11465  
  0         0  
153              
154 161         3679 return $metadata;
155             }
156              
157             #=============================================================================
158             # TODO: Generalize these workarounds and/or move them into a separate module
159              
160             sub __apply_workarounds {
161 161     161   762 my ($self, @provides) = @_;
162              
163 161 50       8024 return $self->__common_sense_workaround(@provides)
164             if $self->archive->basename =~ m/^ common-sense /x;
165              
166 161 50       6085 return $self->__fcgi_workaround(@provides)
167             if $self->archive->basename =~ m/^ FCGI-\d /x;
168              
169 161         1684 return @provides;
170             }
171              
172             #-----------------------------------------------------------------------------
173              
174             sub __common_sense_workaround {
175 0     0     my ($self) = @_;
176              
177 0           my ($version) = ( $self->archive->basename =~ m/common-sense- ([\d_.]+) \.tar\.gz/x );
178              
179             return {
180 0           name => 'common::sense',
181             file => 'sense.pm.PL',
182             version => version->parse($version),
183             };
184             }
185              
186             #-----------------------------------------------------------------------------
187              
188             sub __fcgi_workaround {
189 0     0     my ($self) = @_;
190              
191 0           my ($version) = ( $self->archive->basename =~ m/FCGI- ([\d_.]+) \.tar\.gz/x );
192              
193             return {
194 0           name => 'FCGI',
195             file => 'FCGI.PL',
196             version => version->parse($version),
197             };
198             }
199              
200             #-----------------------------------------------------------------------------
201              
202             __PACKAGE__->meta->make_immutable;
203              
204             #-----------------------------------------------------------------------------
205              
206             1;
207              
208             __END__
209              
210             =pod
211              
212             =encoding UTF-8
213              
214             =for :stopwords Jeffrey Ryan Thalhammer
215              
216             =head1 NAME
217              
218             Pinto::PackageExtractor - Extract packages provided/required by a distribution archive
219              
220             =head1 VERSION
221              
222             version 0.14
223              
224             =head1 AUTHOR
225              
226             Jeffrey Ryan Thalhammer <jeff@stratopan.com>
227              
228             =head1 COPYRIGHT AND LICENSE
229              
230             This software is copyright (c) 2015 by Jeffrey Ryan Thalhammer.
231              
232             This is free software; you can redistribute it and/or modify it under
233             the same terms as the Perl 5 programming language system itself.
234              
235             =cut