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.52';
3             #ABSTRACT: A POE component to retrieve recent CPAN uploads.
4              
5 6     6   870579 use strict;
  6         43  
  6         198  
6 6     6   33 use warnings;
  6         12  
  6         193  
7 6     6   38 use Carp;
  6         16  
  6         441  
8 6     6   38 use POE qw(Component::SmokeBox::Recent::HTTP Component::SmokeBox::Recent::FTP Wheel::Run);
  6         15  
  6         76  
9 6     6   103397 use URI;
  6         15  
  6         132  
10 6     6   34 use HTTP::Request;
  6         14  
  6         169  
11 6     6   37 use File::Spec;
  6         54  
  6         13512  
12              
13             sub recent {
14 6     6 1 20328 my $package = shift;
15 6         40 my %opts = @_;
16 6         62 $opts{lc $_} = delete $opts{$_} for keys %opts;
17 6 50       125 croak "$package requires a 'url' argument\n" unless $opts{url};
18 6 50       45 croak "$package requires an 'event' argument\n" unless $opts{event};
19 6 100       25 $opts{rss} = 0 unless $opts{rss};
20 6         20 my $options = delete $opts{options};
21 6         15 my $self = bless \%opts, $package;
22 6         40 $self->{recent} = [];
23 6         70 $self->{uri} = URI->new( $self->{url} );
24             croak "url provided is of an unsupported scheme\n"
25 6 50 33     39802 unless $self->{uri}->scheme and $self->{uri}->scheme =~ /^(ht|f)tp|file$/;
26 6 50       824 $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         796 return $self;
42             }
43              
44             sub _start {
45 6     6   2306 my ($kernel,$sender,$self) = @_[KERNEL,SENDER,OBJECT];
46 6         30 $self->{session_id} = $_[SESSION]->ID();
47 6 50 33     79 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         21 my $sender_id;
51 6 50       27 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         24 $sender_id = $sender->ID();
61             }
62 6         47 $kernel->refcount_increment( $sender_id, __PACKAGE__ );
63 6         253 $kernel->detach_myself();
64 6         772 $self->{sender_id} = $sender_id;
65 6 100       47 if ( $self->{epoch} ) {
66 1         5 $kernel->yield( '_epoch' );
67 1         74 return;
68             }
69 5         23 $kernel->yield( '_process_' . $self->{uri}->scheme );
70 5         609 return;
71             }
72              
73             sub _recent {
74 6     6   2526 my ($kernel,$self,$type) = @_[KERNEL,OBJECT,ARG0];
75 6         32 my $target = delete $self->{sender_id};
76 6         20 my %reply;
77 6 50       61 $reply{recent} = delete $self->{recent} if $self->{recent};
78 6 50       32 $reply{error} = delete $self->{error} if $self->{error};
79 6 50       84 $reply{context} = delete $self->{context} if $self->{context};
80 6         57 $reply{url} = delete $self->{url};
81 6         28 @{ $reply{recent} } = grep { my @parts = split m!/!; $parts[3] !~ m!^perl6$!i } @{ $reply{recent} };
  6         70  
  420         981  
  420         987  
  6         35  
82 6         36 my $event = delete $self->{event};
83 6         42 $kernel->post( $target, $event, \%reply );
84 6         690 $kernel->refcount_decrement( $target, __PACKAGE__ );
85 6         288 return;
86             }
87              
88             sub _process_http {
89 3     3   714 my ($kernel,$self) = @_[KERNEL,OBJECT];
90 3 100       32 my @path = $self->{rss} ? ( 'modules', '01modules.mtime.rss' ) : ( 'RECENT' );
91 3         51 $self->{uri}->path( File::Spec::Unix->catfile( $self->{uri}->path(), @path ) );
92             POE::Component::SmokeBox::Recent::HTTP->spawn(
93             uri => $self->{uri},
94 3         334 );
95 3         13 return;
96             }
97              
98             sub _http_response {
99 3     3   3439 my ($kernel,$self,$response) = @_[KERNEL,OBJECT,ARG0];
100 3 50       12 if ( $response->code() == 200 ) {
101 3 100       58 if ( $self->{rss} ) {
102 1         35 for ( split /\n/, $response->content() ) {
103 63 100       169 next unless m#(.+?)#i;
104 7 100       19 next unless m#by-authors#i;
105 6         22 my ($link) = $_ =~ m#id/(.+?)\s*$#i;
106 6 50       17 next unless $link;
107 6         11 unshift @{ $self->{recent} }, $link;
  6         16  
108             }
109             }
110             else {
111 2         72 for ( split /\n/, $response->content() ) {
112 60 100       205 next unless /^authors/;
113 54 100       143 next unless /\.(tar\.gz|tgz|tar\.bz2|zip)$/;
114 11         39 s!authors/id/!!;
115 11         24 push @{ $self->{recent} }, $_;
  11         30  
116             }
117             }
118             }
119             else {
120 0         0 $self->{error} = $response->as_string();
121             }
122 3         25 $kernel->yield( '_recent', 'http' );
123 3         232 return;
124             }
125              
126             sub _process_ftp {
127 2     2   566 my ($kernel,$self) = @_[KERNEL,OBJECT];
128 2 100       14 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         17 path => File::Spec::Unix->catfile( $self->{uri}->path, @path ),
135             );
136 2         10 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   377363 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
155 2834         6727 $data =~ s![\x0D\x0A]+$!!g;
156 2834 100       6772 if ( $self->{rss} ) {
    50          
157 127 100       542 return unless $data =~ m#(.+?)#i;
158 7 100       29 return unless $data =~ m#by-authors#i;
159 6         29 my ($link) = $data =~ m#id/(.+?)\s*$#i;
160 6 50       17 return unless $link;
161 6         12 unshift @{ $self->{recent} }, $link;
  6         22  
162             }
163             elsif ( $self->{epoch} ) {
164 0         0 push @{ $self->{recent} }, $data;
  0         0  
165             }
166             else {
167 2707 100       9299 return unless $data =~ /^authors/i;
168 1297 100       4757 return unless $data =~ /\.(tar\.gz|tgz|tar\.bz2|zip)$/;
169 397         1194 $data =~ s!authors/id/!!;
170 397         672 push @{ $self->{recent} }, $data;
  397         935  
171             }
172 403         1005 return;
173             }
174              
175             sub _get_done {
176 2     2   1114 my ($kernel,$self,$sender) = @_[KERNEL,OBJECT,SENDER];
177 2         12 $kernel->yield( '_recent', 'ftp' );
178 2         139 return;
179             }
180              
181             sub _process_file {
182 1     1   432 my ($kernel,$self) = @_[KERNEL,OBJECT];
183 1         3 delete $self->{_epoch_fail};
184             {
185 1         11 my @segs = $self->{uri}->path_segments;
  1         27  
186 1 50       122 pop @segs unless $segs[-1];
187 1         3 push @segs, 'RECENT';
188 1         4 $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         149 ProgramArgs => [ $self->{uri}->file ],
200             StdoutEvent => 'ftp_data',
201             );
202 1         5168 $kernel->sig_child( $self->{wheel}->PID(), '_sig_child' );
203 1         485 return;
204             }
205              
206             sub _epoch {
207 1     1   282 my ($kernel,$self) = @_[KERNEL,OBJECT];
208 1         520 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         18509 ProgramArgs => [ $self->{epoch}, $self->{uri}->as_string ],
217             StdoutEvent => 'ftp_data',
218             StderrEvent => '_epoch_fail',
219             );
220 1         5708 $kernel->sig_child( $self->{wheel}->PID(), '_sig_child' );
221 1         491 return;
222             }
223              
224             sub _epoch_fail {
225 2     2   14707 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
226             # Anything on STDERR means an error
227 2 100       10 return if $self->{_epoch_fail};
228 1         16 $self->{_epoch_fail} = 1;
229 1         17 $kernel->yield( '_process_' . $self->{uri}->scheme );
230 1         127 return;
231             }
232              
233             sub _sig_child {
234 2     2   1226 my ($kernel,$self) = @_[KERNEL,OBJECT];
235 2         39 delete $self->{wheel};
236 2 100       838 $kernel->yield( '_recent', 'file' ) unless $self->{_epoch_fail};
237 2         129 $kernel->sig_handled();
238             }
239              
240             qq[What's the road on the street?];
241              
242             __END__