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__ |