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   4176 use warnings;
  7         22  
  7         243  
3 7     7   41 use strict;
  7         13  
  7         171  
4 7     7   36 use File::Spec::Functions qw(catfile);
  7         24  
  7         360  
5 7     7   5784 use Perl::PrereqScanner::NotQuiteLite 0.9901;
  7         199043  
  7         333  
6 7     7   59 use List::Util 1.33 qw/none/;
  7         178  
  7         491  
7 7     7   60 use version;
  7         17  
  7         51  
8              
9             our $VERSION = '1.02';
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 42     42 1 110 sub order { 100 }
45              
46             ##################################################################
47             # Analyse
48             ##################################################################
49              
50             sub analyse {
51 12     12 1 35 my $class = shift;
52 12         22 my $me = shift;
53            
54 12         271 my $distdir = $me->distdir;
55 12         258 my $modules = $me->d->{modules};
56 12         275 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         104 my @test_files = grep {m!^t\b.*\.t$!} sort keys %$files;
  20         127  
62 12         268 $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         153 } grep {m|^t\b.*\.pm$|} keys %$files;
  20         212  
71              
72 12         55 my %skip=map {$_->{module}=>1 } @$modules;
  9         89  
73              
74             # d->{versions} (from SiteKwalitee) knows inner packages as well
75 12 50       289 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         113 my %uses;
84              
85 12         285 my $scanner = Perl::PrereqScanner::NotQuiteLite->new(
86             parsers => [':bundled'],
87             suggests => 1,
88             recommends => 1,
89             quick => 1,
90             );
91            
92             # modules
93 12         161773 my @module_files = map {$_->{file}} grep {!$_->{not_exists}} @$modules;
  9         48  
  9         49  
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       27 my @configure_files = grep {/(?:^Build|\bMakefile)\.PL$/ && !/^t[\\\/]/} @{$me->d->{files_array} || []};
  20 50       200  
  12         310  
98 12         74 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     29 my @build_files = grep {/\.PL$/ && !/^t[\\\/]/ && !$configure_files_map{$_}} @{$me->d->{files_array} || []};
  20 50       214  
  12         263  
102              
103 12         117 $uses{runtime} = $class->_scan($scanner, $files, $distdir, \@module_files);
104 12         94 $uses{configure} = $class->_scan($scanner, $files, $distdir, \@configure_files);
105 12         80 $uses{build} = $class->_scan($scanner, $files, $distdir, \@build_files);
106 12         61 $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         79 my $test_requirements = $uses{test}{requires}->as_string_hash;
110 12         227 my @test_pmfiles;
111 12         62 for my $module (keys %$test_requirements) {
112 0 0       0 push @test_pmfiles, $test_modules{$module} if $test_modules{$module};
113             }
114 12         45 my $additional_test_requirements = $class->_scan($scanner, $files, $distdir, \@test_pmfiles);
115 12         59 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       580 : $additional_test_requirements->{$relationship};
119             }
120              
121 12         161 for my $phase (keys %uses) {
122 48         88 for my $relationship (keys %{$uses{$phase}}) {
  48         138  
123 192         419 my $requirements = $uses{$phase}{$relationship}->as_string_hash;
124 192         1735 for my $requirement (keys %$requirements) {
125 2 0 33     46 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       320 if (%$requirements) {
134 2         9 $uses{$phase}{$relationship} = $requirements;
135             } else {
136 190         475 delete $uses{$phase}{$relationship};
137             }
138             }
139 48 100       78 delete $uses{$phase} unless %{$uses{$phase}};
  48         134  
140             }
141              
142 12         349 $me->d->{uses} = \%uses;
143 12         788 return;
144             }
145              
146             sub _scan {
147 60     60   151 my ($class, $scanner, $files_hash, $distdir, $files) = @_;
148              
149 60         250 my @methods = qw/requires recommends suggests noes/;
150 60         121 my %reqs = map {$_ => CPAN::Meta::Requirements->new} @methods;
  240         2727  
151 60         908 for my $file (@$files) {
152 9         90 my $ctx = $scanner->scan_file("$distdir/$file");
153              
154             # There may be broken files (intentionally, or unintentionally, esp in tests)
155 9 50       20959 if (@{$ctx->{errors} || []}) {
  9 50       70  
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       33 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         366 my $requirements = $ctx->$method;
168 36         621 my $hash = $requirements->as_string_hash;
169 36 100       966 next unless %$hash;
170 2         6 $files_hash->{$file}{$method} = $hash;
171 2         8 $reqs{$method} = $reqs{$method}->add_requirements($requirements);
172             }
173             }
174 60         382 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   82 my $d = shift;
190 12   50     68 my $files = $d->{files_hash} || {};
191              
192 12         310 my $perl_version_with_implicit_stricture = version->new('5.011')->numify;
193 12         55 my @no_strict;
194              
195 12         67 for my $file (keys %$files) {
196 20 100       72 next unless exists $files->{$file}{module};
197 9 50       28 next if $files->{$file}{unreadable};
198 9 50       28 next if $files->{$file}{perl6};
199 9 50       42 next if $file =~ /\.pod$/;
200 9         27 my $module = $files->{$file}{module};
201 9   100     55 my $requires = $files->{$file}{requires} || {};
202 9         25 my $required_perl = $requires->{perl};
203 9 100       31 if (defined $required_perl) {
204 2         5 $required_perl =~ s/_//; # tweak 5.008_001 and the likes for silence
205 2 50       31 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       105 push @no_strict, $module if none {exists $requires->{$_}} (@STRICT_EQUIV, @STRICT_WARNINGS_EQUIV);
  217         437  
210             }
211 12 100       56 if (@no_strict) {
212 7         43 $d->{error}{use_strict} = join ", ", sort @no_strict;
213 7         26 return 0;
214             }
215 5         63 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   96 my $d = shift;
230 12   50     60 my $files = $d->{files_hash} || {};
231              
232 12         211 my $perl_version_with_implicit_use_warnings = version->new('5.036')->numify;
233 12         41 my @no_warnings;
234 12         44 for my $file (keys %$files) {
235 20 100       61 next unless exists $files->{$file}{module};
236 9 50       32 next if $files->{$file}{unreadable};
237 9 50       34 next if $files->{$file}{perl6};
238 9 50       35 next if $file =~ /\.pod$/;
239 9         22 my $module = $files->{$file}{module};
240 9   100     46 my $requires = $files->{$file}{requires} || {};
241 9         22 my $required_perl = $requires->{perl};
242 9 100       27 if (defined $required_perl) {
243 2         4 $required_perl =~ s/_//; # tweak 5.008_001 and the likes for silence
244 2 50       23 next if version->parse($required_perl)->numify >= $perl_version_with_implicit_use_warnings;
245             }
246 9 50       84 push @no_warnings, $module if none {exists $requires->{$_}} (@WARNINGS_EQUIV, @STRICT_WARNINGS_EQUIV);
  288         480  
247             }
248 12 100       72 if (@no_warnings) {
249 9         61 $d->{error}{use_warnings} = join ", ", sort @no_warnings;
250 9         38 return 0;
251             }
252 3         14 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 202 ];
260             }
261              
262              
263             q{Favourite record of the moment:
264             Fat Freddys Drop: Based on a true story};
265              
266             __END__