File Coverage

blib/lib/Dist/Metadata.pm
Criterion Covered Total %
statement 106 109 97.2
branch 29 34 85.2
condition 21 32 65.6
subroutine 20 20 100.0
pod 10 10 100.0
total 186 205 90.7


line stmt bran cond sub pod time code
1             # vim: set ts=2 sts=2 sw=2 expandtab smarttab:
2             #
3             # This file is part of Dist-Metadata
4             #
5             # This software is copyright (c) 2011 by Randy Stauner.
6             #
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             #
10 6     6   3943 use strict;
  6         10  
  6         175  
11 6     6   22 use warnings;
  6         8  
  6         354  
12              
13             package Dist::Metadata;
14             # git description: v0.926-3-ge4f15df
15              
16             our $AUTHORITY = 'cpan:RWSTAUNER';
17             # ABSTRACT: Information about a perl module distribution
18             $Dist::Metadata::VERSION = '0.927';
19 6     6   40 use Carp qw(croak carp);
  6         11  
  6         476  
20 6     6   2328 use CPAN::Meta 2.1 ();
  6         102477  
  6         159  
21 6     6   44 use List::Util qw(first); # core in perl v5.7.3
  6         9  
  6         7015  
22              
23             # something that is obviously not a real value
24             sub UNKNOWN () { '- unknown -' } # constant
25              
26              
27             sub new {
28 48     48 1 28044 my $class = shift;
29             my $self = {
30             determine_packages => 1,
31 48 50       225 @_ == 1 ? %{ $_[0] } : @_
  0         0  
32             };
33              
34 48         155 my @formats = qw( dist file dir struct );
35             croak(qq[A dist must be specified (one of ] .
36 0         0 join(', ', map { "'$_'" } @formats) . ')')
37 48 50   148   301 unless first { $self->{$_} } @formats;
  148         217  
38              
39 48         196 bless $self, $class;
40             }
41              
42              
43             sub dist {
44 292     292 1 2017 my ($self) = @_;
45 292   66     1391 return $self->{dist} ||= do {
46 48         60 my $dist;
47 48 100       224 if( my $struct = $self->{struct} ){
    100          
    50          
48 22         2157 require Dist::Metadata::Struct;
49 22         181 $dist = Dist::Metadata::Struct->new(%$struct);
50             }
51             elsif( my $dir = $self->{dir} ){
52 8         556 require Dist::Metadata::Dir;
53 8         62 $dist = Dist::Metadata::Dir->new(dir => $dir);
54             }
55             elsif ( my $file = $self->{file} ){
56 18         810 require Dist::Metadata::Archive;
57 18         112 $dist = Dist::Metadata::Archive->new(file => $file);
58             }
59             else {
60             # new() checks for one and dies without so we shouldn't get here
61 0         0 croak q[No dist format parameters found!];
62             }
63 48         197 $dist; # return
64             };
65             }
66              
67              
68             sub default_metadata {
69 50     50 1 404 my ($self) = @_;
70              
71             return {
72             # required
73 50   33     1070 abstract => UNKNOWN,
      50        
74             author => [],
75             dynamic_config => 0,
76             generated_by => ( ref($self) || $self ) . ' version ' . ( $self->VERSION || 0 ),
77             license => ['unknown'], # this 'unknown' comes from CPAN::Meta::Spec
78             'meta-spec' => {
79             version => '2',
80             url => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
81             },
82             name => UNKNOWN,
83              
84             # strictly speaking, release_status is also required but
85             # CPAN::Meta will figure it out based on the version number. if
86             # we were to set it explicitly, then we would first need to
87             # examine the version number for '_' or 'TRIAL' or 'RC' etc.
88              
89             version => 0,
90              
91             # optional
92             no_index => {
93             # Ignore the same directories as PAUSE (https://github.com/andk/pause/blob/master/lib/PAUSE/dist.pm#L758):
94             # skip "t" - libraries in ./t are test libraries!
95             # skip "xt" - libraries in ./xt are author test libraries!
96             # skip "inc" - libraries in ./inc are usually install libraries
97             # skip "local" - somebody shipped his carton setup!
98             # skip 'perl5" - somebody shipped her local::lib!
99             # skip 'fatlib' - somebody shipped their fatpack lib!
100             directory => [qw( inc t xt local perl5 fatlib )],
101             },
102             # provides => { package => { file => $file, version => $version } }
103             };
104             }
105              
106              
107             sub determine_metadata {
108 49     49 1 64 my ($self) = @_;
109              
110 49         89 my $dist = $self->dist;
111 49         137 my $meta = $self->default_metadata;
112              
113             # get name and version from dist if dist was able to parse them
114 49         202 foreach my $att (qw(name version)) {
115 98         367 my $val = $dist->$att;
116             # if the dist could determine it that's better than the default
117             # but undef won't validate. value in $self will still override.
118 98 100       238 $meta->{$att} = $val
119             if defined $val;
120             }
121              
122             # any passed in values should take priority
123 49         190 foreach my $field ( keys %$meta ){
124             $meta->{$field} = $self->{$field}
125 441 50       578 if exists $self->{$field};
126             }
127              
128 49         105 return $meta;
129             }
130              
131              
132             sub determine_packages {
133             # meta must be passed to avoid infinite loop
134 40     40 1 3352 my ( $self, $meta ) = @_;
135             # if not passed in, use defaults (we just want the 'no_index' property)
136 40   66     136 $meta ||= $self->meta_from_struct( $self->determine_metadata );
137              
138             # should_index_file() expects unix paths
139             my @files = grep {
140 40         12736 $meta->should_index_file(
  88         70220  
141             $self->dist->path_classify_file($_)->as_foreign('Unix')->stringify
142             );
143             }
144             $self->dist->perl_files;
145              
146             # TODO: should we limit packages to lib/ if it exists?
147             # my @lib = grep { m#^lib/# } @files; @files = @lib if @lib;
148              
149 40 50       55189 return {} if not @files;
150              
151 40         110 my $packages = $self->dist->determine_packages(@files);
152              
153              
154 40         133 foreach my $pack ( keys %$packages ) {
155              
156             # Remove any packages that should not be indexed
157 75 100       516 if ( !$meta->should_index_package($pack) ) {
158 2         2562 delete $packages->{$pack};
159 2         4 next;
160             }
161              
162 73 100       80773 unless( $self->{include_inner_packages} ){
163             # PAUSE only considers packages that match the basename of the
164             # containing file. For example, file Foo.pm may only contain a
165             # package that matches /\bFoo$/. This is what PAUSE calls a
166             # "simile". All other packages in the file will be ignored.
167              
168             # capture file basename (without the extension)
169 60         426 my ($base) = ($packages->{$pack}->{file} =~ m!([^/]+)\.pm(?:\.PL)?$!);
170             # remove if file didn't match regexp or package doesn't match basename
171 60 100 66     733 delete $packages->{$pack}
172             if !$base || $pack !~ m{\b\Q$base\E$};
173             }
174             }
175              
176 40         200 return $packages;
177             }
178              
179              
180             sub load_meta {
181 44     44 1 1819 my ($self) = @_;
182              
183 44         112 my $dist = $self->dist;
184 44         221 my @files = $dist->list_files;
185 44         51 my ( $meta, $metafile );
186 44         128 my $default_meta = $self->determine_metadata;
187              
188             # prefer json file (spec v2)
189 44 100   92   235 if ( $metafile = first { m#^META\.json$# } @files ) {
  92 100       286  
190 12         47 $meta = CPAN::Meta->load_json_string( $dist->file_content($metafile) );
191             }
192             # fall back to yaml file (spec v1)
193 80     80   137 elsif ( $metafile = first { m#^META\.ya?ml$# } @files ) {
194 2         6 $meta = CPAN::Meta->load_yaml_string( $dist->file_content($metafile) );
195             }
196             # no META file found in dist
197             else {
198 30         78 $meta = $self->meta_from_struct( $default_meta );
199             }
200              
201             {
202             # always include (never index) the default no_index dirs
203 44   100     138240 my $dir = ($meta->{no_index} ||= {})->{directory} ||= [];
  44   100     237  
204 44         87 my %seen = map { ($_ => 1) } @$dir;
  223         341  
205             unshift @$dir,
206 264         400 grep { !$seen{$_}++ }
207 44         86 @{ $default_meta->{no_index}->{directory} };
  44         106  
208             }
209              
210             # Something has to be indexed, so if META has no (or empty) 'provides'
211             # attempt to determine packages unless specifically configured not to
212 44 100 66     53 if ( !keys %{ $meta->provides || {} } && $self->{determine_packages} ) {
  44 100       105  
213             # respect api/encapsulation
214 34         961 my $struct = $meta->as_struct;
215 34         84468 $struct->{provides} = $self->determine_packages($meta);
216 34         153 $meta = $self->meta_from_struct($struct);
217             }
218              
219 44         139864 return $meta;
220             }
221              
222              
223             sub meta {
224 147     147 1 163 my ($self) = @_;
225 147   66     666 return $self->{meta} ||= $self->load_meta;
226             }
227              
228              
229             sub meta_from_struct {
230 70     70 1 93 my ($self, $struct) = @_;
231 70         389 return CPAN::Meta->create( $struct, { lazy_validation => 1 } );
232             }
233              
234              
235             sub package_versions {
236 39     39 1 16492 my ($self) = shift;
237 39 100       135 my $provides = @_ ? shift : $self->provides; # || {}
238             return {
239 39         33609 map { ($_ => $provides->{$_}{version}) } keys %$provides
  62         347  
240             };
241             }
242              
243              
244             sub module_info {
245 13     13 1 11187 my ($self, $opts) = @_;
246 13   66     44 my $provides = $opts->{provides} || $self->provides;
247 13         10966 $provides = { %$provides }; # break reference
248              
249 13   100     54 my $checksums = $opts->{checksum} || $opts->{digest} || [];
250 13 100       29 $checksums = [ $checksums ]
251             unless ref($checksums) eq 'ARRAY';
252              
253 13         11 my $digest_cache = {};
254 13         58 foreach my $mod ( keys %$provides ){
255 21         14 my $data = { %{ $provides->{ $mod } } }; # break reference
  21         51  
256              
257 21         33 foreach my $checksum ( @$checksums ){
258             $data->{ $checksum } =
259             $digest_cache->{ $data->{file} }->{ $checksum } ||=
260 22   33     92 $self->dist->file_checksum($data->{file}, $checksum);
261             }
262              
263             # TODO: $opts->{callback}->($self, $mod, $data, sub { $self->dist->file_content($data->{file}) });
264              
265 21         44 $provides->{ $mod } = $data;
266             }
267              
268 13         47 return $provides;
269             }
270              
271              
272             {
273 6     6   34 no strict 'refs'; ## no critic (NoStrict)
  6         7  
  6         427  
274             foreach my $method ( qw(
275             name
276             provides
277             version
278             ) ){
279 147     147   60602 *$method = sub { $_[0]->meta->$method };
280             }
281             }
282              
283             1;
284              
285             __END__