File Coverage

blib/lib/Module/CPANTS/Kwalitee/Uses.pm
Criterion Covered Total %
statement 126 149 84.5
branch 37 60 61.6
condition 10 20 50.0
subroutine 12 14 85.7
pod 3 3 100.0
total 188 246 76.4


line stmt bran cond sub pod time code
1             package Module::CPANTS::Kwalitee::Uses;
2 7     7   3882 use warnings;
  7         16  
  7         338  
3 7     7   24 use strict;
  7         12  
  7         149  
4 7     7   20 use File::Spec::Functions qw(catfile);
  7         10  
  7         354  
5 7     7   4708 use Perl::PrereqScanner::NotQuiteLite 0.9901;
  7         171347  
  7         363  
6 7     7   59 use List::Util 1.33 qw/none/;
  7         148  
  7         564  
7 7     7   46 use version;
  7         10  
  7         115  
8              
9             our $VERSION = '1.03';
10             $VERSION =~ s/_//; ## no critic
11              
12             # These equivalents should be reasonably well-known and, preferably,
13             # well-documented. Don't add obscure modules used by only one person
14             # or a few people, to keep the list relatively small and to encourage
15             # people to use a better equivalent.
16             # "use_(strict|warnings)" should fail if someone feels the need
17             # to add "use $1;" in the modules.
18             our @STRICT_EQUIV = qw( strict );
19             our @WARNINGS_EQUIV = qw( warnings warnings::compat );
20             our @STRICT_WARNINGS_EQUIV = qw(
21             common::sense
22             Any::Moose
23             Catmandu::Sane Coat
24             Dancer
25             Mo Mu
26             Modern::Perl
27             Moo Moo::Role
28             Moose Moose::Role Moose::Exporter
29             Moose::Util::TypeConstraints Moose::Util::MetaRole
30             MooseX::Declare MooseX::Role::Parameterized MooseX::Types
31             Mouse Mouse::Role
32             Object::Pad
33             perl5 perl5i::1 perl5i::2 perl5i::latest
34             Pegex::Base
35             Role::Tiny
36             strictures
37             );
38             # These modules require a flag to enforce strictness.
39             push @STRICT_WARNINGS_EQUIV, qw(
40             Mojo::Base
41             Spiffy
42             );
43              
44 28     28 1 79 sub order { 100 }
45              
46             ##################################################################
47             # Analyse
48             ##################################################################
49              
50             sub analyse {
51 12     12 1 30 my $class = shift;
52 12         27 my $me = shift;
53            
54 12         179 my $distdir = $me->distdir;
55 12         197 my $modules = $me->d->{modules};
56 12         163 my $files = $me->d->{files_hash};
57              
58             # NOTE: all files in xt/ should be ignored because they are
59             # for authors only and their dependencies may not be (and
60             # often are not) listed in meta files.
61 12         70 my @test_files = grep {m!^t\b.*\.t$!} sort keys %$files;
  20         77  
62 12         166 $me->d->{test_files} = \@test_files;
63              
64             my %test_modules = map {
65 0         0 my $m = my $f = $_;
66 0         0 $m =~ s|\.pm$||;
67 0         0 $m =~ s|/|::|g;
68 0         0 (my $m0 = $m) =~ s|^t::(?:lib::)?||;
69 0         0 ($m => $f, $m0 => $f)
70 12         76 } grep {m|^t\b.*\.pm$|} keys %$files;
  20         113  
71              
72 12         28 my %skip=map {$_->{module}=>1 } @$modules;
  9         57  
73              
74             # d->{versions} (from SiteKwalitee) knows inner packages as well
75 12 50       211 if (my $versions = $me->d->{versions}) {
76 0         0 for my $file (keys %$versions) {
77 0         0 for my $module (keys %{$versions->{$file}}) {
  0         0  
78 0         0 $skip{$module} = 1;
79             }
80             }
81             }
82              
83 12         143 my %uses;
84              
85 12         184 my $scanner = Perl::PrereqScanner::NotQuiteLite->new(
86             parsers => [':bundled'],
87             suggests => 1,
88             recommends => 1,
89             quick => 1,
90             );
91            
92             # modules
93 12         143002 my @module_files = map {$_->{file}} grep {!$_->{not_exists}} @$modules;
  9         37  
  9         38  
94              
95             # Makefile.PL runs other Makefile.PL files at configure time (except ones under t)
96             # Build.PL runs other *.PL files at build time
97 12 50       28 my @configure_files = grep {/(?:^Build|\bMakefile)\.PL$/ && !/^t[\\\/]/} @{$me->d->{files_array} || []};
  20 50       214  
  12         319  
98 12         49 my %configure_files_map = map {$_ => 1} @configure_files;
  0         0  
99              
100             # Other *.PL files (including lib/Build.PL) would (probably) be run at bulid time
101 12 50 33     18 my @build_files = grep {/\.PL$/ && !/^t[\\\/]/ && !$configure_files_map{$_}} @{$me->d->{files_array} || []};
  20 50       141  
  12         189  
102              
103 12         103 $uses{runtime} = $class->_scan($scanner, $files, $distdir, \@module_files);
104 12         75 $uses{configure} = $class->_scan($scanner, $files, $distdir, \@configure_files);
105 12         40 $uses{build} = $class->_scan($scanner, $files, $distdir, \@build_files);
106 12         36 $uses{test} = $class->_scan($scanner, $files, $distdir, \@test_files);
107              
108             # See also .pm files under t (only) if they are used in .t files
109 12         56 my $test_requirements = $uses{test}{requires}->as_string_hash;
110 12         142 my @test_pmfiles;
111 12         39 for my $module (keys %$test_requirements) {
112 0 0       0 push @test_pmfiles, $test_modules{$module} if $test_modules{$module};
113             }
114 12         40 my $additional_test_requirements = $class->_scan($scanner, $files, $distdir, \@test_pmfiles);
115 12         45 for my $relationship (keys %$additional_test_requirements) {
116             $uses{test}{$relationship} = ($uses{test}{$relationship})
117             ? $uses{test}{$relationship}->add_requirements($additional_test_requirements->{$relationship})
118 48 50       383 : $additional_test_requirements->{$relationship};
119             }
120              
121 12         91 for my $phase (keys %uses) {
122 48         56 for my $relationship (keys %{$uses{$phase}}) {
  48         88  
123 192         291 my $requirements = $uses{$phase}{$relationship}->as_string_hash;
124 192         1085 for my $requirement (keys %$requirements) {
125 2 0 33     31 if (
      33        
      33        
126             $skip{$requirement}
127             or $requirement =~ /^(?:inc|t)::/
128             or ($phase eq 'test' and $test_modules{$requirement})
129             ) {
130 0         0 delete $requirements->{$requirement};
131             }
132             }
133 192 100       222 if (%$requirements) {
134 2         8 $uses{$phase}{$relationship} = $requirements;
135             } else {
136 190         331 delete $uses{$phase}{$relationship};
137             }
138             }
139 48 100       59 delete $uses{$phase} unless %{$uses{$phase}};
  48         193  
140             }
141              
142 12         312 $me->d->{uses} = \%uses;
143 12         756 return;
144             }
145              
146             sub _scan {
147 60     60   108 my ($class, $scanner, $files_hash, $distdir, $files) = @_;
148              
149 60         161 my @methods = qw/requires recommends suggests noes/;
150 60         82 my %reqs = map {$_ => CPAN::Meta::Requirements->new} @methods;
  240         1886  
151 60         598 for my $file (@$files) {
152 9         63 my $ctx = $scanner->scan_file("$distdir/$file");
153              
154             # There may be broken files (intentionally, or unintentionally, esp in tests)
155 9 50       15524 if (@{$ctx->{errors} || []}) {
  9 50       45  
156 0         0 my $error = join ',', @{$ctx->{errors}};
  0         0  
157 0         0 $error =~ s/ at \S+ line \d+[^\n]*//gs;
158 0         0 $error =~ s/Scan Error: //g;
159 0         0 $files_hash->{$file}{scan_error} = $error;
160             }
161              
162 9 50       36 if ($ctx->{perl6}) {
163 0         0 $files_hash->{$file}{perl6} = 1;
164 0         0 next;
165             }
166 9         25 for my $method (@methods) {
167 36         358 my $requirements = $ctx->$method;
168 36         406 my $hash = $requirements->as_string_hash;
169 36 100       836 next unless %$hash;
170 2         9 $files_hash->{$file}{$method} = $hash;
171 2         7 $reqs{$method} = $reqs{$method}->add_requirements($requirements);
172             }
173             }
174 60         259 return \%reqs;
175             }
176              
177             ##################################################################
178             # Kwalitee Indicators
179             ##################################################################
180              
181             sub kwalitee_indicators {
182             return [
183             {
184             name => 'use_strict',
185             error => q{This distribution does not 'use strict;' (or its equivalents) in all of its modules. Note that this is not about the actual strictness of the modules. It's bad if nobody can tell whether the modules are strictly written or not, without reading the source code of your favorite clever module that actually enforces strictness. In other words, it's bad if someone feels the need to add 'use strict' to your modules.},
186             remedy => q{Add 'use strict' (or its equivalents) to all modules, or convince us that your favorite module is well-known enough and people can easily see the modules are strictly written.},
187             ignorable => 1,
188             code => sub {
189 12     12   74 my $d = shift;
190 12   50     39 my $files = $d->{files_hash} || {};
191              
192 12         263 my $perl_version_with_implicit_stricture = version->new('5.011')->numify;
193 12         41 my @no_strict;
194              
195 12         32 for my $file (keys %$files) {
196 20 100       66 next unless exists $files->{$file}{module};
197 9 50       27 next if $files->{$file}{unreadable};
198 9 50       24 next if $files->{$file}{perl6};
199 9 50       28 next if $file =~ /\.pod$/;
200 9         24 my $module = $files->{$file}{module};
201 9   100     41 my $requires = $files->{$file}{requires} || {};
202 9         24 my $required_perl = $requires->{perl};
203 9 100       20 if (defined $required_perl) {
204 2         3 $required_perl =~ s/_//; # tweak 5.008_001 and the likes for silence
205 2 50       49 next if version->parse($required_perl)->numify >= $perl_version_with_implicit_stricture;
206             }
207              
208             # There are lots of acceptable strict alternatives
209 7 50       101 push @no_strict, $module if none {exists $requires->{$_}} (@STRICT_EQUIV, @STRICT_WARNINGS_EQUIV);
  217         242  
210             }
211 12 100       42 if (@no_strict) {
212 7         36 $d->{error}{use_strict} = join ", ", sort @no_strict;
213 7         19 return 0;
214             }
215 5         19 return 1;
216             },
217             details => sub {
218 0     0   0 my $d = shift;
219 0         0 return "The following modules don't use strict (or equivalents): " . $d->{error}{use_strict};
220             },
221             },
222             {
223             name => 'use_warnings',
224             error => q{This distribution does not 'use warnings;' (or its equivalents) in all of its modules. Note that this is not about that your modules actually warn when something bad happens. It's bad if nobody can tell if a module warns or not, without reading the source code of your favorite module that actually enforces warnings. In other words, it's bad if someone feels the need to add 'use warnings' to your modules.},
225             is_extra => 1,
226             ignorable => 1,
227             remedy => q{Add 'use warnings' (or its equivalents) to all modules, or convince us that your favorite module is well-known enough and people can easily see the modules warn when something bad happens.},
228             code => sub {
229 12     12   55 my $d = shift;
230 12   50     32 my $files = $d->{files_hash} || {};
231              
232 12         99 my $perl_version_with_implicit_use_warnings = version->new('5.036')->numify;
233 12         29 my @no_warnings;
234 12         28 for my $file (keys %$files) {
235 20 100       53 next unless exists $files->{$file}{module};
236 9 50       25 next if $files->{$file}{unreadable};
237 9 50       20 next if $files->{$file}{perl6};
238 9 50       26 next if $file =~ /\.pod$/;
239 9         25 my $module = $files->{$file}{module};
240 9   100     41 my $requires = $files->{$file}{requires} || {};
241 9         18 my $required_perl = $requires->{perl};
242 9 100       19 if (defined $required_perl) {
243 2         5 $required_perl =~ s/_//; # tweak 5.008_001 and the likes for silence
244 2 50       22 next if version->parse($required_perl)->numify >= $perl_version_with_implicit_use_warnings;
245             }
246 9 50       70 push @no_warnings, $module if none {exists $requires->{$_}} (@WARNINGS_EQUIV, @STRICT_WARNINGS_EQUIV);
  288         300  
247             }
248 12 100       24 if (@no_warnings) {
249 9         40 $d->{error}{use_warnings} = join ", ", sort @no_warnings;
250 9         21 return 0;
251             }
252 3         12 return 1;
253             },
254             details => sub {
255 0     0   0 my $d = shift;
256 0         0 return "The following modules don't use warnings (or equivalents): " . $d->{error}{use_warnings};
257             },
258             },
259 8     8 1 200 ];
260             }
261              
262              
263             q{Favourite record of the moment:
264             Fat Freddys Drop: Based on a true story};
265              
266             __END__