File Coverage

blib/lib/POE/Component/CPAN/SQLite/Info.pm
Criterion Covered Total %
statement 21 168 12.5
branch 0 66 0.0
condition 0 3 0.0
subroutine 7 24 29.1
pod 4 5 80.0
total 32 266 12.0


line stmt bran cond sub pod time code
1             package POE::Component::CPAN::SQLite::Info;
2              
3 1     1   459184 use strict;
  1         4  
  1         46  
4 1     1   6 use warnings;
  1         2  
  1         59  
5              
6             our $VERSION = '0.11';
7              
8 1     1   7 use LWP::UserAgent;
  1         7  
  1         24  
9 1     1   5 use File::Spec;
  1         1  
  1         25  
10 1     1   12 use CPAN::SQLite::Info;
  1         1  
  1         28  
11 1     1   4 use POE (qw( Wheel::Run Filter::Reference Filter::Line));
  1         3  
  1         8  
12 1     1   678 use Carp;
  1         2  
  1         2607  
13              
14             sub spawn {
15 0     0 0   my $package = shift;
16            
17 0 0         croak "Even number of arguments must be passed to $package"
18             if @_ & 1;
19              
20 0           my %params = @_;
21            
22 0           $params{ lc $_ } = delete $params{ $_ } for keys %params;
23              
24 0 0         delete $params{options}
25             unless ref $params{options} eq 'HASH';
26              
27 0 0         unless ( exists $params{path} ) {
28 0           $params{path} = 'cpan_sqlite_info/';
29 0 0         warn "Warning: No `path` parameter was specified\n"
30             if $params{debug};
31             }
32            
33 0 0         unless ( exists $params{mirror} ) {
34 0           $params{mirror} = 'http://cpan.perl.org';
35             }
36              
37 0           my $self = bless \%params, $package;
38              
39 0 0         $self->{session_id} = POE::Session->create(
40             object_states => [
41             $self => {
42             freshen => '_freshen',
43             fetch_info => '_fetch_info',
44             shutdown => '_shutdown',
45             },
46             $self => [
47             qw(
48             _child_error
49             _child_close
50             _child_stderr
51             _child_stdout
52             _sig_chld
53             _start
54             )
55             ],
56             ],
57             ( exists $params{options} ? ( options => $params{options} ) : () ),
58             )->ID;
59            
60 0           return $self;
61             }
62              
63             sub _start {
64 0     0     my ( $kernel, $self ) = @_[ KERNEL, OBJECT ];
65 0           $self->{session_id} = $_[SESSION]->ID();
66            
67 0 0         if ( $self->{alias} ) {
68 0           $kernel->alias_set( $self->{alias} );
69             }
70             else {
71 0           $kernel->refcount_increment( $self->{session_id} => __PACKAGE__ );
72             }
73            
74 0 0         $self->{wheel} = POE::Wheel::Run->new(
75             Program => \&_wheel,
76             ErrorEvent => '_child_error',
77             CloseEvent => '_child_close',
78             StderrEvent => '_child_stderr',
79             StdoutEvent => '_child_stdout',
80             StdioFilter => POE::Filter::Reference->new,
81             StderrFilter => POE::Filter::Line->new,
82             ( $^O eq 'MSWin32' ? ( CloseOnCall => 0 ) : ( CloseOnCall => 1 ) ),
83             );
84            
85 0 0         $kernel->call('shutdown')
86             unless $self->{wheel};
87            
88 0           $kernel->sig_child( $self->{wheel}->PID, '_sig_chld' );
89             }
90              
91             sub _sig_chld {
92 0     0     $poe_kernel->sig_handled;
93             }
94              
95             sub _child_close {
96 0     0     my ( $kernel, $self, $wheel_id ) = @_[ KERNEL, OBJECT, ARG0 ];
97              
98 0 0         warn "_child_close called (@_[ARG0..$#_])\n"
99             if $self->{debug};
100              
101 0           delete $self->{wheel};
102 0 0         $kernel->yield('shutdown')
103             unless $self->{shutdown};
104              
105 0           undef;
106             }
107              
108             sub _child_error {
109 0     0     my ( $kernel, $self ) = @_[ KERNEL, OBJECT ];
110 0 0         warn "_child_error called (@_[ARG0..$#_])\n"
111             if $self->{debug};
112              
113 0           delete $self->{wheel};
114 0 0         $kernel->yield('shutdown')
115             unless $self->{shutdown};
116              
117 0           undef;
118             }
119              
120             sub _child_stderr {
121 0     0     my ( $kernel, $self ) = @_[ KERNEL, OBJECT ];
122 0 0         warn "_child_stderr: $_[ARG0]\n"
123             if $self->{debug};
124              
125 0           undef;
126             }
127              
128             sub _child_stdout {
129 0     0     my ( $kernel, $self, $input ) = @_[ KERNEL, OBJECT, ARG0 ];
130            
131 0           my $session = delete $input->{sender};
132 0           my $event = delete $input->{event};
133              
134 0           $kernel->post( $session, $event, $input );
135 0           $kernel->refcount_decrement( $session => __PACKAGE__ );
136              
137 0           undef;
138             }
139              
140             sub shutdown {
141 0     0 1   my $self = shift;
142 0           $poe_kernel->post( $self->{session_id} => 'shutdown' );
143             }
144              
145             sub _shutdown {
146 0     0     my ( $kernel, $self ) = @_[ KERNEL, OBJECT ];
147 0           $kernel->alarm_remove_all;
148 0           $kernel->alias_remove( $_ ) for $kernel->alias_list;
149 0 0         $kernel->refcount_decrement( $self->{session_id} => __PACKAGE__ )
150             unless $self->{alias};
151              
152 0           $self->{shutdown} = 1;
153 0 0         $self->{wheel}->shutdown_stdin
154             if $self->{wheel};
155             }
156              
157             sub session_id {
158 0     0 1   return $_[0]->{session_id};
159             }
160              
161             sub freshen {
162 0     0 1   my $self = shift;
163 0           $poe_kernel->post( $self->{session_id} => 'freshen' => @_ );
164             }
165              
166             sub fetch_info {
167 0     0 1   my $self = shift;
168 0           $poe_kernel->post( $self->{session_id} => 'fetch_info' => @_ );
169             }
170              
171             sub _fetch_info {
172 0     0     my ( $kernel, $self, $args )= @_[ KERNEL, OBJECT, ARG0 ];
173              
174 0           my $sender = $_[SENDER]->ID;
175              
176             return
177 0 0         if $self->{shutdown};
178              
179 0           $args->{ lc $_ } = delete $args->{ $_ }
180 0           for grep { !/^_/ } keys %{ $args };
  0            
181              
182            
183 0 0         if ( $args->{session} ) {
184 0 0         if ( my $ref = $kernel->alias_resolve( $args->{session} ) ) {
185 0           $args->{sender} = $ref->ID;
186             }
187             else {
188 0           warn "Could not resolve `session` parameter to a "
189             . "valid POE session. Aborting...";
190 0           return;
191             }
192             }
193             else {
194 0           $args->{sender} = $sender;
195             }
196            
197 0 0         unless ( exists $args->{path} ) {
198 0           $args->{path} = $self->{path};
199             }
200              
201 0           delete $args->{freshen}; # to make sure wheel doesn't freshen by mistake
202 0           $kernel->refcount_increment( $args->{sender} => __PACKAGE__ );
203 0           $self->{wheel}->put( $args );
204              
205 0           undef;
206             }
207              
208              
209             # yes, yes, the almost identical sub{} to the above one....
210             # fighting POE's "magik" in here is beyond me...
211             # .. note to self: figure out wtf is going on.
212              
213             sub _freshen {
214 0     0     my ( $kernel, $self, $args )= @_[ KERNEL, OBJECT, ARG0 ];
215              
216 0           my $sender = $_[SENDER]->ID;
217              
218             return
219 0 0         if $self->{shutdown};
220              
221 0           $args->{ lc $_ } = delete $args->{ $_ }
222 0           for grep { !/^_/ } keys %{ $args };
  0            
223              
224            
225 0 0         if ( $args->{session} ) {
226 0 0         if ( my $ref = $kernel->alias_resolve( $args->{session} ) ) {
227 0           $args->{sender} = $ref->ID;
228             }
229             else {
230 0           warn "Could not resolve `session` parameter to a "
231             . "valid POE session. Aborting...";
232 0           return;
233             }
234             }
235             else {
236 0           $args->{sender} = $sender;
237             }
238            
239 0 0         unless ( exists $args->{path} ) {
240 0           $args->{path} = $self->{path};
241             }
242            
243 0 0         unless ( exists $args->{mirror} ) {
244 0           $args->{mirror} = $self->{mirror};
245             }
246              
247 0 0         unless ( exists $args->{ua_args}{timeout} ) {
248 0           $args->{ua_args}{timeout} = 30;
249             }
250              
251 0           $args->{freshen} = 1; # for the wheel to know what to do.
252 0           $kernel->refcount_increment( $args->{sender} => __PACKAGE__ );
253 0           $self->{wheel}->put( $args );
254              
255 0           undef;
256             }
257              
258             sub _wheel {
259 0 0   0     if ( $^O eq 'MSWin32' ) {
260 0           binmode STDIN;
261 0           binmode STDOUT;
262             }
263            
264 0           my $raw;
265 0           my $size = 4096;
266 0           my $filter = POE::Filter::Reference->new;
267              
268 0           while ( sysread STDIN, $raw, $size ) {
269 0           my $requests = $filter->get( [ $raw ] );
270 0           foreach my $req_ref ( @$requests ) {
271              
272 0 0         if ( exists $req_ref->{freshen} ) {
273 0           eval { _fetch_data_files( $req_ref ); };
  0            
274 0 0         $req_ref->{freshen_error} = $@
275             if $@;
276             }
277             else {
278 0           _populate_info( $req_ref );
279             }
280              
281 0           my $response = $filter->put( [ $req_ref ] );
282 0           print STDOUT @$response;
283             }
284             }
285             }
286              
287             sub _populate_info {
288 0     0     my $req_ref = shift;
289              
290 0           my $info = CPAN::SQLite::Info->new( CPAN => $req_ref->{path} );
291            
292             # stupid ->fetch_info prints crap to STDOUT effectively
293             # breaking Wheel ~_~
294             {
295 0           local *STDOUT;
  0            
296 0           open STDOUT, '>', File::Spec->devnull;
297 0           $info->fetch_info;
298             }
299              
300 0           @$req_ref{ qw( dists mods auths ) }
301             = @$info{ qw( dists mods auths ) };
302              
303 0           undef;
304             }
305              
306             sub _fetch_data_files {
307 0     0     my $req_ref = shift;
308            
309 0           my $path = $req_ref->{path};
310 0           my $mod_dir = File::Spec->catdir( $path, 'modules/' );
311 0           my $auth_dir = File::Spec->catdir( $path, 'authors/' );
312              
313 0           foreach my $dir ( $path, $mod_dir, $auth_dir ) {
314 0 0         unless ( -e $dir ) {
315 0 0         mkdir $dir
316             or die "Failed to create directory `$dir` ($!)\n";
317             }
318             }
319              
320 0 0         my $ua = LWP::UserAgent->new( %{ $req_ref->{ua_args} || {} } );
  0            
321              
322 0           my $mirror = $req_ref->{mirror};
323            
324 0           @{ $req_ref->{uris} }{ qw(modlist packages authors) } = (
  0            
325             URI->new( $mirror ),
326             URI->new( $mirror ),
327             URI->new( $mirror ),
328             );
329            
330 0           my $uris_ref = $req_ref->{uris};
331 0           $uris_ref->{modlist }->path('/modules/03modlist.data.gz');
332 0           $uris_ref->{packages}->path('/modules/02packages.details.txt.gz');
333 0           $uris_ref->{authors }->path('/authors/01mailrc.txt.gz');
334              
335 0           $req_ref->{files}{ modlist } = File::Spec->catfile(
336             $mod_dir,
337             '03modlist.data.gz',
338             );
339 0           $req_ref->{files}{ packages } = File::Spec->catfile(
340             $mod_dir,
341             '02packages.details.txt.gz',
342             );
343 0           $req_ref->{files}{ authors } = File::Spec->catfile(
344             $auth_dir,
345             '01mailrc.txt.gz',
346             );
347            
348 0           keys %{ $req_ref->{uris} };
  0            
349 0           while ( my ( $name, $uri ) = each %{ $req_ref->{uris} } ) {
  0            
350              
351 0           $req_ref->{requests}{ $name } = $ua->mirror(
352             $uri,
353             $req_ref->{files}{ $name },
354             );
355              
356 0           my $requests_ref = $req_ref->{requests};
357             # check for fetch errors, but do not consider 304 an error,
358             # we are fine with that since it indicates that file is good
359             # enough for that we need it.
360 0 0 0       if (
361             !$requests_ref->{ $name }->is_success
362             and $requests_ref->{ $name }->status_line ne '304 Not Modified'
363             ) {
364 0           $req_ref->{freshen_errors}{ $name }
365             = $req_ref->{requests}{ $name }->status_line;
366              
367 0           $req_ref->{freshen_error} = 'fetch';
368             }
369             }
370              
371 0           undef;
372             }
373              
374             1;
375              
376             __END__