File Coverage

blib/lib/Module/CPANTS/Kwalitee/MetaYML.pm
Criterion Covered Total %
statement 80 138 57.9
branch 27 82 32.9
condition 12 31 38.7
subroutine 20 29 68.9
pod 3 3 100.0
total 142 283 50.1


line stmt bran cond sub pod time code
1             package Module::CPANTS::Kwalitee::MetaYML;
2 7     7   3818 use warnings;
  7         20  
  7         234  
3 7     7   42 use strict;
  7         15  
  7         161  
4 7     7   38 use File::Spec::Functions qw(catfile);
  7         16  
  7         402  
5 7     7   1546 use CPAN::Meta::YAML;
  7         16908  
  7         493  
6 7     7   3834 use CPAN::Meta::Validator;
  7         36575  
  7         290  
7 7     7   4065 use CPAN::Meta::Converter;
  7         114820  
  7         538  
8 7     7   94 use List::Util qw/first/;
  7         17  
  7         13223  
9              
10             our $VERSION = '1.00';
11             $VERSION =~ s/_//; ## no critic
12              
13 28     28 1 78 sub order { 10 }
14              
15             my $JSON_DECODER = _load_json_decoder() || do { require JSON::PP; JSON::PP->can('decode_json') };
16              
17             ##################################################################
18             # Analyse
19             ##################################################################
20              
21             sub analyse {
22 11     11 1 57 my $class = shift;
23 11         44 my $me = shift;
24 11         278 my $distdir = $me->distdir;
25 11         151 my $meta_yml = catfile($distdir, 'META.yml');
26 11         77 my $meta_json = catfile($distdir, 'META.json');
27 11         85 my $mymeta_yml = catfile($distdir, 'MYMETA.yml');
28              
29             # META.yml is not always the most preferred meta file,
30             # but test it anyway because it may be broken sometimes.
31 11 100 66     258 if (-f $meta_yml && -r _) {
32 3         30 _analyse_yml($me, $meta_yml);
33             }
34              
35             # check also META.json (if exists).
36 11 50 33     224 if (-f $meta_json && -r _) {
37 0         0 _analyse_json($me, $meta_json);
38             }
39              
40             # If, and only if META.yml and META.json don't exist,
41             # try MYMETA.yml
42 11 50 66     313 if (!$me->d->{meta_yml} && -f $mymeta_yml && -r _) {
      33        
43 0         0 _analyse_yml($me, $mymeta_yml);
44             }
45              
46 11 100       515 if (!$me->d->{meta_yml}) {
47 8         73 return;
48             }
49              
50             # Theoretically it might be better to convert 1.* to 2.0.
51             # However, converting 2.0 to 1.4 is much cheaper for CPANTS
52             # website as it's much rarer as of this writing.
53 3 50 50     64 if (($me->d->{meta_yml_spec_version} || '1.0') gt '1.4') {
54 0         0 my $cmc = CPAN::Meta::Converter->new($me->d->{meta_yml});
55 0         0 my $meta_14 = eval { $cmc->convert(version => '1.4') };
  0         0  
56 0 0 0     0 if (!$@ && $meta_14) {
57 0         0 $me->d->{meta_yml} = $meta_14;
58             }
59             }
60              
61 3 50 33     118 $me->d->{dynamic_config} = (!exists $me->d->{meta_yml}{dynamic_config} or $me->d->{meta_yml}{dynamic_config}) ? 1 : 0;
62             }
63              
64             sub _analyse_yml {
65 3     3   19 my ($me, $file) = @_;
66 3         9 eval {
67 3 50       25 my $meta = CPAN::Meta::YAML->read($file) or die CPAN::Meta::YAML->errstr;
68             # Broken META.yml may return a "YAML 1.0" string first.
69             # eg. M/MH/MHASCH/Date-Gregorian-0.07.tar.gz
70 3 50 33     4246 if (@$meta > 1 or ref $meta->[0] ne ref {}) {
71 0     0   0 $me->d->{meta_yml} = first { ref $_ eq ref {} } @$meta;
  0         0  
72 0         0 $me->d->{error}{meta_yml_is_parsable} = "multiple parts found in META.yml";
73             } else {
74 3         87 $me->d->{meta_yml} = $meta->[0];
75 3         83 $me->d->{meta_yml_is_parsable} = 1;
76             }
77             };
78 3 50       49 if (my $error = $@) {
79 0         0 $error =~ s/ at \S+ line \d+.+$//s;
80 0         0 $me->d->{error}{meta_yml_is_parsable} = $error;
81             }
82 3 50       63 if ($me->d->{meta_yml}) {
83 3         85 my ($spec, $error) = _validate_meta($me->d->{meta_yml});
84 3 50       12 $me->d->{error}{meta_yml_conforms_to_known_spec} = $error if $error;
85 3         100 $me->d->{meta_yml_spec_version} = $spec->{spec};
86             }
87             }
88              
89             sub _analyse_json {
90 0     0   0 my ($me, $file) = @_;
91              
92 0         0 my $meta;
93 0         0 eval {
94 0 0       0 my $json = do { open my $fh, '<', $file or die "$file: $!"; local $/; <$fh> };
  0         0  
  0         0  
  0         0  
95 0         0 $meta = $JSON_DECODER->($json);
96 0         0 $me->d->{meta_json_is_parsable} = 1;
97             };
98 0 0       0 if (my $error = $@) {
99 0         0 $error =~ s/ at \S+ line \d+.+$//s;
100 0         0 $me->d->{error}{meta_json_is_parsable} = $error;
101             }
102 0 0       0 if ($meta) {
103 0         0 my ($spec, $error) = _validate_meta($meta);
104 0 0       0 $me->d->{error}{meta_json_conforms_to_known_spec} = $error if $error;
105 0         0 $me->d->{meta_json_spec_version} = $spec->{spec};
106             }
107 0 0       0 if (!$me->d->{meta_yml}) {
108 0         0 $me->d->{meta_yml} = $meta;
109 0         0 $me->d->{meta_yml_spec_version} = $me->d->{meta_json_spec_version};
110 0         0 $me->d->{meta_yml_is_meta_json} = 1;
111             }
112             }
113              
114             sub _load_json_decoder {
115 7   50 7   92 my $json_class = $ENV{CPAN_META_JSON_BACKEND} || $ENV{PERL_JSON_BACKEND} || 'JSON::PP';
116 7 50       460 eval "require $json_class; 1" or return;
117 7         114 $json_class->can('decode_json');
118             }
119              
120             sub _validate_meta {
121 3     3   34 my $meta = shift;
122 3         12 my $error;
123 3         6 my $spec = eval { CPAN::Meta::Validator->new($meta) };
  3         54  
124 3 50       95 if ($error = $@) {
    50          
125 0         0 $error =~ s/ at \S+ line \d+.+$//s;
126             } elsif (!$spec->is_valid) {
127 0         0 $error = join ';', sort $spec->errors;
128             }
129 3         1104 return ($spec, $error);
130             }
131              
132             ##################################################################
133             # Kwalitee Indicators
134             ##################################################################
135              
136             sub kwalitee_indicators{
137             return [
138             {
139             name => 'meta_yml_is_parsable',
140             error => q{The META.yml file of this distribution could not be parsed by the version of CPAN::Meta::YAML.pm CPANTS is using.},
141             remedy => q{Upgrade your YAML generator so it produces valid YAML.},
142             code => sub {
143 11     11   90 my $d = shift;
144 11 50       59 !$d->{error}{meta_yml_is_parsable} ? 1 : 0
145             },
146             details => sub {
147 0     0   0 my $d = shift;
148 0         0 $d->{error}{meta_yml_is_parsable};
149             },
150             },
151             {
152             name => 'meta_json_is_parsable',
153             error => q{The META.json file of this distribution could not be parsed by the version of JSON parser CPANTS is using.},
154             remedy => q{Upgrade your META.json generator so it produces valid JSON.},
155             code => sub {
156 11     11   92 my $d = shift;
157 11 50       66 !$d->{error}{meta_json_is_parsable} ? 1 : 0
158             },
159             details => sub {
160 0     0   0 my $d = shift;
161 0         0 $d->{error}{meta_json_is_parsable};
162             },
163             },
164             {
165             name => 'meta_yml_has_provides',
166             is_experimental => 1,
167             error => q{This distribution does not have a list of provided modules defined in META.yml.},
168             remedy => q{Add all modules contained in this distribution to the META.yml field 'provides'. Module::Build or Dist::Zilla::Plugin::MetaProvides do this automatically for you.},
169             code => sub {
170 11     11   134 my $d = shift;
171 11 50       49 return 1 if !$d->{meta_yml};
172 11 50       43 return 1 if $d->{meta_yml}{provides};
173 11         31 return 0;
174             },
175             details => sub {
176 0     0   0 my $d = shift;
177 0 0       0 return "No META.yml." unless $d->{meta_yml};
178 0         0 return q{No "provides" was found in META.yml.};
179             },
180             },
181             {
182             name => 'meta_yml_conforms_to_known_spec',
183             error => q{META.yml does not conform to any recognised META.yml Spec.},
184             remedy => q{Take a look at the META.yml Spec at https://metacpan.org/pod/CPAN::Meta::History::Meta_1_4 (for version 1.4) or https://metacpan.org/pod/CPAN::Meta::Spec (for version 2), and change your META.yml accordingly.},
185             code => sub {
186 11     11   69 my $d = shift;
187 11 50       45 return 0 if $d->{error}{meta_yml_conforms_to_known_spec};
188 11         25 return 1;
189             },
190             details => sub {
191 0     0   0 my $d = shift;
192 0 0       0 return "No META.yml." unless $d->{meta_yml};
193 0 0       0 return "META.yml is broken." unless $d->{meta_yml_is_parsable};
194 0         0 return $d->{error}{meta_yml_conforms_to_known_spec};
195             },
196             },
197             {
198             name => 'meta_json_conforms_to_known_spec',
199             error => q{META.json does not conform to any recognised META Spec.},
200             remedy => q{Take a look at the META.json Spec at https://metacpan.org/pod/CPAN::Meta::History::Meta_1_4 (for version 1.4) or https://metacpan.org/pod/CPAN::Meta::Spec (for version 2), and change your META.json accordingly.},
201             code => sub {
202 11     11   72 my $d = shift;
203 11 50       41 return 0 if $d->{error}{meta_json_is_parsable};
204 11 50       38 return 0 if $d->{error}{meta_json_conforms_to_known_spec};
205 11         26 return 1;
206             },
207             details => sub {
208 0     0   0 my $d = shift;
209 0 0       0 return "META.json is broken." unless $d->{meta_json_is_parsable};
210 0         0 return $d->{error}{meta_json_conforms_to_known_spec};
211             },
212             },
213             {
214             name => 'meta_yml_declares_perl_version',
215             error => q{This distribution does not declare the minimum perl version in META.yml.},
216             is_extra => 1,
217             remedy => q{If you are using Build.PL define the {requires}{perl} = VERSION field. If you are using MakeMaker (Makefile.PL) you should upgrade ExtUtils::MakeMaker to 6.48 and use MIN_PERL_VERSION parameter. Perl::MinimumVersion can help you determine which version of Perl your module needs.},
218             code => sub {
219 11     11   76 my $d = shift;
220 11         30 my $yaml = $d->{meta_yml};
221 11 50       35 return 1 unless $yaml;
222 11 50 33     76 return ref $yaml->{requires} eq ref {} && $yaml->{requires}{perl} ? 1 : 0;
223             },
224             details => sub {
225 0     0   0 my $d = shift;
226 0         0 my $yaml = $d->{meta_yml};
227 0 0       0 return "No META.yml." unless $yaml;
228 0 0       0 return q{No "requires" was found in META.yml.} unless ref $yaml->{requires} eq ref {};
229 0 0       0 return q{No "perl" subkey was found in META.yml.} unless $yaml->{requires}{perl};
230             },
231             },
232             {
233             name => 'meta_yml_has_repository_resource',
234             is_experimental => 1,
235             error => q{This distribution does not have a link to a repository in META.yml.},
236             remedy => q{Add a 'repository' resource to the META.yml via 'meta_add' accessor (for Module::Build) or META_ADD parameter (for ExtUtils::MakeMaker).},
237             code => sub {
238 11     11   71 my $d = shift;
239 11         27 my $yaml = $d->{meta_yml};
240 11 50       31 return 1 unless $yaml;
241 11 50 33     80 return ref $yaml->{resources} eq ref {} && $yaml->{resources}{repository} ? 1 : 0;
242             },
243             details => sub {
244 0     0   0 my $d = shift;
245 0         0 my $yaml = $d->{meta_yml};
246 0 0       0 return "No META.yml." unless $yaml;
247 0 0       0 return q{No "resources" was found in META.yml.} unless ref $yaml->{resources} eq ref {};
248 0 0       0 return q{No "repository" subkey was found in META.yml.} unless $yaml->{resources}{repository};
249             },
250             },
251 8     8 1 491 ];
252             }
253              
254             q{Barbies Favourite record of the moment:
255             Nine Inch Nails: Year Zero};
256              
257             __END__