File Coverage

blib/lib/POE/Component/SmokeBox/Dists.pm
Criterion Covered Total %
statement 34 36 94.4
branch n/a
condition n/a
subroutine 12 12 100.0
pod n/a
total 46 48 95.8


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