File Coverage

blib/lib/POE/Component/SmokeBox/Dists.pm
Criterion Covered Total %
statement 127 197 64.4
branch 21 84 25.0
condition 6 17 35.2
subroutine 28 33 84.8
pod 4 4 100.0
total 186 335 55.5


line stmt bran cond sub pod time code
1             package POE::Component::SmokeBox::Dists;
2             $POE::Component::SmokeBox::Dists::VERSION = '1.10';
3             #ABSTRACT: Search for CPAN distributions by cpanid or distribution name
4              
5 5     5   795045 use strict;
  5         47  
  5         149  
6 5     5   28 use warnings;
  5         10  
  5         143  
7 5     5   23 use Carp;
  5         12  
  5         263  
8 5     5   28 use Cwd;
  5         10  
  5         255  
9 5     5   31 use File::Spec ();
  5         17  
  5         122  
10 5     5   25 use File::Path (qw/mkpath/);
  5         25  
  5         342  
11 5     5   2537 use URI;
  5         22678  
  5         157  
12 5     5   2974 use File::Fetch;
  5         252614  
  5         189  
13 5     5   2403 use CPAN::DistnameInfo;
  5         4592  
  5         164  
14 5     5   2045 use Sort::Versions;
  5         3449  
  5         595  
15 5     5   2549 use IO::Zlib;
  5         292576  
  5         38  
16 5     5   1215 use POE qw(Wheel::Run);
  5         19798  
  5         47  
