File Coverage

blib/lib/Module/CPANTS/Kwalitee/Pod.pm
Criterion Covered Total %
statement 78 97 80.4
branch 43 64 67.1
condition 7 17 41.1
subroutine 11 13 84.6
pod 3 3 100.0
total 142 194 73.2


line stmt bran cond sub pod time code
1             package Module::CPANTS::Kwalitee::Pod;
2 7     7   3887 use warnings;
  7         15  
  7         233  
3 7     7   37 use strict;
  7         18  
  7         156  
4 7     7   35 use File::Spec::Functions qw/catfile/;
  7         17  
  7         345  
5 7     7   73 use Encode;
  7         12  
  7         835  
6 7     7   3213 use Data::Binary qw/is_binary/;
  7         42029  
  7         9043  
7              
8             our $VERSION = '1.02';
9             $VERSION =~ s/_//; ## no critic
10              
11             our @ABSTRACT_STUBS = (
12             q{Perl extension for blah blah blah}, # h2xs
13             q{[One line description of module's purpose here]}, # Module::Starter etc
14             q{The great new}, # Module::Starter
15             q{It's new $module}, # Minilla
16             );
17              
18 42     42 1 135 sub order { 100 }
19              
20             ##################################################################
21             # Analyse
22             ##################################################################
23              
24             sub analyse {
25 12     12 1 57 my ($class, $me) = @_;
26 12         263 my $distdir = $me->distdir;
27 12         100 my @errors;
28              
29 12 100       34 my @files = map { $_->{file} } @{$me->d->{modules} || []};
  9         151  
  12         232  
30              
31 12 50       93 for my $file (@{$me->d->{files_array} || []}) {
  12         245  
32             # sometimes pod for .pm file is put into .pod
33             # and scripts may have abstract
34 20 100 0     383 if (
      33        
      66        
35             ( $file =~ /\.pod$/ && ($file =~ m!^lib/! or $file =~ m!^[^/]+$!) )
36             or $file =~ m!^(?:bin|scripts?)/!
37             ) {
38 1         12 push @files, $file;
39             }
40             }
41              
42 12         48 for my $file (@files) {
43 10         24 local $@;
44 10         119 my ($package, $abstract, $error, $has_binary_data) = $class->_parse_abstract(catfile($distdir, $file));
45 10 100       55 push @errors, "$error ($package)" if $error;
46 10 100       84 $me->d->{abstracts_in_pod}{$package} = $abstract if $package;
47 10 50       60 $me->d->{files_hash}{$file}{has_binary_data} = 1 if $has_binary_data;
48             }
49 12 100       105 $me->d->{error}{has_abstract_in_pod} = join ';', @errors if @errors;
50             }
51              
52             # adapted from ExtUtils::MM_Unix and Module::Build::PodParser
53             sub _parse_abstract {
54 10     10   44 my ($class, $file) = @_;
55 10         24 my ($package, $abstract);
56 10         20 my $inpod = 0;
57 10 50       477 open my $fh, '<', $file or return;
58 10         46 my $directive;
59             my $encoding;
60 10         49 my $package_name_pattern = '(?:[A-Za-z0-9_]+::)*[A-Za-z0-9_]+ | [BCIF] < (?:[A-Za-z0-9_]+::)*[A-Za-z0-9_]+ >';
61 10 100       121 if ( $file !~ /\.p(?:m|od)$/ ) {
62 1         10 $package_name_pattern .= ' | [A-Za-z0-9_.-]+ | [BCIF] < [A-Za-z0-9_.-]+ >';
63             }
64 10         209 while(<$fh>) {
65 40 50       127 if (/^\s*__DATA__\s*$/) {
66 0         0 my $copy = $_ = <$fh>;
67 0 0       0 last unless defined $copy;
68 0 0       0 return (undef, undef, undef, 1) if is_binary($copy);
69             }
70 40 100       136 if (substr($_, 0, 1) eq '=') {
71 6 100       26 if (/^=encoding\s+(.+)/) {
72 2         6 $encoding = $1;
73             }
74 6 50       40 if (/^=cut/) {
    50          
75 0         0 $inpod = 0;
76             } elsif (/^=(?!cut)(.+)/) {
77 6         17 $directive = $1;
78 6         9 $inpod = 1;
79             }
80             }
81 40 100       181 next if !$inpod;
82 16 100       52 next unless $directive =~ /^head/;
83 8 100       205 if ( /^\s*(${package_name_pattern}) \s+ -+ (?:\s+ (.*)\s*$|$)/x ) {
84 2         9 ($package, $abstract) = ($1, $2);
85 2         5 $package =~ s![BCIF]<([^>]+)>!$1!;
86 2         7 next;
87             }
88 6 100       27 next unless $abstract;
89 2 50 33     24 last if /^\s*$/ || /^=/;
90 0         0 s/\s+$//s;
91 0         0 $abstract .= "\n$_";
92             }
93              
94 10         60 my $error;
95 10 100 66     74 if ($encoding && $abstract) {
96 2         13 my $encoder = find_encoding($encoding);
97 2 50       1084 if (!$encoder) {
98 2         7 $error = "unknown encoding: $encoding";
99             } else {
100 0         0 $abstract = eval { $encoder->decode($abstract) };
  0         0  
101 0 0       0 if ($@) {
102 0         0 $error = $@;
103 0         0 $error =~ s|\s*at .+ line \d+.+$||s;
104             }
105             }
106             }
107 10         194 return ($package, $abstract, $error);
108             }
109              
110             ##################################################################
111             # Kwalitee Indicators
112             ##################################################################
113              
114             sub kwalitee_indicators {
115             return [
116             {
117             name => 'has_abstract_in_pod',
118             error => q{No abstract (short description of a module) is found in pod from this distribution.},
119             remedy => q{Provide a short description in the NAME section of the pod (after the module name followed by a hyphen) at least for the main module of this distribution.},
120             code => sub {
121 12     12   107 my $d = shift;
122 12 100       49 return 0 if $d->{error}{has_abstract_in_pod};
123 10 0       20 my @abstracts = grep {defined $_ && length $_} values %{$d->{abstracts_in_pod} || {}};
  0 50       0  
  10         133  
124 10 50       54 return @abstracts ? 1 : 0;
125             },
126             details => sub {
127 0     0   0 my $d = shift;
128 0         0 return "No abstracts in pod";
129             },
130             },
131             {
132             name => 'no_abstract_stub_in_pod',
133             is_extra => 1,
134             error => q{A well-known abstract stub (typically generated by an authoring tool) is found in this distribution.},
135             remedy => q{Modify the stub. You might need to modify other stubs (for name, synopsis, license, etc) as well.},
136             code => sub {
137 12     12   70 my $d = shift;
138 12         43 my %mapping = map {$_ => 1} @ABSTRACT_STUBS;
  48         320  
139 12         41 my @errors;
140 12 100       39 for (sort keys %{$d->{abstracts_in_pod} || {}}) {
  12         128  
141 2 50 50     9 push @errors, $_ if $mapping{$d->{abstracts_in_pod}{$_} || ''};
142             }
143 12 50       66 if (@errors) {
144 0         0 $d->{error}{no_abstract_stub_in_pod} = join ',', @errors;
145             }
146 12 50       75 return @errors ? 0 : 1;
147             },
148             details => sub {
149 0     0   0 my $d = shift;
150 0         0 my %mapping = map {$_ => 1} @ABSTRACT_STUBS;
  0         0  
151 0         0 return "Abstracts in the following packages are stubs:". $d->{error}{no_abstract_stub_in_pod};
152             },
153             },
154 8     8 1 253 ];
155             }
156              
157              
158             q{Favourite record of the moment:
159             Fat Freddys Drop: Based on a true story};
160              
161             __END__