File Coverage

blib/lib/Dist/Metadata.pm
Criterion Covered Total %
statement 110 113 97.3
branch 29 34 85.2
condition 22 32 68.7
subroutine 22 22 100.0
pod 10 10 100.0
total 193 211 91.4


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   6172 use strict;
  6         14  
  6         551  
11 6     6   36 use warnings;
  6         14  
  6         453  
12              
13             package Dist::Metadata;
14             {
15             $Dist::Metadata::VERSION = '0.925';
16             }
17             # git description: v0.924-1-g6701fe0
18              
19             BEGIN {
20 6     6   137 $Dist::Metadata::AUTHORITY = 'cpan:RWSTAUNER';
21             }
22             # ABSTRACT: Information about a perl module distribution
23              
24 6     6   36 use Carp qw(croak carp);
  6         10  
  6         485  
25 6     6   5500 use CPAN::Meta 2.1 ();
  6         198568  
  6         207  
26 6     6   69 use List::Util qw(first); # core in perl v5.7.3
  6         15  
  6         559  
27              
28             # something that is obviously not a real value
29 6     6   36 use constant UNKNOWN => '- unknown -';
  6         15  
  6         10090  
30              
31              
32             sub new {
33 47     47 1 28327 my $class = shift;
34 0         0 my $self = {
35             determine_packages => 1,
36 47 50       299 @_ == 1 ? %{ $_[0] } : @_
37             };
38              
39 47         202 my @formats = qw( dist file dir struct );
40 0         0 croak(qq[A dist must be specified (one of ] .
41             join(', ', map { "'$_'" } @formats) . ')')
42 47 50   144   412 unless first { $self->{$_} } @formats;
  144         351  
43              
44 47         298 bless $self, $class;
45             }
46              
47              
48             sub dist {
49 278     278 1 2782 my ($self) = @_;
50 278   66     4168 return $self->{dist} ||= do {
51 47         72 my $dist;
52 47 100       250 if( my $struct = $self->{struct} ){
    100          
    50          
53 21         3002 require Dist::Metadata::Struct;
54 21         236 $dist = Dist::Metadata::Struct->new(%$struct);
55             }
56             elsif( my $dir = $self->{dir} ){
57 8         827 require Dist::Metadata::Dir;
58 8         85 $dist = Dist::Metadata::Dir->new(dir => $dir);
59             }
60             elsif ( my $file = $self->{file} ){
61 18         2223 require Dist::Metadata::Archive;
62 18         182 $dist = Dist::Metadata::Archive->new(file => $file);
63             }
64             else {
65             # new() checks for one and dies without so we shouldn't get here
66 0         0 croak q[No dist format parameters found!];
67             }
68 47         369 $dist; # return
69             };
70             }
71              
72              
73             sub default_metadata {
74 49     49 1 559 my ($self) = @_;
75              
76             return {
77             # required
78 49   33     2252 abstract => UNKNOWN,
      50        
79             author => [],
80             dynamic_config => 0,
81             generated_by => ( ref($self) || $self ) . ' version ' . ( $self->VERSION || 0 ),
82             license => ['unknown'], # this 'unknown' comes from CPAN::Meta::Spec
83             'meta-spec' => {
84             version => '2',
85             url => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
86             },
87             name => UNKNOWN,
88              
89             # strictly speaking, release_status is also required but
90             # CPAN::Meta will figure it out based on the version number. if
91             # we were to set it explicitly, then we would first need to
92             # examine the version number for '_' or 'TRIAL' or 'RC' etc.
93              
94             version => 0,
95              
96             # optional
97             no_index => {
98             # ignore test and build directories by default
99             directory => [qw( inc t xt )],
100             },
101             # provides => { package => { file => $file, version => $version } }
102             };
103             }
104              
105              
106             sub determine_metadata {
107 48     48 1 110 my ($self) = @_;
108              
109 48         170 my $dist = $self->dist;
110 48         182 my $meta = $self->default_metadata;
111              
112             # get name and version from dist if dist was able to parse them
113 48         514 foreach my $att (qw(name version)) {
114 96         659 my $val = $dist->$att;
115             # if the dist could determine it that's better than the default
116             # but undef won't validate. value in $self will still override.
117 96 100       575 $meta->{$att} = $val
118             if defined $val;
119             }
120              
121             # any passed in values should take priority
122 48         234 foreach my $field ( keys %$meta ){
123 432 50       964 $meta->{$field} = $self->{$field}
124             if exists $self->{$field};
125             }
126              
127 48         156 return $meta;
128             }
129              
130              
131             sub determine_packages {
132             # meta must be passed to avoid infinite loop
133 39     39 1 5137 my ( $self, $meta ) = @_;
134             # if not passed in, use defaults (we just want the 'no_index' property)
135 39   66     203 $meta ||= $self->meta_from_struct( $self->determine_metadata );
136              
137             # should_index_file() expects unix paths
138 78         86545 my @files = grep {
139 39         20186 $meta->should_index_file(
140             $self->dist->path_classify_file($_)->as_foreign('Unix')->stringify
141             );
142             }
143             $self->dist->perl_files;
144              
145             # TODO: should we limit packages to lib/ if it exists?
146             # my @lib = grep { m#^lib/# } @files; @files = @lib if @lib;
147              
148 39 50       77075 return {} if not @files;
149              
150 39         151 my $packages = $self->dist->determine_packages(@files);
151              
152              
153 39         192 foreach my $pack ( keys %$packages ) {
154              
155             # Remove any packages that should not be indexed
156 72 100       365 if ( !$meta->should_index_package($pack) ) {
157 2         3263 delete $packages->{$pack};
158 2         26 next;
159             }
160              
161 70 100       119595 unless( $self->{include_inner_packages} ){
162             # PAUSE only considers packages that match the basename of the
163             # containing file. For example, file Foo.pm may only contain a
164             # package that matches /\bFoo$/. This is what PAUSE calls a
165             # "simile". All other packages in the file will be ignored.
166              
167             # capture file basename (without the extension)
168 57         674 my ($base) = ($packages->{$pack}->{file} =~ m!([^/]+)\.pm(?:\.PL)?$!);
169             # remove if file didn't match regexp or package doesn't match basename
170 57 100 66     1131 delete $packages->{$pack}
171             if !$base || $pack !~ m{\b\Q$base\E$};
172             }
173             }
174              
175 39         349 return $packages;
176             }
177              
178              
179             sub load_meta {
180 43     43 1 2431 my ($self) = @_;
181              
182 43         138 my $dist = $self->dist;
183 43         357 my @files = $dist->list_files;
184 43         1812 my ( $meta, $metafile );
185 43         156 my $default_meta = $self->determine_metadata;
186              
187             # prefer json file (spec v2)
188 43 100   91   307 if ( $metafile = first { m#^META\.json$# } @files ) {
  91 100       461  
189 11         51 $meta = CPAN::Meta->load_json_string( $dist->file_content($metafile) );
190             }
191             # fall back to yaml file (spec v1)
192 80     80   195 elsif ( $metafile = first { m#^META\.ya?ml$# } @files ) {
193 2         7 $meta = CPAN::Meta->load_yaml_string( $dist->file_content($metafile) );
194             }
195             # no META file found in dist
196             else {
197 30         157 $meta = $self->meta_from_struct( $default_meta );
198             }
199              
200             {
201             # always inlude (never index) the default no_index dirs
202 43   100     246620 my $dir = ($meta->{no_index} ||= {})->{directory} ||= [];
  43   100     347  
203 43         101 my %seen = map { ($_ => 1) } @$dir;
  132         476  
204 129         398 unshift @$dir,
205 43         139 grep { !$seen{$_}++ }
206 43         121 @{ $default_meta->{no_index}->{directory} };
207             }
208              
209             # Something has to be indexed, so if META has no (or empty) 'provides'
210             # attempt to determine packages unless specifically configured not to
211 43 100 100     78 if ( !keys %{ $meta->provides || {} } && $self->{determine_packages} ) {
  43 100       192  
212             # respect api/encapsulation
213 33         1891 my $struct = $meta->as_struct;
214 33         151683 $struct->{provides} = $self->determine_packages($meta);
215 33         166 $meta = $self->meta_from_struct($struct);
216             }
217              
218 43         245789 return $meta;
219             }
220              
221              
222             sub meta {
223 146     146 1 253 my ($self) = @_;
224 146   66     1084 return $self->{meta} ||= $self->load_meta;
225             }
226              
227              
228             sub meta_from_struct {
229 69     69 1 148 my ($self, $struct) = @_;
230 69         665 return CPAN::Meta->create( $struct, { lazy_validation => 1 } );
231             }
232              
233              
234             sub package_versions {
235 38     38 1 33026 my ($self) = shift;
236 38 100       207 my $provides = @_ ? shift : $self->provides; # || {}
237             return {
238 38         63301 map { ($_ => $provides->{$_}{version}) } keys %$provides
  59         637  
239             };
240             }
241              
242              
243             sub module_info {
244 13     13 1 19153 my ($self, $opts) = @_;
245 13   66     71 my $provides = $opts->{provides} || $self->provides;
246 13         19351 $provides = { %$provides }; # break reference
247              
248 13   100     94 my $checksums = $opts->{checksum} || $opts->{digest} || [];
249 13 100       53 $checksums = [ $checksums ]
250             unless ref($checksums) eq 'ARRAY';
251              
252 13         26 my $digest_cache = {};
253 13         40 foreach my $mod ( keys %$provides ){
254 21         31 my $data = { %{ $provides->{ $mod } } }; # break reference
  21         87  
255              
256 21         50 foreach my $checksum ( @$checksums ){
257 22   33     201 $data->{ $checksum } =
258             $digest_cache->{ $data->{file} }->{ $checksum } ||=
259             $self->dist->file_checksum($data->{file}, $checksum);
260             }
261              
262             # TODO: $opts->{callback}->($self, $mod, $data, sub { $self->dist->file_content($data->{file}) });
263              
264 21         231 $provides->{ $mod } = $data;
265             }
266              
267 13         78 return $provides;
268             }
269              
270              
271             {
272 6     6   42 no strict 'refs'; ## no critic (NoStrict)
  6         13  
  6         552  
273             foreach my $method ( qw(
274             name
275             provides
276             version
277             ) ){
278 146     146   142290 *$method = sub { $_[0]->meta->$method };
279             }
280             }
281              
282             1;
283              
284             __END__