File Coverage

blib/lib/CPAN/Mini/ProjectDocs.pm
Criterion Covered Total %
statement 40 164 24.3
branch 0 36 0.0
condition n/a
subroutine 14 22 63.6
pod 8 8 100.0
total 62 230 26.9


line stmt bran cond sub pod time code
1              
2             package CPAN::Mini::ProjectDocs ;
3              
4 2     2   74127 use strict;
  2         6  
  2         61  
5 2     2   9 use warnings ;
  2         4  
  2         54  
6 2     2   9 use Carp qw(carp croak confess) ;
  2         9  
  2         172  
7              
8             BEGIN
9             {
10 2         27 use Sub::Exporter -setup =>
11             {
12             exports => [ qw(generate_html get_module_distribution generate_cache search_modules) ],
13             groups =>
14             {
15             all => [ qw() ],
16             }
17 2     2   1839 };
  2         26872  
18            
19 2     2   941 use vars qw ($VERSION);
  2         6  
  2         93  
20 2     2   40 $VERSION = '0.03';
21             }
22              
23             #-------------------------------------------------------------------------------
24              
25 2     2   7798 use English qw( -no_match_vars ) ;
  2         10006  
  2         12  
26              
27 2     2   2788 use Readonly ;
  2         6314  
  2         113  
28             Readonly my $EMPTY_STRING => q{} ;
29              
30 2     2   1766 use CPAN::PackageDetails ;
  2         38002  
  2         86  
31 2     2   22 use Digest::MD5;
  2         6  
  2         82  
32 2     2   2245 use File::Slurp ;
  2         30725  
  2         166  
33 2     2   21 use Data::Dumper ;
  2         6  
  2         87  
34 2     2   2605 use Archive::Tar ;
  2         249271  
  2         177  
35 2     2   2067 use Pod::ProjectDocs;
  2         215264  
  2         35  
