File Coverage

blib/lib/CPAN/Documentation/HTML.pm
Criterion Covered Total %
statement 37 177 20.9
branch 0 36 0.0
condition n/a
subroutine 13 33 39.3
pod 0 9 0.0
total 50 255 19.6


line stmt bran cond sub pod time code
1             package CPAN::Documentation::HTML;
2             BEGIN {
3 1     1   49085 $CPAN::Documentation::HTML::AUTHORITY = 'cpan:GETTY';
4             }
5             {
6             $CPAN::Documentation::HTML::VERSION = '0.002';
7             }
8             # ABSTRACT: Generate files for documentations of CPAN Distributions or simple packages
9              
10 1     1   1024 use Moo;
  1         19400  
  1         7  
11 1     1   2050 use Cwd;
  1         7  
  1         74  
12 1     1   817 use Path::Class;
  1         61630  
  1         82  
13 1     1   1173 use JSON;
  1         13489  
  1         6  
14 1     1   140 use File::Copy;
  1         3  
  1         54  
15 1     1   636 use CPAN::Documentation::HTML::PodSimple;
  1         4  
  1         34  
16 1     1   688 use CPAN::Documentation::HTML::Entry;
  1         3  
  1         32  
17 1     1   1419 use Dist::Data;
  1         553321  
  1         41  
18 1     1   9 use File::Temp qw( tempdir );
  1         2  
  1         84  
19 1     1   1049 use HTML::Zoom;
  1         12129  
  1         37  
20 1     1   1213 use HTML::TreeBuilder;
  1         30330  
  1         11  
21 1     1   3960 use File::ShareDir::ProjectDistDir;
  1         22655  
  1         11  
