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.50';
3             #ABSTRACT: A POE component to retrieve recent CPAN uploads.
4              
5 6     6   428790 use strict;
  6         8  
  6         145  
6 6     6   21 use warnings;
  6         9  
  6         143  
7 6     6   20 use Carp;
  6         6  
  6         303  
8 6     6   22 use POE qw(Component::SmokeBox::Recent::HTTP Component::SmokeBox::Recent::FTP Wheel::Run);
  6         5  
  6         49  
9 6     6   74983 use URI;
  6         9  
  6         100  
10 6     6   18 use HTTP::Request;
  6         6  
  6         87  
11 6     6   17 use File::Spec;
  6         5  
  6         8492  
12              
13             sub recent {
14 6     6 1 11086 my $package = shift;
15 6         24 my %opts = @_;
16 6         52 $opts{lc $_} = delete $opts{$_} for keys %opts;
17 6 50       86 croak "$package requires a 'url' argument\n" unless $opts{url};
18 6 50       26 croak "$package requires an 'event' argument\n" unless $opts{event};
19 6 100       35 $opts{rss} = 0 unless $opts{rss};
20 6         13 my $options = delete $opts{options};
21 6         13 my $self = bless \%opts, $package;
22 6         35 $self->{recent} = [];
23 6         48 $self->{uri} = URI->new( $self->{url} );
24             croak "url provided is of an unsupported scheme\n"
25 6 50 33     30238 unless $self->{uri}->scheme and $self->{uri}->scheme =~ /^(ht|f)tp|file$/;
26 6 50       597 $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         432 return $self;
42             }
43              
44             sub _start {
45 6     6   1568 my ($kernel,$sender,$self) = @_[KERNEL,SENDER,OBJECT];
46 6         49 $self->{session_id} = $_[SESSION]->ID();
47 6 50 33     49 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         7 my $sender_id;
51 6 50       20 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         17 $sender_id = $sender->ID();
61             }
62 6         32 $kernel->refcount_increment( $sender_id, __PACKAGE__ );
63 6         137 $kernel->detach_myself();
64 6         404 $self->{sender_id} = $sender_id;
65 6 100       25 if ( $self->{epoch} ) {
66 1         5 $kernel->yield( '_epoch' );
67 1         52 return;
68             }
69 5         20 $kernel->yield( '_process_' . $self->{uri}->scheme );
70 5         325 return;
71             }
72              
73             sub _recent {
74 6     6   1257 my ($kernel,$self,$type) = @_[KERNEL,OBJECT,ARG0];
75 6         19 my $target = delete $self->{sender_id};
76 6         10 my %reply;
77 6 50       52 $reply{recent} = delete $self->{recent} if $self->{recent};
78 6 50       20 $reply{error} = delete $self->{error} if $self->{error};
79 6 50       26 $reply{context} = delete $self->{context} if $self->{context};
80 6         17 $reply{url} = delete $self->{url};
81 6         9 @{ $reply{recent} } = grep { my @parts = split m!/!; $parts[3] !~ m!^perl6$!i } @{ $reply{recent} };
  6         72  
  420         475  
  420         514  
  6         21  
82 6         26 my $event = delete $self->{event};
83 6         58 $kernel->post( $target, $event, \%reply );
84 6         336 $kernel->refcount_decrement( $target, __PACKAGE__ );
85 6         138 return;
86             }
87              
88             sub _process_http {
89 3     3   348 my ($kernel,$self) = @_[KERNEL,OBJECT];
90 3 100       16 my @path = $self->{rss} ? ( 'modules', '01modules.mtime.rss' ) : ( 'RECENT' );
91 3         28 $self->{uri}->path( File::Spec::Unix->catfile( $self->{uri}->path(), @path ) );
92             POE::Component::SmokeBox::Recent::HTTP->spawn(
93             uri => $self->{uri},
94 3         213 );
95 3         8 return;
96             }
97              
98             sub _http_response {
99 3     3   1844 my ($kernel,$self,$response) = @_[KERNEL,OBJECT,ARG0];
100 3 50       11 if ( $response->code() == 200 ) {
101 3 100       35 if ( $self->{rss} ) {
102 1         4 for ( split /\n/, $response->content() ) {
103 63 100       105 next unless m#(.+?)#i;
104 7 100       12 next unless m#by-authors#i;
105 6         15 my ($link) = $_ =~ m#id/(.+?)\s*$#i;
106 6 50       9 next unless $link;
107 6         2 unshift @{ $self->{recent} }, $link;
  6         11  
108             }
109             }
110             else {
111 2         7 for ( split /\n/, $response->content() ) {
112 60 100       124 next unless /^authors/;
113 54 100       101 next unless /\.(tar\.gz|tgz|tar\.bz2|zip)$/;
114 11         23 s!authors/id/!!;
115 11         10 push @{ $self->{recent} }, $_;
  11         18  
116             }
117             }
118             }
119             else {
120 0         0 $self->{error} = $response->as_string();
121             }
122 3         16 $kernel->yield( '_recent', 'http' );
123 3         136 return;
124             }
125              
126             sub _process_ftp {
127 2     2   290 my ($kernel,$self) = @_[KERNEL,OBJECT];
128 2 100       9 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         15 path => File::Spec::Unix->catfile( $self->{uri}->path, @path ),
135             );
136 2         6 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   191003 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
155 2834         3446 $data =~ s![\x0D\x0A]+$!!g;
156 2834 100       4116 if ( $self->{rss} ) {
    50          
157 127 100       447 return unless $data =~ m#(.+?)#i;
158 7 100       20 return unless $data =~ m#by-authors#i;
159 6         23 my ($link) = $data =~ m#id/(.+?)\s*$#i;
160 6 50       13 return unless $link;
161 6         5 unshift @{ $self->{recent} }, $link;
  6         16  
162             }
163             elsif ( $self->{epoch} ) {
164 0         0 push @{ $self->{recent} }, $data;
  0         0  
165             }
166             else {
167 2707 100       6708 return unless $data =~ /^authors/i;
168 1297 100       3288 return unless $data =~ /\.(tar\.gz|tgz|tar\.bz2|zip)$/;
169 397         690 $data =~ s!authors/id/!!;
170 397         281 push @{ $self->{recent} }, $data;
  397         590  
171             }
172 403         614 return;
173             }
174              
175             sub _get_done {
176 2     2   591 my ($kernel,$self,$sender) = @_[KERNEL,OBJECT,SENDER];
177 2         6 $kernel->yield( '_recent', 'ftp' );
178 2         69 return;
179             }
180              
181             sub _process_file {
182 1     1   201 my ($kernel,$self) = @_[KERNEL,OBJECT];
183 1         1 delete $self->{_epoch_fail};
184             {
185 1         1 my @segs = $self->{uri}->path_segments;
  1         11  
186 1 50       56 pop @segs unless $segs[-1];
187 1         1 push @segs, 'RECENT';
188 1         3 $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         93 ProgramArgs => [ $self->{uri}->file ],
200             StdoutEvent => 'ftp_data',
201             );
202 1         2599 $kernel->sig_child( $self->{wheel}->PID(), '_sig_child' );
203 1         193 return;
204             }
205              
206             sub _epoch {
207 1     1   166 my ($kernel,$self) = @_[KERNEL,OBJECT];
208 1         549 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         10933 ProgramArgs => [ $self->{epoch}, $self->{uri}->as_string ],
217             StdoutEvent => 'ftp_data',
218             StderrEvent => '_epoch_fail',
219             );
220 1         3186 $kernel->sig_child( $self->{wheel}->PID(), '_sig_child' );
221 1         235 return;
222             }
223              
224             sub _epoch_fail {
225 2     2   51891 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
226             # Anything on STDERR means an error
227 2 100       9 return if $self->{_epoch_fail};
228 1         10 $self->{_epoch_fail} = 1;
229 1         8 $kernel->yield( '_process_' . $self->{uri}->scheme );
230 1         60 return;
231             }
232              
233             sub _sig_child {
234 2     2   3079 my ($kernel,$self) = @_[KERNEL,OBJECT];
235 2         22 delete $self->{wheel};
236 2 100       472 $kernel->yield( '_recent', 'file' ) unless $self->{_epoch_fail};
237 2         54 $kernel->sig_handled();
238             }
239              
240             qq[What's the road on the street?];
241              
242             __END__