File Coverage

blib/lib/EPublisher/Source/Plugin/MetaCPAN.pm
Criterion Covered Total %
statement 83 104 79.8
branch 15 28 53.5
condition 1 3 33.3
subroutine 9 9 100.0
pod 1 1 100.0
total 109 145 75.1


line stmt bran cond sub pod time code
1             package EPublisher::Source::Plugin::MetaCPAN;
2              
3             # ABSTRACT: Get POD from distributions via MetaCPAN
4              
5 4     4   66696 use strict;
  4         7  
  4         160  
6 4     4   19 use warnings;
  4         6  
  4         117  
7              
8 4     4   3054 use Data::Dumper;
  4         35358  
  4         330  
9 4     4   2740 use Encode;
  4         37101  
  4         455  
10 4     4   33 use File::Basename;
  4         7  
  4         392  
11 4     4   2122 use MetaCPAN::API;
  4         432574  
  4         269  
12              
13 4     4   2308 use EPublisher::Source::Base;
  4         1073  
  4         151  
14 4     4   1913 use EPublisher::Utils::PPI qw(extract_pod_from_code);
  4         431594  
  4         3604  
15              
16             our @ISA = qw( EPublisher::Source::Base );
17              
18             our $VERSION = 0.24;
19              
20             # implementing the interface to EPublisher::Source::Base
21             sub load_source{
22 3     3 1 1051 my ($self) = @_;
23              
24 3         8 $self->publisher->debug( '100: start ' . __PACKAGE__ );
25              
26 3         42 my $options = $self->_config;
27            
28 3 50       19 return '' unless $options->{module};
29              
30 3         5 my $module = $options->{module}; # the name of the CPAN-module
31 3         6 my $dont_merge_release = $options->{onlythis};
32 3         58 my $mcpan = MetaCPAN::API->new;
33              
34             # metacpan does not handle ".pm" in dist names
35 3         250 my $release_name_metacpan = $module;
36 3         7 $release_name_metacpan =~ s/\.pm\z//;
37              
38             # fetching the requested module from metacpan
39 3         14 $self->publisher->debug( "103: fetch release $module ($release_name_metacpan)" );
40              
41             # if just the one and only POD from the modules name and not the entire
42             # release is wanted, we just fetch it and return
43 3 50       31 if ($dont_merge_release) {
44              
45 0         0 my $result;
46              
47             eval {
48 0         0 $result = $mcpan->pod(
49             module => $release_name_metacpan,
50             'content-type' => 'text/x-pod',
51             );
52 0         0 1;
53 0 0       0 } or do {
54 0         0 $self->publisher->debug(
55             "103: Can't retrieve pod for $release_name_metacpan"
56             );
57 0         0 return;
58             };
59              
60 0         0 my @pod = ();
61 0         0 my $info = { pod => $result, filename => '', title => $module };
62 0         0 push (@pod, $info);
63              
64             # EXIT!
65 0         0 return @pod;
66             }
67             # ELSE we go on and build the entire release...
68              
69             # if there is a wrong module-name we write a debug-message and return
70             # an empty array
71 3         5 my $module_result;
72             eval {
73 3         14 $module_result =
74             $mcpan->fetch( 'release/' . $release_name_metacpan );
75 2         160129 1;
76 3 100       4 } or do {
77 1         72745 $self->publisher->debug(
78             "103: release $release_name_metacpan does not exist"
79             );
80 1         189 return;
81             };
82              
83             # if we reached here the module-call was probably fine...
84             # so we print out what we have got
85 2         14 $self->publisher->debug(
86             "103: fetch result: " . Dumper $module_result
87             );
88              
89             # get the manifest with module-author and modulename-moduleversion
90 2         1574 $self->publisher->debug( '103: get MANIFEST' );
91 2         19 my $manifest;
92             eval {
93 2         15 $manifest = $mcpan->source(
94             author => $module_result->{author},
95             release => $module_result->{name},
96             path => 'MANIFEST',
97             );
98 2 50       4 } or do {
99 0         0 $self->publisher->debug(
100             "103: Cannot get MANIFEST",
101             );
102 0         0 return;
103             };
104              
105             # make a list from all possible POD-files in the lib directory
106 2         25783 my @files = split /\n/, $manifest;
107              
108             #$self->publisher->debug( "103: files from manifest: " . join ', ', @files );
109              
110             # some MANIFESTS (like POD::Parser) have comments after the filenames,
111             # so we match against an optional \s instead of \z
112             # the manifest, in POD::Parser in looks e.g. like this:
113             #
114             # lib/Pod/Usage.pm -- The Pod::Usage module source
115             # lib/Pod/Checker.pm -- The Pod::Checker module source
116             # lib/Pod/Find.pm -- The Pod::Find module source
117 93 100       318 my @pod_files = grep{
118 2         8 /^.*\.p(?:od|m|l)(?:\s|$)/ # all POD everywhere
119             and not
120             /^(?:example\/|x?t\/|inc\/)/ # but not in example/ or t/ or xt/ or inc/
121             }@files;
122              
123             # especially in App::* dists the most important documentation
124             # is often in the scripts
125 93 50       170 push @pod_files, grep {
126 2         7 /^bin\//
127             and not
128             /^.*\.p(?:od|m|l)(?:\s|$)/
129             }@files;
130              
131             # here whe store POD if we find some later on
132 2         5 my @pod;
133              
134             # look for POD
135 2         6 for my $file ( @pod_files ) {
136              
137             # we match the filename again, in case there are comments in
138             # the manifest, in POD::Parser in looks e.g. like this:
139             #
140             # lib/Pod/Usage.pm -- The Pod::Usage module source
141             # lib/Pod/Checker.pm -- The Pod::Checker module source
142             # lib/Pod/Find.pm -- The Pod::Find module source
143              
144 15         252 my ($path) = split /\s/, $file;
145 15 0 33     96 next if $path !~ m{ \. p(?:od|m|l) \z }x && $path !~ m{ \A bin/ }x;
146              
147 15         26 $file = $path;
148              
149             # the call below ($mcpan->pod()) fails if there is no POD in a
150             # module so this is why I filter all the modules. I check if they
151             # have any line BEGINNING with '=head1' ore similar
152 15         24 my $source;
153             eval {
154 15         141 $source = $mcpan->source(
155             author => $module_result->{author},
156             release => $module_result->{name},
157             path => $file,
158             );
159 15         208084 1;
160 15 50       51 } or do {
161 0         0 $self->publisher->debug(
162             "103: Cannot get source for $file",
163             );
164 0         0 return;
165             };
166              
167 15         100 $self->publisher->debug( "103: source of $file found" );
168              
169             # The Moose-Project made me write this filtering Regex, because
170             # they have .pm's without POD, and also with nonsense POD which
171             # still fails if you call $mcpan->pod
172 15         214 my $pod_src;
173 15 100       208 if ( $source =~ m{ ^=head[1234] }xim ) {
174              
175             eval {
176 13         109 $pod_src = $mcpan->pod(
177             author => $module_result->{author},
178             release => $module_result->{name},
179             path => $file,
180             'content-type' => 'text/x-pod',
181             );
182              
183 13         291968 1;
184 13 50       26 } or do{ $self->publisher->debug( $@ ); next; };
  0         0  
  0         0  
185              
186 13 50       56 if (!$pod_src) {
187 0         0 $self->publisher->debug( "103: empty pod handle" );
188 0         0 next;
189             }
190              
191 13 50       64 if ( $pod_src =~ m/ \A ({.*) /xs ) {
192 0         0 $self->publisher->debug( "103: error message: $1" );
193 0         0 next;
194             }
195             else {
196 13         89 $self->publisher->debug( "103: got pod" );
197             }
198              
199             # metacpan always provides utf-8 encoded data, so we have to decode it
200             # otherwise the target plugins may produce garbage
201 13         267 $pod_src = decode( 'utf-8', $pod_src );
202              
203             }
204             else {
205             # if there is no head we consider this POD unvalid
206 2         6 next;
207             }
208            
209             # check if $result is always only the Pod
210             #push @pod, extract_pod_from_code( $result );
211 13         2574 my $filename = basename $file;
212 13         26 my $title = $file;
213              
214 13         82 $title =~ s{^(?:lib|bin)/}{};
215 13         74 $title =~ s{\.p(?:m|od|l)\z}{};
216 13         46 $title =~ s{/}{::}g;
217            
218 13         79 my $info = { pod => $pod_src, filename => $filename, title => $title };
219 13         35 push @pod, $info;
220              
221             # make some nice debug output for what is in $info
222 13         18 my $pod_short;
223 13 50       66 if ($pod_src =~ m/(.{50})/s) {
224 13         52 $pod_short = $1 . '[...]';
225             }
226             else {
227 0         0 $pod_short = $pod_src;
228             }
229 13         60 $self->publisher->debug(
230             "103: passed info: "
231             . "filename => $filename, "
232             . "title => $title, "
233             . "pod => $pod_short"
234             );
235             }
236              
237             # voila
238 2         444 return @pod;
239             }
240              
241             1;
242              
243             __END__