22              
23             has root => (
24             is => 'ro',
25             lazy => 1,
26 0     0     builder => sub { dir(getcwd)->absolute->stringify },
27             );
28              
29             has html => (
30             is => 'ro',
31             lazy => 1,
32 0     0     builder => sub { dir(shift->root,'perldoc')->absolute->stringify },
33             );
34              
35             has url_prefix => (
36             is => 'ro',
37             lazy => 1,
38 0     0     builder => sub { '/perldoc/' },
39             );
40              
41 0     0     has assets => (
42             is => 'ro',
43             lazy => 1,
44             builder => sub {{
45             "default.css" => file(dist_dir('CPAN-Documentation-HTML'),'default.css'),
46             "default.png" => file(dist_dir('CPAN-Documentation-HTML'),'default.png'),
47             }},
48             );
49              
50             has template => (
51             is => 'ro',
52             lazy => 1,
53 0     0     builder => sub { file(dist_dir('CPAN-Documentation-HTML'),'default.html')->slurp },
54             );
55              
56             has cache_file => (
57             is => 'ro',
58             lazy => 1,
59 0     0     builder => sub { file(shift->root,'.cpandochtml.cache')->absolute->stringify },
60             );
61              
62             has _pod_simple_html => (
63             is => 'ro',
64             lazy => 1,
65 0     0     builder => sub { Pod::Simple::HTML->new },
66             );
67              
68             has _json => (
69             is => 'ro',
70             lazy => 1,
71             builder => sub {
72 0     0     my $json = JSON->new;
73 0           return $json;
74             }
75             );
76              
77             has cache => (
78             is => 'ro',
79             lazy => 1,
80             builder => sub {
81 0     0     my ( $self ) = @_;
82 0 0         if (-f $self->cache_file) {
83 0           my %cache = %{$self->_json->decode(file($self->cache_file)->slurp)};
  0            
84 0           for (keys %cache) {
85 0           $cache{$_} = CPAN::Documentation::HTML::Entry->new(
86             pod => $cache{$_}->{pod},
87             module => $cache{$_}->{module},
88             type => $cache{$_}->{type},
89             dist => $cache{$_}->{dist},
90             );
91             }
92 0           return \%cache;
93             } else {
94             {}
95 0           }
96             },
97             );
98              
99             sub save_cache {
100 0     0 0   my ( $self ) = @_;
101 0           my %cache = %{$self->cache};
  0            
102 0           for (keys %cache) {
103 0           $cache{$_} = {
104             pod => $cache{$_}->pod,
105             module => $cache{$_}->module,
106             type => $cache{$_}->type,
107             dist => $cache{$_}->dist,
108             };
109             }
110 0           file($self->cache_file)->spew($self->_json->encode(\%cache));
111             }
112              
113             sub replace_assets {
114 0     0 0   my ( $self, $zoom, $prefix ) = @_;
115 0 0         $prefix = '' unless $prefix;
116 0           for (keys %{$self->assets}) {
  0            
117 0           my $file = $_;
118 0           my $id_file = $file;
119 0           $id_file =~ s/\./-/g;
120 0           for (qw( src href )) {
121 0           $zoom = $zoom->select('#cdh-'.$_.'-'.$id_file)->add_to_attribute( $_ => $prefix.$file );
122             }
123             }
124 0           $zoom = $zoom->select('#cdh-index-link')->add_to_attribute( href => $prefix.'index.html' );
125 0           return $zoom;
126             }
127              
128             sub save_index {
129 0     0 0   my ( $self ) = @_;
130 0           my $target = file($self->html,'index.html');
131 0           my $zoom = HTML::Zoom->from_html($self->template);
132              
133 0           $zoom = $self->replace_assets($zoom);
134              
135 0           for (keys %{$self->assets}) {
  0            
136 0           copy($self->assets->{$_},file($self->html,$_));
137             }
138              
139 0           my @tm = ([1,'documentation'],[2,'scripts'],[0,'modules']);
140              
141 0           my %dists;
142              
143 0           for (sort { $a cmp $b } keys %{$self->cache}) {
  0            
  0            
144 0           my $entry = $self->cache->{$_};
145 0 0         $dists{$entry->dist} = {} unless defined $dists{$entry->dist};
146 0           for (@tm) {
147 0 0         if ($entry->type == $_->[0]) {
148 0 0         $dists{$entry->dist}->{$_->[1]} = [] unless defined $dists{$entry->dist}->{$_->[1]};
149 0           push @{$dists{$entry->dist}->{$_->[1]}}, $entry;
  0            
150             }
151             }
152             }
153              
154 0           $target->spew($zoom->select('.cdh-index-list')->repeat_content([ map {
155 0           my $dist = $_;
156             sub {
157 0     0     my $distzoom = $_;
158 0           my $entry_matrix = $dists{$dist};
159 0           $distzoom = $distzoom->select('.cdh-index-dist-name')->replace_content($dist);
160 0           for (@tm) {
161 0           my $typename = $_->[1];
162 0 0         if (defined $entry_matrix->{$typename}) {
163 0           my @entries = @{$entry_matrix->{$typename}};
  0            
164 0           $distzoom = $distzoom
165             ->select('.cdh-index-dist-'.$typename.'-list')
166             ->repeat_content([ map {
167 0           my $entry = $_;
168 0 0         return unless $entry->pod;
169             sub {
170 0           $_->select('.cdh-index-entry')
171             ->add_to_attribute( href => './'.$entry->module.'/index.html' )
172             ->then
173             ->replace_content($entry->module)
174             }
175 0           } @entries ]);
176             } else {
177 0           $distzoom = $distzoom->select('.cdh-index-dist-'.$typename)->replace('');
178             }
179             }
180 0           return $distzoom;
181 0           };
182 0           } sort { $a cmp $b } keys %dists ])->to_html);
183             }
184              
185             sub BUILD {
186 0     0 0   my ( $self ) = @_;
187 0 0         die __PACKAGE__." Directory ".$self->root." does not exist" unless -d $self->root;
188             }
189              
190             sub add_dist {
191 0     0 0   my ( $self, $distfile ) = @_;
192 0           my $distdir = tempdir;
193 0           my $distdata = Dist::Data->new( filename => $distfile, dir => $distdir );
194 0           $distdata->extract_distribution;
195 0           my $dist = $distdata->name;
196 0 0         if (-d dir($distdir,'lib')) {
197 0           $self->add_lib($dist, dir($distdir,'lib'));
198             }
199 0 0         if (-d dir($distdir,'bin')) {
200 0           $self->add_bin($dist, dir($distdir,'bin'));
201             }
202 0 0         if (-d dir($distdir,'script')) {
203 0           $self->add_bin($dist, dir($distdir,'script'));
204             }
205             }
206              
207             sub get_entry {
208 0     0 0   my ( $self, $module, $file, $type, $dist ) = @_;
209 0           my @lines = file($file)->slurp;
210 0           my $pod;
211 0           for (@lines) {
212 0 0         if (/^=\w+/../^=(cut)\s*$/) {
213 0 0         $pod .= $_ . ( $1 ?"\n":"" )
214             }
215             }
216 0           return CPAN::Documentation::HTML::Entry->new(
217             pod => $pod,
218             module => $module,
219             type => $type,
220             dist => $dist,
221             );
222             }
223              
224             sub add_lib {
225 0     0 0   my ( $self, $dist, $path ) = @_;
226 0           my ( @pods, @pms );
227 0           my $dir = dir($path);
228             $dir->traverse(sub {
229 0     0     my $b = $_[0]->basename;
230 0 0         if ($b =~ qr!\.pm$!) {
    0          
231 0           push @pms, $_[0];
232             } elsif ($b =~ qr!\.pod$!) {
233 0           push @pods, $_[0];
234             }
235 0           return $_[1]->();
236 0           });
237 0           my %modules;
238 0           for my $file (@pods) {
239 0           my @parts = $file->relative(dir($path))->components;
240 0           my $filename = pop @parts;
241 0           $filename =~ s!\.pod$!!;
242 0           my $module = join('::',@parts,$filename);
243 0           $modules{$module} = $self->get_entry( $module, $file, 1, $dist );
244             }
245 0           for my $file (@pms) {
246 0           my @parts = $file->relative(dir($path))->components;
247 0 0         shift @parts if $parts[0] eq '.';
248 0           my $filename = pop @parts;
249 0           $filename =~ s!\.pm$!!;
250 0           my $module = join('::',@parts,$filename);
251 0 0         $modules{$module} = $self->get_entry( $module, $file, 0, $dist )
252             unless defined $modules{$module};
253             }
254 0           for (sort keys %modules) {
255 0           $self->add_entry($modules{$_});
256             }
257             }
258              
259             sub add_bin {
260 0     0 0   my ( $self, $dist, $path ) = @_;
261 0           my $dir = dir($path);
262 0           while (my $file = $dir->next) {
263 0 0         next unless -f $file;
264 0           my $module = $file->basename;
265 0           $self->add_entry($self->get_entry( $module, $file, 2, $dist ));
266             }
267             }
268              
269             sub add_entry {
270 0     0 0   my ( $self, $entry ) = @_;
271 0           my $html_target = file($self->html,$entry->module,'index.html');
272 0           $html_target->dir->mkpath;
273 0           my $psh = CPAN::Documentation::HTML::PodSimple->new;
274 0           $psh->perldoc_url_prefix($self->url_prefix);
275 0           my $pod_simple_html = '';
276 0           $psh->output_string(\$pod_simple_html);
277 0           $psh->index(1);
278 0           $psh->parse_string_document($entry->pod);
279 0           my $tree = HTML::TreeBuilder->new_from_content($pod_simple_html);
280 0           my $body = $tree->find_by_tag_name('body');
281 0           my $body_html = join('',map { $_->as_XML } $body->content_list);
  0            
282 0           my $zoom = HTML::Zoom->from_html($self->template)
283             ->select('.cdh-title')->replace_content($entry->dist.' - '.$entry->module)
284             ->select('.cdh-body')->replace_content(\$body_html);
285 0           $zoom = $self->replace_assets($zoom,'../');
286 0           $html_target->spew($zoom->to_html);
287 0           $self->cache->{$entry->module} = $entry;
288             }
289              
290             1;
291              
292              
293             __END__