File Coverage

blib/lib/App/CPAN/Fresh.pm
Criterion Covered Total %
statement 27 105 25.7
branch 0 40 0.0
condition 0 9 0.0
subroutine 9 21 42.8
pod 3 12 25.0
total 39 187 20.8


line stmt bran cond sub pod time code
1             package App::CPAN::Fresh;
2              
3 1     1   6 use strict;
  1         2  
  1         230  
4 1     1   33 use 5.008_001;
  1         4  
  1         60  
5             our $VERSION = '0.11';
6              
7 1     1   6 use base qw(App::Cmd::Simple);
  1         3  
  1         68391  
8              
9 1     1   209287 use Carp;
  1         3  
  1         71  
10 1     1   3169 use Time::Piece;
  1         32314  
  1         8  
11 1     1   1538 use File::Temp;
  1         26481  
  1         83  
12 1     1   1035 use JSON;
  1         25439  
  1         7  
13 1     1   1327 use LWP::UserAgent;
  1         54539  
  1         37  
14 1     1   10 use URI;
  1         2  
  1         2615  
15              
16             my $duration = 2 * 24 * 60 * 60;
17              
18             sub opt_spec {
19             return (
20 0     0 1   [ "install|i", "install the module" ],
21             [ "list|l", "list the recent uploads" ],
22             [ "test|t", "test the dist" ],
23             [ "force|f", "force install" ],
24             [ "devel|d", "install even if it's a devel release" ],
25             [ "minus|m", "use cpanminus" ],
26             [ "help|h", "displays usage info" ],
27             );
28             }
29              
30             sub execute {
31 0     0 1   my($self, $opt, $args) = @_;
32              
33 0 0 0       if ($opt->{list} && $args->[0]) {
    0 0        
    0          
34 0           $self->search($args->[0]);
35             } elsif ($opt->{list}) {
36 0           $self->recent;
37             } elsif ($opt->{help} || !@$args) {
38 0           $self->usage;
39             } else {
40 0           $self->handle($opt, $args);
41             }
42             }
43              
44             sub recent {
45 0     0 0   my $self = shift;
46 0           my $res = $self->call("/feed/cpan");
47 0           $self->display_results($res);
48             }
49              
50             sub search {
51 0     0 0   my($self, $q) = @_;
52 0           my $res = $self->call("/search", { q => "$q group:cpan" });
53 0           $self->display_results($res);
54             }
55              
56             sub usage {
57 0     0 1   require Pod::Usage;
58 0           Pod::Usage::pod2usage(0);
59             }
60              
61             sub handle {
62 0     0 0   my($self, $opt, $dists) = @_;
63              
64 0           my @install;
65 0           for my $dist (@$dists) {
66 0           my $path = $self->inject($dist, $opt);
67 0 0         if ($path) {
68 0           push @install, $path;
69             } else {
70 0           print "$dist is not found or not in the fresh uploads. Falling back to your mirror.\n";
71 0           push @install, $dist;
72             }
73             }
74              
75 0           my $method = "install";
76 0 0         $method = "test" if $opt->{test};
77              
78 0 0         if (@install) {
79 0 0         if ($opt->{minus}) {
80 0 0         system "cpanm", ($opt->{force} ? "-f" : ()), @install;
81             } else {
82 0           require CPAN;
83 0 0         if ($opt->{force}) {
84 0           CPAN::Shell->force($method, @install);
85             } else {
86 0           CPAN::Shell->$method(@install);
87             }
88             }
89             }
90             }
91              
92             sub inject {
93 0     0 0   my($self, $dist, $opt) = @_;
94 0           $dist =~ s/::/-/g;
95              
96 0           for my $method ([ "/feed/cpan" ], [ "/search", { q => "$dist group:cpan" } ]) {
97 0           my $res = $self->call($method->[0], $method->[1]);
98 0           for my $entry (@{$res->{entries}}) {
  0            
99 0 0         my $info = $self->parse_entry($entry->{body}, $entry->{date}) or next;
100 0 0         if ($info->{dist} eq $dist) {
101 0 0 0       if ($info->{version} =~ /_|-TRIAL/ && !$opt->{devel}) {
102 0           warn "$info->{dist}-$info->{version} found: No -d option, skipping\n";
103 0           return;
104             }
105 0           return $self->do_inject($info, $opt);
106             }
107             }
108             }
109              
110 0           return;
111             }
112              
113             sub do_inject {
114 0     0 0   my($self, $info, $opt) = @_;
115              
116 0 0         if ($opt->{minus}) {
117 0           return $info->{url};
118             }
119              
120 0           my $dir = File::Temp::tempdir(CLEANUP => 1);
121 0           my $local = "$dir/$info->{dist}-$info->{version}.tar.gz";
122              
123 0           print "Fetching $info->{url}\n";
124 0           my $res = $self->new_ua->mirror($info->{url}, $local);
125 0 0         if ($res->is_error) {
126 0           croak "Fetching $info->{url} failed: ", $res->status_line;
127             }
128              
129 0           require CPAN::Inject;
130 0           CPAN::Inject->from_cpan_config->add(file => $local);
131             }
132              
133             sub display_results {
134 0     0 0   my($self, $res) = @_;
135 0           for my $entry (@{$res->{entries}}) {
  0            
136 0 0         my $info = $self->parse_entry($entry->{body}, $entry->{date}) or next;
137 0           printf "%s-%s (%s)\n", $info->{dist}, $info->{version}, $info->{author};
138             }
139             }
140              
141             sub parse_entry {
142 0     0 0   my($self, $body, $date) = @_;
143              
144 0 0         my $time = Time::Piece->strptime($date, "%Y-%m-%dT%H:%M:%SZ") or return;
145 0 0         if (time - $time->epoch > $duration) {
146             # entry found, but it's old
147 0           return;
148             }
149              
150 0 0         if ($body =~ /^([\w\-]+) (v?[0-9\._]*(?:-TRIAL)?) by (.+?) -
151             return {
152 0           dist => $1,
153             version => $2,
154             author => $3,
155             url => $4,
156             };
157             }
158              
159 0           return;
160             }
161              
162             sub new_ua {
163 0     0 0   my $self = shift;
164 0           LWP::UserAgent->new(agent => "cpanf/$VERSION", env_proxy => 1);
165             }
166              
167             sub call {
168 0     0 0   my($self, $method, $opts) = @_;
169              
170 0           my $uri = URI->new("http://friendfeed-api.com/v2$method");
171 0 0         $uri->query_form(%$opts) if $opts;
172              
173 0           my $ua = $self->new_ua;
174 0           my $res = $ua->get($uri);
175              
176 0 0         if ($res->is_error) {
177 0           croak "HTTP error: ", $res->status_line;
178             }
179              
180 0           JSON::decode_json($res->content);
181             }
182              
183             1;
184             __END__