File Coverage

blib/lib/POE/Component/SmokeBox/Recent.pm
Criterion Covered Total %
statement 133 156 85.2
branch 46 64 71.8
condition 2 6 33.3
subroutine 19 23 82.6
pod 1 1 100.0
total 201 250 80.4


line stmt bran cond sub pod time code
1             package POE::Component::SmokeBox::Recent;
2             $POE::Component::SmokeBox::Recent::VERSION = '1.54';
3             #ABSTRACT: A POE component to retrieve recent CPAN uploads.
4              
5 6     6   884191 use strict;
  6         43  
  6         195  
6 6     6   52 use warnings;
  6         18  
  6         211  
7 6     6   29 use Carp;
  6         14  
  6         454  
8 6     6   39 use POE qw(Component::SmokeBox::Recent::HTTP Component::SmokeBox::Recent::FTP Wheel::Run);
  6         21  
  6         71  
9 6     6   104693 use URI;
  6         17  
  6         140  
10 6     6   34 use HTTP::Request;
  6         11  
  6         201  
11 6     6   47 use File::Spec;
  6         17  
  6         14136  
12              
13             sub recent {
14 6     6 1 22121 my $package = shift;
15 6         49 my %opts = @_;
16 6         60 $opts{lc $_} = delete $opts{$_} for keys %opts;
17 6 50       164 croak "$package requires a 'url' argument\n" unless $opts{url};
18 6 50       55 croak "$package requires an 'event' argument\n" unless $opts{event};
19 6 100       33 $opts{rss} = 0 unless $opts{rss};
20 6         18 my $options = delete $opts{options};
21 6         19 my $self = bless \%opts, $package;
22 6         43 $self->{recent} = [];
23 6         82 $self->{uri} = URI->new( $self->{url} );
24             croak "url provided is of an unsupported scheme\n"
25 6 50 33     40472 unless $self->{uri}->scheme and $self->{uri}->scheme =~ /^(ht|f)tp|file$/;
26 6 50       938 $self->{session_id} = POE::Session->create(
27             object_states => [
28             $self => [ qw(_start _process_http _process_ftp _process_file _recent _sig_child _epoch _epoch_fail) ],
29             $self => {
30             http_sockerr => '_get_connect_error',
31             http_timeout => '_get_connect_error',
32             http_response => '_http_response',
33             ftp_sockerr => '_get_connect_error',
34             ftp_error => '_get_error',
35             ftp_data => '_get_data',
36             ftp_done => '_get_done', },
37             ],
38             heap => $self,
39             ( ref($options) eq 'HASH' ? ( options => $options ) : () ),
40             )->ID();
41 6         801 return $self;
42             }
43              
44             sub _start {
45 6     6   2350 my ($kernel,$sender,$self) = @_[KERNEL,SENDER,OBJECT];
46 6         26 $self->{session_id} = $_[SESSION]->ID();
47 6 50 33     78 if ( $kernel == $sender and !$self->{session} ) {
48 0         0 croak "Not called from another POE session and 'session' wasn't set\n";
49             }
50 6         18 my $sender_id;
51 6 50       34 if ( $self->{session} ) {
52 0 0       0 if ( my $ref = $kernel->alias_resolve( $self->{session} ) ) {
53 0         0 $sender_id = $ref->ID();
54             }
55             else {
56 0         0 croak "Could not resolve 'session' to a valid POE session\n";
57             }
58             }
59             else {
60 6         20 $sender_id = $sender->ID();
61             }
62 6         51 $kernel->refcount_increment( $sender_id, __PACKAGE__ );
63 6         278 $kernel->detach_myself();
64 6         836 $self->{sender_id} = $sender_id;
65 6 100       38 if ( $self->{epoch} ) {
66 1         6 $kernel->yield( '_epoch' );
67 1         75 return;
68             }
69 5         26 $kernel->yield( '_process_' . $self->{uri}->scheme );
70 5         617 return;
71             }
72              
73             sub _recent {
74 6     6   2561 my ($kernel,$self,$type) = @_[KERNEL,OBJECT,ARG0];
75 6         30 my $target = delete $self->{sender_id};
76 6         17 my %reply;
77 6 50       57 $reply{recent} = delete $self->{recent} if $self->{recent};
78 6 50       44 $reply{error} = delete $self->{error} if $self->{error};
79 6 50       41 $reply{context} = delete $self->{context} if $self->{context};
80 6         33 $reply{url} = delete $self->{url};
81 6         20 @{ $reply{recent} } = grep { my @parts = split m!/!; $parts[3] !~ m!^perl6$!i } @{ $reply{recent} };
  6         75  
  420         1013  
  420         954  
  6         32  
82 6         39 my $event = delete $self->{event};
83 6         51 $kernel->post( $target, $event, \%reply );
84 6         691 $kernel->refcount_decrement( $target, __PACKAGE__ );
85 6         297 return;
86             }
87              
88             sub _process_http {
89 3     3   741 my ($kernel,$self) = @_[KERNEL,OBJECT];
90 3 100       36 my @path = $self->{rss} ? ( 'modules', '01modules.mtime.rss' ) : ( 'RECENT' );
91 3         52 $self->{uri}->path( File::Spec::Unix->catfile( $self->{uri}->path(), @path ) );
92             POE::Component::SmokeBox::Recent::HTTP->spawn(
93             uri => $self->{uri},
94 3         422 );
95 3         7129 return;
96             }
97              
98             sub _http_response {
99 3     3   43369 my ($kernel,$self,$response) = @_[KERNEL,OBJECT,ARG0];
100 3 50       13 if ( $response->code() == 200 ) {
101 3 100       69 if ( $self->{rss} ) {
102 1         3 for ( split /\n/, $response->content() ) {
103 63 100       170 next unless m#(.+?)#i;
104 7 100       18 next unless m#by-authors#i;
105 6         23 my ($link) = $_ =~ m#id/(.+?)\s*$#i;
106 6 50       15 next unless $link;
107 6         8 unshift @{ $self->{recent} }, $link;
  6         19  
108             }
109             }
110             else {
111 2         9 for ( split /\n/, $response->content() ) {
112 60 100       195 next unless /^authors/;
113 54 100       143 next unless /\.(tar\.gz|tgz|tar\.bz2|zip)$/;
114 11         34 s!authors/id/!!;
115 11         29 push @{ $self->{recent} }, $_;
  11         30  
116             }
117             }
118             }
119             else {
120 0         0 $self->{error} = $response->as_string();
121             }
122 3         23 $kernel->yield( '_recent', 'http' );
123 3         235 return;
124             }
125              
126             sub _process_ftp {
127 2     2   554 my ($kernel,$self) = @_[KERNEL,OBJECT];
128 2 100       12 my @path = $self->{rss} ? ( 'modules', '01modules.mtime.rss' ) : ( 'RECENT' );
129             POE::Component::SmokeBox::Recent::FTP->spawn(
130             Username => 'anonymous',
131             Password => 'anon@anon.org',
132             address => $self->{uri}->host,
133             port => $self->{uri}->port,
134 2         16 path => File::Spec::Unix->catfile( $self->{uri}->path, @path ),
135             );
136 2         9 return;
137             }
138              
139             sub _get_connect_error {
140 0     0   0 my ($kernel,$self,@args) = @_[KERNEL,OBJECT,ARG0..$#_];
141 0         0 $self->{error} = join ' ', @args;
142 0         0 $kernel->yield( '_recent', 'ftp' );
143 0         0 return;
144             }
145              
146             sub _get_error {
147 0     0   0 my ($kernel,$self,$sender,@args) = @_[KERNEL,OBJECT,SENDER,ARG0..$#_];
148 0         0 $self->{error} = join ' ', @args;
149 0         0 $kernel->yield( '_recent', 'ftp' );
150 0         0 return;
151             }
152              
153             sub _get_data {
154 2834     2834   377103 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
155 2834         6815 $data =~ s![\x0D\x0A]+$!!g;
156 2834 100       6488 if ( $self->{rss} ) {
    50          
157 127 100       593 return unless $data =~ m#(.+?)#i;
158 7 100       29 return unless $data =~ m#by-authors#i;
159 6         30 my ($link) = $data =~ m#id/(.+?)\s*$#i;
160 6 50       20 return unless $link;
161 6         7 unshift @{ $self->{recent} }, $link;
  6         20  
162             }
163             elsif ( $self->{epoch} ) {
164 0         0 push @{ $self->{recent} }, $data;
  0         0  
165             }
166             else {
167 2707 100       9617 return unless $data =~ /^authors/i;
168 1297 100       4587 return unless $data =~ /\.(tar\.gz|tgz|tar\.bz2|zip)$/;
169 397         1308 $data =~ s!authors/id/!!;
170 397         703 push @{ $self->{recent} }, $data;
  397         916  
171             }
172 403         1003 return;
173             }
174              
175             sub _get_done {
176 2     2   1102 my ($kernel,$self,$sender) = @_[KERNEL,OBJECT,SENDER];
177 2         35 $kernel->yield( '_recent', 'ftp' );
178 2         165 return;
179             }
180              
181             sub _process_file {
182 1     1   429 my ($kernel,$self) = @_[KERNEL,OBJECT];
183 1         2 delete $self->{_epoch_fail};
184             {
185 1         2 my @segs = $self->{uri}->path_segments;
  1         23  
186 1 50       152 pop @segs unless $segs[-1];
187 1         4 push @segs, 'RECENT';
188 1         5 $self->{uri}->path_segments( @segs );
189             }
190             $self->{wheel} = POE::Wheel::Run->new(
191             Program => sub {
192 0     0   0 my $path = shift;
193 0 0       0 open my $fh, '<', $path or die "$!\n";
194 0         0 while (<$fh>) {
195 0         0 print STDOUT $_;
196             }
197 0         0 close $fh;
198             },
199 1         163 ProgramArgs => [ $self->{uri}->file ],
200             StdoutEvent => 'ftp_data',
201             );
202 1         5812 $kernel->sig_child( $self->{wheel}->PID(), '_sig_child' );
203 1         463 return;
204             }
205              
206             sub _epoch {
207 1     1   277 my ($kernel,$self) = @_[KERNEL,OBJECT];
208 1         524 require CPAN::Recent::Uploads;
209             $self->{wheel} = POE::Wheel::Run->new(
210             Program => sub {
211 0     0   0 my $epoch = shift;
212 0         0 my $mirror = shift;
213 0         0 print STDOUT $_, "\n" for
214             CPAN::Recent::Uploads->recent( $epoch, $mirror );
215             },
216 1         18910 ProgramArgs => [ $self->{epoch}, $self->{uri}->as_string ],
217             StdoutEvent => 'ftp_data',
218             StderrEvent => '_epoch_fail',
219             );
220 1         5378 $kernel->sig_child( $self->{wheel}->PID(), '_sig_child' );
221 1         487 return;
222             }
223              
224             sub _epoch_fail {
225 2     2   14170 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
226             # Anything on STDERR means an error
227 2 100       16 return if $self->{_epoch_fail};
228 1         23 $self->{_epoch_fail} = 1;
229 1         14 $kernel->yield( '_process_' . $self->{uri}->scheme );
230 1         112 return;
231             }
232              
233             sub _sig_child {
234 2     2   4779 my ($kernel,$self) = @_[KERNEL,OBJECT];
235 2         52 delete $self->{wheel};
236 2 100       813 $kernel->yield( '_recent', 'file' ) unless $self->{_epoch_fail};
237 2         142 $kernel->sig_handled();
238             }
239              
240             qq[What's the road on the street?];
241              
242             __END__