17              
18             sub author {
19 1     1 1 8435 my $package = shift;
20 1         25 return $package->_spawn( @_, command => 'author' );
21             }
22              
23             sub distro {
24 1     1 1 7612 my $package = shift;
25 1         38 return $package->_spawn( @_, command => 'distro' );
26             }
27              
28             sub phalanx {
29 1     1 1 13547 my $package = shift;
30 1         62 return $package->_spawn( @_, command => 'phalanx' );
31             }
32              
33             sub random {
34 1     1 1 6691 my $package = shift;
35 1         21 return $package->_spawn( @_, command => 'random' );
36             }
37              
38             sub _spawn {
39 4     4   79 my $package = shift;
40 4         220 my %opts = @_;
41 4         94 $opts{lc $_} = delete $opts{$_} for grep { !/^\_/ } keys %opts;
  15         186  
42              
43 4 50       88 $opts{pkg_time} = 21600 unless $opts{pkg_time};
44              
45 4         46 my @mandatory = qw(event);
46 4 100 100     137 push @mandatory, 'search' unless $opts{command} eq 'phalanx' or $opts{command} eq 'random';
47 4         60 foreach my $mandatory ( @mandatory ) {
48 6 50       65 next if $opts{ $mandatory };
49 0         0 carp "The '$mandatory' parameter is a mandatory requirement\n";
50 0         0 return;
51             }
52 4         57 my $options = delete $opts{options};
53 4         79 my $self = bless \%opts, $package;
54 4 50       248 $self->{session_id} = POE::Session->create(
55             package_states => [
56             $self => [qw(
57             _start
58             _initialise
59             _dispatch
60             _spawn_fetch
61             _fetch_err
62             _fetch_close
63             _fetch_sout
64             _fetch_serr
65             _spawn_process
66             _proc_close
67             _proc_sout
68             _sig_child)],
69             ],
70             heap => $self,
71             ( ref($options) eq 'HASH' ? ( options => $options ) : () ),
72             )->ID();
73              
74 4         870 return $self;
75             }
76              
77             sub _start {
78 4     4   2274 my ($kernel,$sender,$self) = @_[KERNEL,SENDER,OBJECT];
79 4         49 $self->{session_id} = $_[SESSION]->ID();
80 4 50 33     70 if ( $kernel == $sender and !$self->{session} ) {
81 0         0 croak "Not called from another POE session and 'session' wasn't set\n";
82             }
83 4         22 my $sender_id;
84 4 50       43 if ( $self->{session} ) {
85 0 0       0 if ( my $ref = $kernel->alias_resolve( $self->{session} ) ) {
86 0         0 $sender_id = $ref->ID();
87             }
88             else {
89 0         0 croak "Could not resolve 'session' to a valid POE session\n";
90             }
91             }
92             else {
93 4         35 $sender_id = $sender->ID();
94             }
95 4         167 $kernel->refcount_increment( $sender_id, __PACKAGE__ );
96 4         438 $self->{session} = $sender_id;
97 4 50       146 $kernel->detach_myself() if $kernel != $sender;
98 4         988 $kernel->yield( '_initialise' );
99 4         949 return;
100             }
101              
102             sub _initialise {
103 4     4   2859 my ($kernel,$self) = @_[KERNEL,OBJECT];
104 4         20 my $return = { };
105              
106 4         53 my $smokebox_dir = File::Spec->catdir( _smokebox_dir(), '.smokebox' );
107              
108 4 50       1184 mkpath $smokebox_dir if ! -d $smokebox_dir;
109 4 50       79 if ( ! -d $smokebox_dir ) {
110 0         0 $return->{error} = "Could not create smokebox directory '$smokebox_dir': $!";
111 0         0 $kernel->yield( '_dispatch', $return );
112 0         0 return;
113             }
114              
115 4         40 $self->{return} = $return;
116 4         23 $self->{sb_dir} = $smokebox_dir;
117              
118 4         72 my $packages_file = File::Spec->catfile( $smokebox_dir, '02packages.details.txt.gz' );
119              
120 4         34 $self->{pack_file} = $packages_file;
121              
122 4 50       96 if ( -e $packages_file ) {
123 0         0 my $mtime = ( stat( $packages_file ) )[9];
124 0 0 0     0 if ( $self->{force} or ( time() - $mtime > $self->{pkg_time} ) ) {
125 0         0 $kernel->yield( '_spawn_fetch', $smokebox_dir, $self->{url} );
126 0         0 return;
127             }
128             }
129             else {
130 4         59 $kernel->yield( '_spawn_fetch', $smokebox_dir, $self->{url} );
131 4         383 return;
132             }
133              
134             # if packages file exists but is older than $self->{pkg_time}, fetch.
135             # if packages file does not exist, fetch.
136             # otherwise it exists so spawn packages processing.
137              
138 0         0 $kernel->yield( '_spawn_process' );
139 0         0 return;
140             }
141              
142             sub _dispatch {
143 4     4   1040 my ($kernel,$self,$return) = @_[KERNEL,OBJECT,ARG0];
144 4         31 $return->{$_} = $self->{$_} for grep { /^\_/ } keys %{ $self };
  35         119  
  4         108  
145 4         52 $kernel->post( $self->{session}, $self->{event}, $return );
146 4         609 $kernel->refcount_decrement( $self->{session}, __PACKAGE__ );
147 4         288 return;
148             }
149              
150             sub _sig_child {
151 0     0   0 $poe_kernel->sig_handled();
152             }
153              
154             sub _spawn_fetch {
155 4     4   1188 my ($kernel,$self) = @_[KERNEL,OBJECT];
156             $self->{FETCH} = POE::Wheel::Run->new(
157             Program => \&_fetch,
158 4         155 ProgramArgs => [ $self->{sb_dir}, $self->{url} ],
159             StdoutEvent => '_fetch_sout',
160             StderrEvent => '_fetch_serr',
161             ErrorEvent => '_fetch_err', # Event to emit on errors.
162             CloseEvent => '_fetch_close', # Child closed all output.
163             );
164 4 50       21474 $kernel->sig_child( $self->{FETCH}->PID(), '_sig_chld' ) if $self->{FETCH};
165 4         1820 return;
166             }
167              
168             sub _fetch_sout {
169 0     0   0 return;
170             }
171              
172             sub _fetch_serr {
173 0     0   0 return;
174             }
175              
176             sub _fetch_err {
177 16     16   14228018 return;
178             }
179              
180             sub _fetch_close {
181 4     4   258 my ($kernel,$self) = @_[KERNEL,OBJECT];
182 4         117 delete $self->{FETCH};
183 4 50       2155 if ( -e $self->{pack_file} ) {
184 4         66 $kernel->yield( '_spawn_process' );
185             }
186             else {
187 0         0 $self->{return}->{error} = 'Could not retrieve packages file';
188 0         0 $kernel->yield( '_dispatch', $self->{return} );
189             }
190 4         591 return;
191             }
192              
193             sub _spawn_process {
194 4     4   904 my ($kernel,$self) = @_[KERNEL,OBJECT];
195 4         58 $self->{dists} = [ ];
196             $self->{PROCESS} = POE::Wheel::Run->new(
197             Program => \&_read_packages,
198 4         129 ProgramArgs => [ $self->{pack_file}, $self->{command}, $self->{search} ],
199             StdoutEvent => '_proc_sout',
200             StderrEvent => '_fetch_serr',
201             ErrorEvent => '_fetch_err', # Event to emit on errors.
202             CloseEvent => '_proc_close', # Child closed all output.
203             );
204 4 50       20284 $kernel->sig_child( $self->{PROCESS}->PID(), '_sig_chld' ) if $self->{PROCESS};
205 4         1026 return;
206             }
207              
208             sub _proc_sout {
209 498     498   157901438 my ($self,$line) = @_[OBJECT,ARG0];
210 498         916 push @{ $self->{dists} }, $line;
  498         1552  
211 498         1238 return;
212             }
213              
214             sub _proc_close {
215 4     4   224 my ($kernel,$self) = @_[KERNEL,OBJECT];
216 4         79 delete $self->{PROCESS};
217 4         1790 $self->{return}->{dists} = delete $self->{dists};
218 4         85 $kernel->yield( '_dispatch', $self->{return} );
219 4         536 return;
220             }
221              
222             sub _read_packages {
223 0     0   0 my ($packages_file,$command,$search) = @_;
224 0         0 my %phalanx;
225 0 0       0 if ( $command eq 'phalanx' ) {
226 0         0 $phalanx{ $_ } = undef for _phalanx();
227             }
228 0 0       0 my $fh = IO::Zlib->new( $packages_file, "rb" ) or die "$!\n";
229 0         0 my %dists;
230 0         0 while (<$fh>) {
231 0 0       0 last if /^\s*$/;
232             }
233 0         0 while (<$fh>) {
234 0         0 chomp;
235 0         0 my $path = ( split ' ', $_ )[2];
236 0 0       0 next unless $path;
237 0 0       0 next if exists $dists{ $path };
238 0         0 my $distinfo = CPAN::DistnameInfo->new( $path );
239 0 0       0 next unless $distinfo->filename() =~ m!(\.tar\.gz|\.tgz|\.zip)$!i;
240 0 0       0 if ( $command eq 'author' ) {
    0          
    0          
241 0 0       0 next unless eval { $distinfo->cpanid() =~ /$search/ };
  0         0  
242 0         0 print $path, "\n";
243             }
244             elsif ( $command eq 'phalanx' ) {
245 0 0       0 next unless exists $phalanx{ $distinfo->dist };
246 0 0       0 if ( defined $phalanx{ $distinfo->dist } ) {
247 0         0 my $exists = CPAN::DistnameInfo->new( $phalanx{ $distinfo->dist } );
248 0 0       0 if ( versioncmp( $distinfo->version, $exists->version ) == 1 ) {
249 0         0 $phalanx{ $distinfo->dist } = $path;
250             }
251             }
252             else {
253 0         0 $phalanx{ $distinfo->dist } = $path;
254             }
255             }
256             elsif ( $command eq 'random' ) {
257 0         0 $dists{ $path } = 1;
258 0         0 next;
259             }
260             else {
261 0 0       0 next unless eval { $distinfo->distvname() =~ /$search/ };
  0         0  
262 0         0 print $path, "\n";
263             }
264 0         0 $dists{ $path } = 1;
265             }
266 0 0       0 if ( $command eq 'phalanx' ) {
267 0         0 print $_, "\n" for grep { defined $_ } values %phalanx;
  0         0  
268             }
269 0 0       0 if ( $command eq 'random' ) {
270 0         0 my @dists = keys %dists;
271 0         0 my %picked;
272 0         0 while ( scalar keys %picked < 100 ) {
273 0         0 my $random = $dists[ rand( $#dists ) ];
274 0 0       0 next if $picked{ $random };
275 0         0 $picked{ $random } = $random;
276 0         0 print $random, "\n";
277             }
278             }
279 0         0 return;
280             }
281              
282             sub _fetch {
283 4   50 4   2801 my $location = shift || return;
284 4         10 my $url = shift;
285 4         20 my @urls = qw(
286             http://www.cpan.org/
287             ftp://ftp.cpan.org/pub/CPAN/
288             http://cpan.cpantesters.org/
289             ftp://cpan.cpantesters.org/CPAN/
290             ftp://ftp.funet.fi/pub/CPAN/
291             );
292 4 50       14 @urls = ( $url ) if $url;
293 4         9 my $file;
294 4         11 foreach my $url ( @urls ) {
295 4 50       34 my $uri = URI->new( $url ) or next;
296 4         29311 my @segs = $uri->path_segments();
297 4 50       183 pop @segs unless $segs[$#segs];
298 4         25 $uri->path_segments( @segs, 'modules', '02packages.details.txt.gz' );
299 4         402 local $File::Fetch::TIMEOUT = 30;
300 4 50       44 my $ff = File::Fetch->new( uri => $uri->as_string() ) or next;
301 4 50       22277 $file = $ff->fetch( to => $location ) or next;
302 4 50       1430650 last if $file;
303             }
304 4         131 return $file;
305             }
306              
307             sub _smokebox_dir {
308             return $ENV{PERL5_SMOKEBOX_DIR}
309             if exists $ENV{PERL5_SMOKEBOX_DIR}
310 5 50 33 5   4940 && defined $ENV{PERL5_SMOKEBOX_DIR};
311              
312 0           my @os_home_envs = qw( APPDATA HOME USERPROFILE WINDIR SYS$LOGIN );
313              
314 0           for my $env ( @os_home_envs ) {
315 0 0         next unless exists $ENV{ $env };
316 0 0 0       next unless defined $ENV{ $env } && length $ENV{ $env };
317 0 0         return $ENV{ $env } if -d $ENV{ $env };
318             }
319              
320 0           return cwd();
321             }
322              
323             # List taken from Bundle::Phalanx100 v0.07
324             sub _phalanx {
325 0     0     return qw(
326             Test-Harness
327             Test-Reporter
328             Test-Simple
329             Test-Builder-Tester
330             Sub-Uplevel
331             Test-Exception
332             Test-Tester
333             Test-NoWarnings
334             Test-Tester
335             Pod-Escapes
336             Pod-Simple
337             Test-Pod
338             YAML
339             PathTools
340             Archive-Tar
341             Module-Build
342             Devel-Symdump
343             Pod-Coverage
344             Test-Pod-Coverage
345             Compress-Zlib
346             IO-Zlib
347             Archive-Zip
348             Archive-Tar
349             Storable
350             Digest-MD5
351             URI
352             HTML-Tagset
353             HTML-Parser
354             libwww-perl
355             IPC-Run
356             CPANPLUS
357             DBI
358             DBD-mysql
359             GD
360             MIME-Base64
361             Net-SSLeay
362             perl-ldap
363             XML-Parser
364             Apache-ASP
365             CGI.pm
366             Date-Manip
367             DBD-Oracle
368             DBD-Pg
369             Digest-SHA1
370             Digest-HMAC
371             HTML-Tagset
372             HTML-Template
373             libnet
374             MailTools
375             MIME-tools
376             Net-DNS
377             Time-HiRes
378             Apache-DBI
379             Apache-Session
380             Apache-Test
381             AppConfig
382             App-Info
383             Authen-PAM
384             Authen-SASL
385             BerkeleyDB
386             Bit-Vector
387             Carp-Clan
388             Chart
389             Class-DBI
390             Compress-Zlib-Perl
391             Config-IniFiles
392             Convert-ASN1
393             Convert-TNEF
394             Convert-UUlib
395             CPAN
396             Crypt-CBC
397             Crypt-DES
398             Crypt-SSLeay
399             Data-Dumper
400             Date-Calc
401             DateTime
402             DBD-DB2
403             DBD-ODBC
404             DBD-SQLite
405             DBD-Sybase
406             Device-SerialPort
407             Digest-SHA
408             Encode
409             Event
410             Excel-Template
411             Expect
412             ExtUtils-MakeMaker
413             File-Scan
414             File-Spec
415             File-Tail
416             File-Temp
417             GDGraph
418             GDTextUtil
419             Getopt-Long
420             HTML-Mason
421             Image-Size
422             IMAP-Admin
423             Parse-RecDescent
424             Inline
425             IO
426             Spiffy
427             IO-All
428             IO-Socket-SSL
429             IO-String
430             IO-stringy
431             libxml-perl
432             Mail-Audit
433             Mail-ClamAV
434             Mail-Sendmail
435             Math-Pari
436             MD5
437             MIME-Lite
438             MP3-Info
439             Net-Daemon
440             Net-FTP-Common
441             Net-Ping
442             Net-Server
443             Net-SNMP
444             Net-SSH-Perl
445             Net-Telnet
446             OLE-Storage_Lite
447             Params-Validate
448             PerlMagick
449             PlRPC
450             Pod-Parser
451             POE
452             SNMP
453             SOAP-Lite
454             Spreadsheet-ParseExcel
455             Spreadsheet-WriteExcel
456             Spreadsheet-WriteExcelXML
457             Storable
458             Template-Toolkit
459             TermReadKey
460             Term-ReadLine-Perl
461             Text-Iconv
462             TimeDate
463             Time-modules
464             Unicode-String
465             Unix-Syslog
466             Verilog-Perl
467             WWW-Mechanize
468             XML-DOM
469             XML-Generator
470             XML-LibXML
471             XML-NamespaceSupport
472             XML-SAX
473             XML-Simple
474             XML-Writer
475             );
476             }
477              
478             1;
479              
480             __END__