File Coverage

blib/lib/Module/CPANTS/Kwalitee/Uses.pm
Criterion Covered Total %
statement 121 144 84.0
branch 34 56 60.7
condition 10 20 50.0
subroutine 12 14 85.7
pod 3 3 100.0
total 180 237 75.9


line stmt bran cond sub pod time code
1             package Module::CPANTS::Kwalitee::Uses;
2 7     7   4035 use warnings;
  7         15  
  7         219  
3 7     7   38 use strict;
  7         13  
  7         149  
4 7     7   51 use File::Spec::Functions qw(catfile);
  7         12  
  7         316  
5 7     7   4481 use Perl::PrereqScanner::NotQuiteLite 0.9901;
  7         166926  
  7         304  
6 7     7   68 use List::Util 1.33 qw/none/;
  7         147  
  7         524  
7 7     7   48 use version;
  7         16  
  7         45  
8              
9             our $VERSION = '1.01';
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             perl5 perl5i::1 perl5i::2 perl5i::latest
33             Pegex::Base
34             Role::Tiny
35             strictures
36             );
37             # These modules require a flag to enforce strictness.
38             push @STRICT_WARNINGS_EQUIV, qw(
39             Mojo::Base
40             Spiffy
41             );
42              
43 42     42 1 105 sub order { 100 }
44              
45             ##################################################################
46             # Analyse
47             ##################################################################
48              
49             sub analyse {
50 11     11 1 35 my $class = shift;
51 11         25 my $me = shift;
52            
53 11         253 my $distdir = $me->distdir;
54 11         205 my $modules = $me->d->{modules};
55 11         209 my $files = $me->d->{files_hash};
56              
57             # NOTE: all files in xt/ should be ignored because they are
58             # for authors only and their dependencies may not be (and
59             # often are not) listed in meta files.
60 11         78 my @test_files = grep {m!^t\b.*\.t$!} sort keys %$files;
  19         109  
61 11         213 $me->d->{test_files} = \@test_files;
62              
63             my %test_modules = map {
64 0         0 my $m = my $f = $_;
65 0         0 $m =~ s|\.pm$||;
66 0         0 $m =~ s|/|::|g;
67 0         0 (my $m0 = $m) =~ s|^t::(?:lib::)?||;
68 0         0 ($m => $f, $m0 => $f)
69 11         116 } grep {m|^t\b.*\.pm$|} keys %$files;
  19         153  
70              
71 11         42 my %skip=map {$_->{module}=>1 } @$modules;
  9         82  
72              
73             # d->{versions} (from SiteKwalitee) knows inner packages as well
74 11 50       214 if (my $versions = $me->d->{versions}) {
75 0         0 for my $file (keys %$versions) {
76 0         0 for my $module (keys %{$versions->{$file}}) {
  0         0  
77 0         0 $skip{$module} = 1;
78             }
79             }
80             }
81              
82 11         79 my %uses;
83              
84 11         225 my $scanner = Perl::PrereqScanner::NotQuiteLite->new(
85             parsers => [':bundled'],
86             suggests => 1,
87             recommends => 1,
88             quick => 1,
89             );
90            
91             # modules
92 11         132224 my @module_files = map {$_->{file}} grep {!$_->{not_exists}} @$modules;
  9         57  
  9         42  
93              
94             # Makefile.PL runs other Makefile.PL files at configure time (except ones under t)
95             # Build.PL runs other *.PL files at build time
96 11 50       26 my @configure_files = grep {/(?:^Build|\bMakefile)\.PL$/ && !/^t[\\\/]/} @{$me->d->{files_array} || []};
  19 50       178  
  11         263  
97 11         51 my %configure_files_map = map {$_ => 1} @configure_files;
  0         0  
98              
99             # Other *.PL files (including lib/Build.PL) would (probably) be run at bulid time
100 11 50 33     19 my @build_files = grep {/\.PL$/ && !/^t[\\\/]/ && !$configure_files_map{$_}} @{$me->d->{files_array} || []};
  19 50       134  
  11         207  
101              
102 11         114 $uses{runtime} = $class->_scan($scanner, $files, $distdir, \@module_files);
103 11         80 $uses{configure} = $class->_scan($scanner, $files, $distdir, \@configure_files);
104 11         50 $uses{build} = $class->_scan($scanner, $files, $distdir, \@build_files);
105 11         42 $uses{test} = $class->_scan($scanner, $files, $distdir, \@test_files);
106              
107             # See also .pm files under t (only) if they are used in .t files
108 11         70 my $test_requirements = $uses{test}{requires}->as_string_hash;
109 11         160 my @test_pmfiles;
110 11         52 for my $module (keys %$test_requirements) {
111 0 0       0 push @test_pmfiles, $test_modules{$module} if $test_modules{$module};
112             }
113 11         40 my $additional_test_requirements = $class->_scan($scanner, $files, $distdir, \@test_pmfiles);
114 11         39 for my $relationship (keys %$additional_test_requirements) {
115             $uses{test}{$relationship} = ($uses{test}{$relationship})
116             ? $uses{test}{$relationship}->add_requirements($additional_test_requirements->{$relationship})
117 44 50       499 : $additional_test_requirements->{$relationship};
118             }
119              
120 11         121 for my $phase (keys %uses) {
121 44         64 for my $relationship (keys %{$uses{$phase}}) {
  44         116  
122 176         336 my $requirements = $uses{$phase}{$relationship}->as_string_hash;
123 176         1453 for my $requirement (keys %$requirements) {
124 2 0 33     30 if (
      33        
      33        
125             $skip{$requirement}
126             or $requirement =~ /^(?:inc|t)::/
127             or ($phase eq 'test' and $test_modules{$requirement})
128             ) {
129 0         0 delete $requirements->{$requirement};
130             }
131             }
132 176 100       282 if (%$requirements) {
133 2         11 $uses{$phase}{$relationship} = $requirements;
134             } else {
135 174         382 delete $uses{$phase}{$relationship};
136             }
137             }
138 44 100       71 delete $uses{$phase} unless %{$uses{$phase}};
  44         114  
139             }
140              
141 11         406 $me->d->{uses} = \%uses;
142 11         603 return;
143             }
144              
145             sub _scan {
146 55     55   146 my ($class, $scanner, $files_hash, $distdir, $files) = @_;
147              
148 55         179 my @methods = qw/requires recommends suggests noes/;
149 55         94 my %reqs = map {$_ => CPAN::Meta::Requirements->new} @methods;
  220         2239  
150 55         964 for my $file (@$files) {
151 9         66 my $ctx = $scanner->scan_file("$distdir/$file");
152              
153             # There may be broken files (intentionally, or unintentionally, esp in tests)
154 9 50       18866 if (@{$ctx->{errors} || []}) {
  9 50       64  
155 0         0 my $error = join ',', @{$ctx->{errors}};
  0         0  
156 0         0 $error =~ s/ at \S+ line \d+[^\n]*//gs;
157 0         0 $error =~ s/Scan Error: //g;
158 0         0 $files_hash->{$file}{scan_error} = $error;
159             }
160              
161 9 50       29 if ($ctx->{perl6}) {
162 0         0 $files_hash->{$file}{perl6} = 1;
163 0         0 next;
164             }
165 9         27 for my $method (@methods) {
166 36         352 my $requirements = $ctx->$method;
167 36         550 my $hash = $requirements->as_string_hash;
168 36 100       887 next unless %$hash;
169 2         8 $files_hash->{$file}{$method} = $hash;
170 2         21 $reqs{$method} = $reqs{$method}->add_requirements($requirements);
171             }
172             }
173 55         282 return \%reqs;
174             }
175              
176             ##################################################################
177             # Kwalitee Indicators
178             ##################################################################
179              
180             sub kwalitee_indicators {
181             return [
182             {
183             name => 'use_strict',
184             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.},
185             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.},
186             ignorable => 1,
187             code => sub {
188 11     11   80 my $d = shift;
189 11   50     36 my $files = $d->{files_hash} || {};
190              
191 11         227 my $perl_version_with_implicit_stricture = version->new('5.011')->numify;
192 11         46 my @no_strict;
193              
194 11         50 for my $file (keys %$files) {
195 19 100       74 next unless exists $files->{$file}{module};
196 9 50       28 next if $files->{$file}{unreadable};
197 9 50       25 next if $files->{$file}{perl6};
198 9 50       30 next if $file =~ /\.pod$/;
199 9         22 my $module = $files->{$file}{module};
200 9   100     62 my $requires = $files->{$file}{requires} || {};
201 9         24 my $required_perl = $requires->{perl};
202 9 100       25 if (defined $required_perl) {
203 2         6 $required_perl =~ s/_//; # tweak 5.008_001 and the likes for silence
204 2 50       33 next if version->parse($required_perl)->numify >= $perl_version_with_implicit_stricture;
205             }
206              
207             # There are lots of acceptable strict alternatives
208 7 50       99 push @no_strict, $module if none {exists $requires->{$_}} (@STRICT_EQUIV, @STRICT_WARNINGS_EQUIV);
  210         298  
209             }
210 11 100       36 if (@no_strict) {
211 7         32 $d->{error}{use_strict} = join ", ", sort @no_strict;
212 7         27 return 0;
213             }
214 4         17 return 1;
215             },
216             details => sub {
217 0     0   0 my $d = shift;
218 0         0 return "The following modules don't use strict (or equivalents): " . $d->{error}{use_strict};
219             },
220             },
221             {
222             name => 'use_warnings',
223             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.},
224             is_extra => 1,
225             ignorable => 1,
226             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.},
227             code => sub {
228 11     11   68 my $d = shift;
229 11   50     39 my $files = $d->{files_hash} || {};
230              
231 11         20 my @no_warnings;
232 11         35 for my $file (keys %$files) {
233 19 100       66 next unless exists $files->{$file}{module};
234 9 50       43 next if $files->{$file}{unreadable};
235 9 50       26 next if $files->{$file}{perl6};
236 9 50       33 next if $file =~ /\.pod$/;
237 9         19 my $module = $files->{$file}{module};
238 9   100     58 my $requires = $files->{$file}{requires} || {};
239 9 50       97 push @no_warnings, $module if none {exists $requires->{$_}} (@WARNINGS_EQUIV, @STRICT_WARNINGS_EQUIV);
  279         390  
240             }
241 11 100       35 if (@no_warnings) {
242 9         51 $d->{error}{use_warnings} = join ", ", sort @no_warnings;
243 9         34 return 0;
244             }
245 2         12 return 1;
246             },
247             details => sub {
248 0     0   0 my $d = shift;
249 0         0 return "The following modules don't use warnings (or equivalents): " . $d->{error}{use_warnings};
250             },
251             },
252 8     8 1 190 ];
253             }
254              
255              
256             q{Favourite record of the moment:
257             Fat Freddys Drop: Based on a true story};
258              
259             __END__