36              
37             #-------------------------------------------------------------------------------
38              
39             =head1 NAME
40              
41             CPAN::Mini::ProjectDocs - mini CPAN documentation browser
42              
43             =head1 SYNOPSIS
44              
45             see the B command for a full example.
46              
47             =head1 DESCRIPTION
48              
49             This module and associated script B let you search and display documentation for the modules in you CPAN mini.
50             The documentation is displayed in your browser (text mode browsers supported)
51              
52             =head1 DOCUMENTATION
53              
54             You most probably want to run the B script, use the I<--help> option for help.
55              
56             =head1 SUBROUTINES/METHODS
57              
58             =cut
59              
60             #-------------------------------------------------------------------------------
61              
62             sub get_mcd_paths
63             {
64              
65             =head2 get_mcd_paths($cpan_mini, $mcd_cache)
66              
67             Given a CPAN mini location and a cache location, computes a list containing the paths used by CPAN::Mini::ProjectDocs.
68              
69             I
70              
71             =over 2
72              
73             =item $cpan_mini - Location of the CPAN::MINI repository
74              
75             =item $mcd_cache - Location of the cache maintained by CPAN::Mini::ProjectDocs
76              
77             =back
78              
79             I - A list containing the paths used by CPAN::Mini::ProjectDocs
80              
81             I - None
82              
83             =cut
84              
85 0     0 1   my ($cpan_mini, $mcd_cache) = @_ ;
86              
87 0           my $modules_details_file = "$cpan_mini/modules/02packages.details.txt.gz" ;
88 0           my $modules_details_txt_md5_file = "$mcd_cache/packages.details.md5.txt" ;
89 0           my $modules_details_cache_file = "$mcd_cache/packages.details.cache" ;
90 0           my $modules_details_cache_all_names = "${modules_details_cache_file}_all_names.txt" ;
91              
92             return
93             (
94 0           $modules_details_file,
95             $modules_details_txt_md5_file,
96             $modules_details_cache_file,
97             $modules_details_cache_all_names
98             ) ;
99             }
100              
101             #---------------------------------------------------------------------------------------------------------
102              
103             sub generate_html
104             {
105              
106             =head2 generate_html($cpan_mini, $mcd_cache, $distribution, $html_index)
107              
108             Generates the HTML documentation for $distribution. The generation is performed only if the
109             documentation does not exist in the cache.
110              
111             I
112              
113             =over 2
114              
115             =item $cpan_mini - Location of the CPAN::MINI repository
116              
117             =item $mcd_cache - Location of the cache maintained by CPAN::Mini::ProjectDocs
118              
119             =item $distribution - Location of the distribution containing the module to display
120              
121             =item $html_index - Boolean - generate a pure HTML index for text based browser
122              
123             =back
124              
125             I - $html_documentation_location
126              
127             I - problems with the distribution extraction, write errors on the file system, ...
128              
129             =cut
130              
131 0     0 1   my ($cpan_mini, $mcd_cache, $distribution, $html_index) = @_ ;
132              
133 0           my ($module_directory) = $distribution =~ /([^\/]+)\.tar.gz$/ ;
134 0           my $html_directory = "$mcd_cache/generated_html/$module_directory" ;
135 0           my $html_directory_md5 = "$html_directory/md5.txt" ;
136              
137 0           my $regenerate_html = 0 ;
138              
139 0           my ($modules_details_file) = get_mcd_paths($cpan_mini, $mcd_cache) ;
140 0           my $modules_details_txt_md5 = get_file_MD5($modules_details_file) ;
141              
142             #check if the html was already generated
143 0 0         if(-e $html_directory)
144             {
145             eval
146 0           {
147 0           my $mcd_cache_md5 = read_file($html_directory_md5) ;
148 0 0         $regenerate_html++ if $mcd_cache_md5 ne $modules_details_txt_md5 ;
149             } ;
150            
151 0 0         $regenerate_html++ if $@ ; # file not found
152             }
153             else
154             {
155 0           $regenerate_html++ ;
156             }
157            
158 0 0         if($regenerate_html)
159             {
160 0           my $tar = Archive::Tar->new($distribution) ;
161 0           $tar->setcwd($mcd_cache);
162 0           $tar->extract() ;
163            
164 0           mkdir "$mcd_cache/generated_html/" ;
165 0           mkdir $html_directory ;
166            
167 0 0         Pod::ProjectDocs->new
168             (
169             outroot => $html_directory,
170             libroot => -e "$mcd_cache/$module_directory/lib" ? "$mcd_cache/$module_directory/lib" : "$mcd_cache/$module_directory",
171             title => $distribution,
172             )->gen() ;
173            
174 0           write_file($html_directory_md5, $modules_details_txt_md5) ;
175             }
176            
177 0           my $html_documentation_location = "$mcd_cache/generated_html/$module_directory/index.html" ;
178              
179 0 0         if($html_index)
180             {
181 0           $html_documentation_location = generate_pure_html_index("$mcd_cache/generated_html/$module_directory/", 'index.html') ;
182             }
183              
184 0           return $html_documentation_location;
185             }
186              
187             #---------------------------------------------------------------------------------------------------------
188              
189             sub generate_pure_html_index
190             {
191              
192             =head2 generate_pure_html_index($path, $file)
193              
194             Generate a pure HTML index for text based browsers.
195              
196             I
197              
198             =over 2
199              
200             =item $path - path to the POD::ProjDocs generated index file
201              
202             =item $file - POD::ProjDocs generated index file
203              
204             =back
205              
206             I - The location of the pure HTML index
207              
208             I - None
209              
210             =cut
211              
212 0     0 1   my ($path, $file) = @_ ;
213              
214 0           my $index = read_file("$path/$file") ;
215              
216             # convert JS data structure to Perl data structure
217 0           my ($data) = $index =~ m/var managers = (.*?)function render\(pattern\)/sm ;
218              
219 0           my $perl_data_structure = '' ;
220 0           my $in_string = 0 ;
221              
222 0           for my $character (split //, $data)
223             {
224 0 0         if($in_string)
225             {
226 0           $perl_data_structure .= $character ;
227 0 0         $in_string = 0 if $character eq q{"} ;
228             }
229             else
230             {
231 0 0         $in_string = 1 if $character eq q{"} ;
232            
233 0 0         if($character eq q{:})
234             {
235 0           $perl_data_structure .= q{=>} ;
236             }
237             else
238             {
239 0           $perl_data_structure .= $character ;
240             }
241             }
242             }
243            
244 0           $data = eval $perl_data_structure ;
245              
246 0           my $html = '' ;
247              
248 0           for my $section (@{$data})
  0            
249             {
250 0           $html .= <
251            
252            

$section->{desc}

253            
254             EOH
255              
256 0           my $row_class = 'r' ;
257              
258 0           for my $module (@{$section->{records}})
  0            
259             {
260 0           $html .= <
261            
262            
263             {path}>
264               $module->{name}
265            
266            
267            
268            
269               $module->{title}
270            
271            
272            
273             EOR
274              
275 0 0         if($row_class eq 'r' )
276             {
277 0           $row_class = 's' ;
278             }
279             else
280             {
281 0           $row_class = 'r' ;
282             }
283             }
284            
285 0           $html .= <
286            
287            
288             EOH
289             }
290            
291             # remove all trace of javascript
292 0           $index =~ s{}{}sm ;
293 0           $index =~ s{\Q}{}sm ;
294              
295 0           $index =~ s{(
.*?
)}{}sm ;
296 0           my $title = '' ; #$1 ;
297              
298 0           $index =~ s{
.*?
}{}sm ;
299              
300 0           $index =~ s{()}{$1\n$title\n$html}sm ;
301              
302 0           my $pure_html_index_location = "$path/pure_html_index.html" ;
303              
304 0           write_file $pure_html_index_location, $index ;
305              
306 0           return $pure_html_index_location ;
307             }
308              
309             #---------------------------------------------------------------------------------------------------------
310              
311             sub get_module_distribution
312             {
313              
314             =head2 get_module_distribution($cpan_mini, $mcd_cache, $module)
315              
316             Finds the distribution containing the module.
317              
318             my $distribution = get_module_distribution($cpan_mini, $mcd_cache, $module) ;
319              
320             I
321              
322             =over 2
323              
324             =item $cpan_mini - Location of the CPAN::MINI repository
325              
326             =item $mcd_cache - Location of the cache maintained by CPAN::Mini::ProjectDocs
327              
328             =item $module - Name of the module to display
329              
330             =back
331              
332             I - The location of the distribution containing the module to display
333              
334             I - read error if the cache is not already generated
335              
336             =cut
337              
338 0     0 1   my ($cpan_mini, $mcd_cache, $module) = @_ ;
339              
340 0           my (undef, undef, $modules_details_cache_file) = get_mcd_paths($cpan_mini, $mcd_cache) ;
341 0           my $first_letter = substr($module, 0, 1) ;
342 0           my $cache_file = "${modules_details_cache_file}_$first_letter.txt" ;
343              
344 0 0         my $module_details = do $cache_file or carp "Error: Invalid '$cache_file'\n" ;;
345              
346 0           my $distribution ;
347              
348 0           for my $record ( @{$module_details->{entries}{entries}})
  0            
349             {
350 0 0         if($record->{'package name'} eq $module)
351             {
352 0           $distribution = "$cpan_mini/authors/id/$record->{'path'}" ;
353 0           last ;
354             }
355             }
356              
357 0           return $distribution ;
358             }
359              
360             #---------------------------------------------------------------------------------------------------------
361              
362             sub generate_cache
363             {
364              
365             =head2 generate_cache($cpan_mini, $mcd_cache)
366              
367             Checks the state of the B cache and regenerates it if necessary.
368              
369             I
370              
371             =over 2
372              
373             =item $cpan_mini - Location of the CPAN::MINI repository
374              
375             =item $mcd_cache - Location of the cache maintained by CPAN::Mini::ProjectDocs
376              
377             =back
378              
379             I - Nothing
380              
381             I - None
382              
383             =cut
384              
385 0     0 1   my ($cpan_mini, $mcd_cache) = @_ ;
386              
387 0           my ($modules_details_file, $modules_details_txt_md5_file, $modules_details_cache_file) = get_mcd_paths($cpan_mini, $mcd_cache) ;
388 0           my $modules_details_txt_md5 = get_file_MD5($modules_details_file) ;
389              
390 0 0         if(-e $modules_details_txt_md5_file)
391             {
392 0           my $mcd_cache_md5 = read_file($modules_details_txt_md5_file) ;
393            
394 0 0         if($mcd_cache_md5 ne $modules_details_txt_md5)
395             {
396 0           regenerate_cache($cpan_mini, $mcd_cache) ;
397             }
398             }
399             else
400             {
401 0           regenerate_cache($cpan_mini, $mcd_cache) ;
402             }
403              
404 0           return ;
405             }
406              
407             #---------------------------------------------------------------------------------------------------------
408              
409             sub regenerate_cache
410             {
411              
412             =head2 regenerate_cache($cpan_mini, $mcd_cache)
413              
414             Generates the B cache.
415              
416             I
417              
418             =over 2
419              
420             =item $cpan_mini - Location of the CPAN::MINI repository
421              
422             =item $mcd_cache - Location of the cache maintained by CPAN::Mini::ProjectDocs
423              
424             =back
425              
426             I - Nothing
427              
428             I - File sytem related errors if any
429              
430             =cut
431              
432 0     0 1   my ($cpan_mini, $mcd_cache) = @_ ;
433              
434 0           warn "Generating cache.\n" ;
435              
436 0           my ($modules_details_file, $modules_details_txt_md5_file, $modules_details_cache_file, $modules_details_cache_all_names)
437             = get_mcd_paths($cpan_mini, $mcd_cache) ;
438            
439 0           my $modules_details_txt_md5 = get_file_MD5($modules_details_file) ;
440              
441 0           my $module_details = CPAN::PackageDetails->read( $modules_details_file );
442              
443 0           my $count = $module_details->count;
444 0           warn "$count records found.\n" ;
445              
446 0           my $entries_lookup = {} ;
447 0           my @modules ;
448              
449 0           my $entries = $module_details->{entries};
450 0           my $records = $entries->{entries};
451 0           for my $record ( @{$records})
  0            
452             {
453 0           push @modules, $record->{'package name'} ;
454            
455 0           my $first_letter = substr($record->{'package name'}, 0, 1) ;
456 0           push @{$entries_lookup->{$first_letter}}, $record ;
  0            
457             }
458              
459             #----------------------------------
460              
461 0           local $Data::Dumper::Purity = 1 ;
462 0           local $Data::Dumper::Indent = 0 ;
463              
464 0           for(keys %{$entries_lookup})
  0            
465             {
466 0           $module_details->{entries}{entries} = $entries_lookup->{$_} ;
467 0           write_file "${modules_details_cache_file}_$_.txt", Dumper($module_details) , "\n\$VAR1 ;\n" ;
468             }
469              
470 0           write_file $modules_details_cache_all_names, Dumper(\@modules), "\n\$VAR1 ;\n" ;
471 0           write_file($modules_details_txt_md5_file, $modules_details_txt_md5) ;
472              
473 0           return ;
474             }
475            
476             #---------------------------------------------------------------------------------------------------------
477              
478             sub search_modules
479             {
480              
481             =head2 search_modules($cpan_mini, $mcd_cache, $module)
482              
483             Matches I<$module> to all the modules in the CPAN mini repository and displays the match results.
484              
485             I
486              
487             =over 2
488              
489             =item $cpan_mini - Location of the CPAN::MINI repository
490              
491             =item $mcd_cache - Location of the cache maintained by CPAN::Mini::ProjectDocs
492              
493             =item $module - Name of the module to match
494              
495             =back
496              
497             I - Nothing
498              
499             I
500              
501             =cut
502              
503 0     0 1   my ($cpan_mini, $mcd_cache, $module) = @_ ;
504              
505 0           my ($modules_details_file, $modules_details_txt_md5_file, $modules_details_cache_file, $modules_details_cache_all_names)
506             = get_mcd_paths($cpan_mini, $mcd_cache) ;
507              
508 0 0         my $modules = do $modules_details_cache_all_names or carp "Error: Invalid '$modules_details_cache_all_names'!\n";
509              
510 0           for (@{$modules})
  0            
511             {
512 0 0         print "$_\n" if(/$module/i) ;
513             }
514              
515             #~ use Text::Soundex ;
516             #~ my $soundex = soundex($module) ;
517              
518             #~ for(@{$module_details->{entries}{entries}})
519             #~ {
520             #~ my $possible_package_soundex = soundex($_->{'package name'}) ;
521            
522             #~ print "\t$_->{'package name'}\n" if $soundex eq $possible_package_soundex ;
523             #~ }
524            
525 0           return ;
526             }
527            
528             #---------------------------------------------------------------------------------------------------------
529              
530             sub get_file_MD5
531             {
532              
533             =head2 get_file_MD5($file)
534              
535             Returns the MD5 of the I<$file> argument.
536              
537             I
538              
539             =over 2
540              
541             =item $file - The location of the file to compute an MD5 for
542              
543             =back
544              
545             I - A string containing the file md5
546              
547             I - fails if the file can't be open
548              
549             =cut
550              
551 0     0 1   my ($file) = @_ ;
552 0 0         open(FILE, $file) or croak "Can't open '$file': $!";
553 0           binmode(FILE);
554 0           return Digest::MD5->new->addfile(*FILE)->hexdigest ;
555             }
556              
557             #-------------------------------------------------------------------------------
558              
559             1 ;
560              
561             =head1 BUGS AND LIMITATIONS
562              
563             None so far.
564              
565             =head1 AUTHOR
566              
567             Nadim ibn hamouda el Khemir
568             CPAN ID: NH
569             mailto: nadim@cpan.org
570              
571             =head1 LICENSE AND COPYRIGHT
572              
573             This program is free software; you can redistribute
574             it and/or modify it under the same terms as Perl itself.
575              
576             =head1 SUPPORT
577              
578             You can find documentation for this module with the perldoc command.
579              
580             perldoc CPAN::Mini::ProjectDocs
581              
582             You can also look for information at:
583              
584             =over 4
585              
586             =item * AnnoCPAN: Annotated CPAN documentation
587              
588             L
589              
590             =item * RT: CPAN's request tracker
591              
592             Please report any bugs or feature requests to L .
593              
594             We will be notified, and then you'll automatically be notified of progress on
595             your bug as we make changes.
596              
597             =item * Search CPAN
598              
599             L
600              
601             =back
602              
603             =head1 SEE ALSO
604              
605             L, elinks
606              
607             =cut