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   3193 use warnings;
  7         12  
  7         348  
3 7     7   26 use strict;
  7         11  
  7         133  
4 7     7   24 use File::Spec::Functions qw/catfile/;
  7         21  
  7         405  
5 7     7   28 use Encode;
  7         13  
  7         659  
6 7     7   2675 use Data::Binary qw/is_binary/;
  7         3374  
  7         8677  
7              
8             our $VERSION = '1.03';
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 63 sub order { 100 }
19              
20             ##################################################################
21             # Analyse
22             ##################################################################
23              
24             sub analyse {
25 12     12 1 34 my ($class, $me) = @_;
26 12         153 my $distdir = $me->distdir;
27 12         41 my @errors;
28              
29 12 100       19 my @files = map { $_->{file} } @{$me->d->{modules} || []};
  9         79  
  12         132  
30              
31 12 50       65 for my $file (@{$me->d->{files_array} || []}) {
  12         168  
32             # sometimes pod for .pm file is put into .pod
33             # and scripts may have abstract
34 20 100 0     263 if (
      33        
      66        
35             ( $file =~ /\.pod$/ && ($file =~ m!^lib/! or $file =~ m!^[^/]+$!) )
36             or $file =~ m!^(?:bin|scripts?)/!
37             ) {
38 1         9 push @files, $file;
39             }
40             }
41              
42 12         23 for my $file (@files) {
43 10         18 local $@;
44 10         91 my ($package, $abstract, $error, $has_binary_data) = $class->_parse_abstract(catfile($distdir, $file));
45 10 100       34 push @errors, "$error ($package)" if $error;
46 10 100       72 $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       73 $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   21 my ($class, $file) = @_;
55 10         16 my ($package, $abstract);
56 10         21 my $inpod = 0;
57 10 50       387 open my $fh, '<', $file or return;
58 10         21 my $directive;
59             my $encoding;
60 10         26 my $package_name_pattern = '(?:[A-Za-z0-9_]+::)*[A-Za-z0-9_]+ | [BCIF] < (?:[A-Za-z0-9_]+::)*[A-Za-z0-9_]+ >';
61 10 100       66 if ( $file !~ /\.p(?:m|od)$/ ) {
62 1         8 $package_name_pattern .= ' | [A-Za-z0-9_.-]+ | [BCIF] < [A-Za-z0-9_.-]+ >';
63             }
64 10         138 while(<$fh>) {
65 40 50       83 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       76 if (substr($_, 0, 1) eq '=') {
71 6 100       18 if (/^=encoding\s+(.+)/) {
72 2         7 $encoding = $1;
73             }
74 6 50       23 if (/^=cut/) {
    50          
75 0         0 $inpod = 0;
76             } elsif (/^=(?!cut)(.+)/) {
77 6         12 $directive = $1;
78 6         5 $inpod = 1;
79             }
80             }
81 40 100       122 next if !$inpod;
82 16 100       30 next unless $directive =~ /^head/;
83 8 100       284 if ( /^\s*(${package_name_pattern}) \s+ -+ (?:\s+ (.*)\s*$|$)/x ) {
84 2         7 ($package, $abstract) = ($1, $2);
85 2         4 $package =~ s![BCIF]<([^>]+)>!$1!;
86 2         5 next;
87             }
88 6 100       20 next unless $abstract;
89 2 50 33     13 last if /^\s*$/ || /^=/;
90 0         0 s/\s+$//s;
91 0         0 $abstract .= "\n$_";
92             }
93              
94 10         14 my $error;
95 10 100 66     34 if ($encoding && $abstract) {
96 2         17 my $encoder = find_encoding($encoding);
97 2 50       828 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         137 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   64 my $d = shift;
122 12 100       34 return 0 if $d->{error}{has_abstract_in_pod};
123 10 0       17 my @abstracts = grep {defined $_ && length $_} values %{$d->{abstracts_in_pod} || {}};
  0 50       0  
  10         102  
124 10 50       31 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   53 my $d = shift;
138 12         33 my %mapping = map {$_ => 1} @ABSTRACT_STUBS;
  48         180  
139 12         24 my @errors;
140 12 100       22 for (sort keys %{$d->{abstracts_in_pod} || {}}) {
  12         94  
141 2 50 50     7 push @errors, $_ if $mapping{$d->{abstracts_in_pod}{$_} || ''};
142             }
143 12 50       29 if (@errors) {
144 0         0 $d->{error}{no_abstract_stub_in_pod} = join ',', @errors;
145             }
146 12 50       42 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 85 ];
155             }
156              
157              
158             q{Favourite record of the moment:
159             Fat Freddys Drop: Based on a true story};
160              
161             __END__