File Coverage

blib/lib/Dist/Metadata.pm
Criterion Covered Total %
statement 106 109 97.2
branch 29 34 85.2
condition 22 32 68.7
subroutine 20 20 100.0
pod 10 10 100.0
total 187 205 91.2


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   4921 use strict;
  6         12  
  6         298  
11 6     6   31 use warnings;
  6         10  
  6         480  
12              
13             package Dist::Metadata;
14             # git description: v0.925-17-g08a6891
15              
16             our $AUTHORITY = 'cpan:RWSTAUNER';
17             # ABSTRACT: Information about a perl module distribution
18             $Dist::Metadata::VERSION = '0.926';
19 6     6   34 use Carp qw(croak carp);
  6         10  
  6         531  
20 6     6   3700 use CPAN::Meta 2.1 ();
  6         138581  
  6         180  
21 6     6   57 use List::Util qw(first); # core in perl v5.7.3
  6         6  
  6         7374  
22              
23             # something that is obviously not a real value
24             sub UNKNOWN () { '- unknown -' } # constant
25              
26              
27             sub new {
28 48     48 1 20982 my $class = shift;
29 0         0 my $self = {
30             determine_packages => 1,
31 48 50       299 @_ == 1 ? %{ $_[0] } : @_
32             };
33              
34 48         193 my @formats = qw( dist file dir struct );
35 0         0 croak(qq[A dist must be specified (one of ] .
36             join(', ', map { "'$_'" } @formats) . ')')
37 48 50   148   429 unless first { $self->{$_} } @formats;
  148         289  
38              
39 48         246 bless $self, $class;
40             }
41              
42              
43             sub dist {
44 292     292 1 1677 my ($self) = @_;
45 292   66     2287 return $self->{dist} ||= do {
46 48         59 my $dist;
47 48 100       212 if( my $struct = $self->{struct} ){
    100          
    50          
48 22         2435 require Dist::Metadata::Struct;
49 22         238 $dist = Dist::Metadata::Struct->new(%$struct);
50             }
51             elsif( my $dir = $self->{dir} ){
52 8         764 require Dist::Metadata::Dir;
53 8         55 $dist = Dist::Metadata::Dir->new(dir => $dir);
54             }
55             elsif ( my $file = $self->{file} ){
56 18         1150 require Dist::Metadata::Archive;
57 18         124 $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         262 $dist; # return
64             };
65             }
66              
67              
68             sub default_metadata {
69 50     50 1 428 my ($self) = @_;
70              
71             return {
72             # required
73 50   33     1495 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 90 my ($self) = @_;
109              
110 49         123 my $dist = $self->dist;
111 49         145 my $meta = $self->default_metadata;
112              
113             # get name and version from dist if dist was able to parse them
114 49         262 foreach my $att (qw(name version)) {
115 98         512 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       290 $meta->{$att} = $val
119             if defined $val;
120             }
121              
122             # any passed in values should take priority
123 49         231 foreach my $field ( keys %$meta ){
124 441 50       667 $meta->{$field} = $self->{$field}
125             if exists $self->{$field};
126             }
127              
128 49         133 return $meta;
129             }
130              
131              
132             sub determine_packages {
133             # meta must be passed to avoid infinite loop
134 40     40 1 5710 my ( $self, $meta ) = @_;
135             # if not passed in, use defaults (we just want the 'no_index' property)
136 40   66     170 $meta ||= $self->meta_from_struct( $self->determine_metadata );
137              
138             # should_index_file() expects unix paths
139 88         95971 my @files = grep {
140 40         16997 $meta->should_index_file(
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       69659 return {} if not @files;
150              
151 40         154 my $packages = $self->dist->determine_packages(@files);
152              
153              
154 40         161 foreach my $pack ( keys %$packages ) {
155              
156             # Remove any packages that should not be indexed
157 75 100       354 if ( !$meta->should_index_package($pack) ) {
158 2         5017 delete $packages->{$pack};
159 2         69 next;
160             }
161              
162 73 100       104296 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         645 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     1033 delete $packages->{$pack}
172             if !$base || $pack !~ m{\b\Q$base\E$};
173             }
174             }
175              
176 40         307 return $packages;
177             }
178              
179              
180             sub load_meta {
181 44     44 1 1457 my ($self) = @_;
182              
183 44         123 my $dist = $self->dist;
184 44         286 my @files = $dist->list_files;
185 44         96 my ( $meta, $metafile );
186 44         146 my $default_meta = $self->determine_metadata;
187              
188             # prefer json file (spec v2)
189 44 100   92   295 if ( $metafile = first { m#^META\.json$# } @files ) {
  92 100       297  
190 12         58 $meta = CPAN::Meta->load_json_string( $dist->file_content($metafile) );
191             }
192             # fall back to yaml file (spec v1)
193 80     80   169 elsif ( $metafile = first { m#^META\.ya?ml$# } @files ) {
194 2         8 $meta = CPAN::Meta->load_yaml_string( $dist->file_content($metafile) );
195             }
196             # no META file found in dist
197             else {
198 30         88 $meta = $self->meta_from_struct( $default_meta );
199             }
200              
201             {
202             # always include (never index) the default no_index dirs
203 44   100     165305 my $dir = ($meta->{no_index} ||= {})->{directory} ||= [];
  44   100     308  
204 44         93 my %seen = map { ($_ => 1) } @$dir;
  223         448  
205 264         492 unshift @$dir,
206 44         119 grep { !$seen{$_}++ }
207 44         95 @{ $default_meta->{no_index}->{directory} };
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 100     39 if ( !keys %{ $meta->provides || {} } && $self->{determine_packages} ) {
  44 100       165  
213             # respect api/encapsulation
214 34         1334 my $struct = $meta->as_struct;
215 34         93170 $struct->{provides} = $self->determine_packages($meta);
216 34         135 $meta = $self->meta_from_struct($struct);
217             }
218              
219 44         151504 return $meta;
220             }
221              
222              
223             sub meta {
224 147     147 1 215 my ($self) = @_;
225 147   66     899 return $self->{meta} ||= $self->load_meta;
226             }
227              
228              
229             sub meta_from_struct {
230 70     70 1 139 my ($self, $struct) = @_;
231 70         570 return CPAN::Meta->create( $struct, { lazy_validation => 1 } );
232             }
233              
234              
235             sub package_versions {
236 39     39 1 12934 my ($self) = shift;
237 39 100       159 my $provides = @_ ? shift : $self->provides; # || {}
238             return {
239 39         39296 map { ($_ => $provides->{$_}{version}) } keys %$provides
  62         594  
240             };
241             }
242              
243              
244             sub module_info {
245 13     13 1 20382 my ($self, $opts) = @_;
246 13   66     97 my $provides = $opts->{provides} || $self->provides;
247 13         17932 $provides = { %$provides }; # break reference
248              
249 13   100     124 my $checksums = $opts->{checksum} || $opts->{digest} || [];
250 13 100       53 $checksums = [ $checksums ]
251             unless ref($checksums) eq 'ARRAY';
252              
253 13         31 my $digest_cache = {};
254 13         46 foreach my $mod ( keys %$provides ){
255 21         32 my $data = { %{ $provides->{ $mod } } }; # break reference
  21         102  
256              
257 21         57 foreach my $checksum ( @$checksums ){
258 22   33     279 $data->{ $checksum } =
259             $digest_cache->{ $data->{file} }->{ $checksum } ||=
260             $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         289 $provides->{ $mod } = $data;
266             }
267              
268 13         89 return $provides;
269             }
270              
271              
272             {
273 6     6   47 no strict 'refs'; ## no critic (NoStrict)
  6         14  
  6         469  
274             foreach my $method ( qw(
275             name
276             provides
277             version
278             ) ){
279 147     147   77413 *$method = sub { $_[0]->meta->$method };
280             }
281             }
282              
283             1;
284              
285             __END__