line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CPAN::Access::AdHoc; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
142905
|
use 5.008; |
|
4
|
|
|
|
|
15
|
|
|
4
|
|
|
|
|
151
|
|
4
|
|
|
|
|
|
|
|
5
|
4
|
|
|
4
|
|
18
|
use strict; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
108
|
|
6
|
4
|
|
|
4
|
|
25
|
use warnings; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
100
|
|
7
|
|
|
|
|
|
|
|
8
|
4
|
|
|
4
|
|
1384
|
use Config::Tiny (); |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
63
|
|
9
|
4
|
|
|
4
|
|
1694
|
use CPAN::Access::AdHoc::Archive; |
|
4
|
|
|
|
|
14
|
|
|
4
|
|
|
|
|
157
|
|
10
|
4
|
|
|
|
|
647
|
use CPAN::Access::AdHoc::Util qw{ |
11
|
|
|
|
|
|
|
:carp __attr __cache __expand_distribution_path __guess_media_type |
12
|
4
|
|
|
4
|
|
27
|
}; |
|
4
|
|
|
|
|
9
|
|
13
|
4
|
|
|
4
|
|
4357
|
use Digest::SHA (); |
|
4
|
|
|
|
|
17265
|
|
|
4
|
|
|
|
|
97
|
|
14
|
4
|
|
|
4
|
|
1565
|
use File::HomeDir (); |
|
4
|
|
|
|
|
81
|
|
|
4
|
|
|
|
|
72
|
|
15
|
4
|
|
|
4
|
|
25
|
use File::Spec (); |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
53
|
|
16
|
4
|
|
|
4
|
|
21
|
use IO::File (); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
61
|
|
17
|
4
|
|
|
4
|
|
4090
|
use LWP::UserAgent (); |
|
4
|
|
|
|
|
63044
|
|
|
4
|
|
|
|
|
100
|
|
18
|
4
|
|
|
4
|
|
42
|
use LWP::Protocol (); |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
65
|
|
19
|
4
|
|
|
4
|
|
23
|
use Module::Pluggable::Object; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
91
|
|
20
|
4
|
|
|
4
|
|
11750
|
use Safe; |
|
4
|
|
|
|
|
166510
|
|
|
4
|
|
|
|
|
241
|
|
21
|
4
|
|
|
4
|
|
47
|
use Scalar::Util qw{ blessed }; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
257
|
|
22
|
4
|
|
|
4
|
|
3166
|
use Text::ParseWords (); |
|
4
|
|
|
|
|
4943
|
|
|
4
|
|
|
|
|
88
|
|
23
|
4
|
|
|
4
|
|
29
|
use URI (); |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
5796
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our $VERSION = '0.000_18'; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# In the following list of attribute names, 'config' must be first |
28
|
|
|
|
|
|
|
# because it supplies default values for everything else. 'cpan' must be |
29
|
|
|
|
|
|
|
# after 'default_cpan_source' because 'default_cpan_source' determines |
30
|
|
|
|
|
|
|
# how the default value of 'cpan' is computed. |
31
|
|
|
|
|
|
|
my @attributes = qw{ |
32
|
|
|
|
|
|
|
config __debug http_error_handler default_cpan_source cpan |
33
|
|
|
|
|
|
|
}; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub new { |
36
|
12
|
|
|
12
|
1
|
5948
|
my ( $class, %arg ) = @_; |
37
|
|
|
|
|
|
|
|
38
|
12
|
|
33
|
|
|
103
|
my $self = bless {}, ref $class || $class; |
39
|
|
|
|
|
|
|
|
40
|
12
|
|
|
|
|
51
|
$self->__init( \%arg ); |
41
|
|
|
|
|
|
|
|
42
|
12
|
100
|
|
|
|
50
|
%arg |
43
|
|
|
|
|
|
|
and __wail( 'Unknown attribute(s): ', join ', ', sort keys %arg ); |
44
|
|
|
|
|
|
|
|
45
|
11
|
|
|
|
|
44
|
return $self; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub __init { |
49
|
12
|
|
|
12
|
|
23
|
my ( $self, $arg ) = @_; |
50
|
|
|
|
|
|
|
|
51
|
12
|
|
|
|
|
36
|
foreach my $name ( @attributes ) { |
52
|
60
|
|
|
|
|
224
|
$self->$name( delete $arg->{$name} ); |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
12
|
|
|
|
|
27
|
return $self; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub corpus { |
59
|
1
|
|
|
1
|
1
|
374
|
my ( $self, $cpan_id ) = @_; |
60
|
1
|
|
|
|
|
3
|
$cpan_id = uc $cpan_id; |
61
|
|
|
|
|
|
|
|
62
|
1
|
|
|
|
|
4
|
my $re = join '/', |
63
|
|
|
|
|
|
|
substr( $cpan_id, 0, 1 ), |
64
|
|
|
|
|
|
|
substr( $cpan_id, 0, 2 ), |
65
|
|
|
|
|
|
|
$cpan_id; |
66
|
|
|
|
|
|
|
|
67
|
1
|
|
|
|
|
21
|
$re = qr{ \A \Q$re\E / }smx; |
68
|
1
|
|
|
|
|
6
|
return ( grep { $_ =~ $re } $self->indexed_distributions() ); |
|
3
|
|
|
|
|
16
|
|
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub fetch { |
72
|
23
|
|
|
23
|
1
|
2587
|
my ( $self, $path ) = @_; |
73
|
|
|
|
|
|
|
|
74
|
23
|
|
|
|
|
57
|
$path =~ s{ \A / }{}smx; |
75
|
|
|
|
|
|
|
|
76
|
23
|
|
|
|
|
174
|
my $ua = LWP::UserAgent->new(); |
77
|
|
|
|
|
|
|
|
78
|
23
|
|
|
|
|
10464
|
my $url = $self->cpan() . $path; |
79
|
|
|
|
|
|
|
|
80
|
23
|
|
|
|
|
220
|
my $rslt = $ua->get( $url ); |
81
|
|
|
|
|
|
|
|
82
|
23
|
100
|
|
|
|
35284
|
$rslt->is_success |
83
|
|
|
|
|
|
|
or return $self->http_error_handler()->( $self, $path, $rslt ); |
84
|
|
|
|
|
|
|
|
85
|
17
|
|
|
|
|
204
|
__guess_media_type( $rslt, $path ); |
86
|
|
|
|
|
|
|
|
87
|
17
|
|
|
|
|
76
|
$self->_checksum( $rslt ); |
88
|
|
|
|
|
|
|
|
89
|
17
|
100
|
|
|
|
139
|
my $archive = |
90
|
|
|
|
|
|
|
CPAN::Access::AdHoc::Archive->__handle_http_response( $rslt ) |
91
|
|
|
|
|
|
|
or __wail( sprintf q{Unsupported Content-Type '%s'}, |
92
|
|
|
|
|
|
|
$rslt->header( 'Content-Type' ) ); |
93
|
|
|
|
|
|
|
|
94
|
16
|
|
|
|
|
596
|
return $archive; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub fetch_author_index { |
98
|
2
|
|
|
2
|
1
|
2642
|
my ( $self ) = @_; |
99
|
|
|
|
|
|
|
|
100
|
2
|
|
|
|
|
12
|
my $cache = $self->__cache(); |
101
|
2
|
50
|
|
|
|
9
|
exists $cache->{author_index} |
102
|
|
|
|
|
|
|
and return $cache->{author_index}; |
103
|
|
|
|
|
|
|
|
104
|
2
|
|
|
|
|
7
|
my $author_details = $self->fetch( 'authors/01mailrc.txt.gz' ); |
105
|
2
|
50
|
|
|
|
12
|
_got_archive( $author_details ) |
106
|
|
|
|
|
|
|
or return $author_details; |
107
|
2
|
|
|
|
|
11
|
$author_details = $author_details->get_item_content(); |
108
|
|
|
|
|
|
|
|
109
|
2
|
100
|
|
|
|
31
|
my $fh = IO::File->new( \$author_details, '<' ) |
110
|
|
|
|
|
|
|
or __wail( "Unable to open string reference: $!" ); |
111
|
|
|
|
|
|
|
|
112
|
1
|
|
|
|
|
67
|
my %author_index; |
113
|
1
|
|
|
|
|
8
|
while ( <$fh> ) { |
114
|
2
|
|
|
|
|
15
|
s/ \s+ \z //smx; |
115
|
2
|
|
|
|
|
17
|
my ( undef, $cpan_id, $address ) = Text::ParseWords::parse_line( |
116
|
|
|
|
|
|
|
qr{ \s+ }smx, 0, $_ ); |
117
|
2
|
|
|
|
|
279
|
( my $name = $address ) =~ s{ \s+ < (.*) > }{}smx; |
118
|
2
|
|
|
|
|
4
|
my $mail_addr = $1; |
119
|
2
|
|
|
|
|
16
|
$author_index{ uc $cpan_id } = { |
120
|
|
|
|
|
|
|
name => $name, |
121
|
|
|
|
|
|
|
address => $mail_addr, |
122
|
|
|
|
|
|
|
}; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
1
|
|
|
|
|
9
|
return ( $cache->{author_index} = \%author_index ); |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub fetch_distribution_archive { |
129
|
4
|
|
|
4
|
1
|
3103
|
my ( $self, $distribution ) = @_; |
130
|
4
|
|
|
|
|
23
|
my $path = __expand_distribution_path( $distribution ); |
131
|
4
|
|
|
|
|
26
|
return $self->fetch( "authors/id/$path" ); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub fetch_distribution_checksums { |
135
|
8
|
|
|
8
|
1
|
538
|
my ( $self, $distribution ) = @_; |
136
|
|
|
|
|
|
|
|
137
|
8
|
100
|
|
|
|
65
|
$distribution =~ m{ \A ( .* / ) ( [^/]* ) \z }smx |
138
|
|
|
|
|
|
|
or __wail( "Invalid distribution '$distribution'" ); |
139
|
7
|
|
|
|
|
28
|
my ( $dir, $file ) = ( $1, $2 ); |
140
|
|
|
|
|
|
|
|
141
|
7
|
50
|
|
|
|
25
|
$file eq 'CHECKSUMS' |
142
|
|
|
|
|
|
|
and $file = ''; |
143
|
7
|
|
|
|
|
43
|
my $path = __expand_distribution_path( $dir . 'CHECKSUMS' ); |
144
|
7
|
|
|
|
|
50
|
( $dir = $path ) =~ s{ [^/]* \z }{}smx; |
145
|
|
|
|
|
|
|
|
146
|
7
|
|
|
|
|
34
|
my $cache = $self->__cache(); |
147
|
|
|
|
|
|
|
|
148
|
7
|
100
|
|
|
|
32
|
if ( ! $cache->{checksums}{$dir} ) { |
149
|
4
|
|
|
|
|
25
|
my $archive = $self->fetch( "authors/id/$path" ); |
150
|
4
|
50
|
|
|
|
20
|
_got_archive( $archive ) |
151
|
|
|
|
|
|
|
or return $archive; |
152
|
4
|
|
|
|
|
19
|
$cache->{checksums}{$dir} = _eval_string( |
153
|
|
|
|
|
|
|
$archive->get_item_content() ); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
7
|
100
|
|
|
|
432
|
$file eq '' |
157
|
|
|
|
|
|
|
and return $cache->{checksums}{$dir}; |
158
|
6
|
|
|
|
|
40
|
return $cache->{checksums}{$dir}{$file}; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# TODO finish implementing error handling. See above, _got_archive(). |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub fetch_module_index { |
164
|
5
|
|
|
5
|
1
|
1184
|
my ( $self ) = @_; |
165
|
|
|
|
|
|
|
|
166
|
5
|
|
|
|
|
25
|
my $cache = $self->__cache(); |
167
|
|
|
|
|
|
|
|
168
|
0
|
|
|
|
|
0
|
exists $cache->{module_index} |
169
|
|
|
|
|
|
|
and return wantarray ? |
170
|
5
|
50
|
|
|
|
29
|
@{ $cache->{module_index} } : |
|
|
100
|
|
|
|
|
|
171
|
|
|
|
|
|
|
$cache->{module_index}[0]; |
172
|
|
|
|
|
|
|
|
173
|
4
|
|
|
|
|
11
|
my ( $meta, %module ); |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# The only way this can return undef is if the http_error_handler |
176
|
|
|
|
|
|
|
# returns it. We take that as a request to cache an empty index. |
177
|
4
|
100
|
|
|
|
13
|
if ( my $packages_details = $self->fetch( |
178
|
|
|
|
|
|
|
'modules/02packages.details.txt.gz' ) ) { |
179
|
2
|
|
|
|
|
10
|
$packages_details = $packages_details->get_item_content(); |
180
|
|
|
|
|
|
|
|
181
|
2
|
100
|
|
|
|
18
|
my $fh = IO::File->new( \$packages_details, '<' ) |
182
|
|
|
|
|
|
|
or __wail( "Unable to open string reference: $!" ); |
183
|
|
|
|
|
|
|
|
184
|
1
|
|
|
|
|
1482
|
$meta = $self->_read_meta( $fh ); |
185
|
|
|
|
|
|
|
|
186
|
1
|
|
|
|
|
6
|
while ( <$fh> ) { |
187
|
3
|
|
|
|
|
5
|
chomp; |
188
|
3
|
|
|
|
|
16
|
my ( $mod, @info ) = split qr{ \s+ }smx; |
189
|
|
|
|
|
|
|
## 'undef' eq $ver |
190
|
|
|
|
|
|
|
## and $ver = undef; |
191
|
3
|
|
|
|
|
7
|
my ( $pkg, $ver ) = reverse @info; |
192
|
3
|
50
|
|
|
|
8
|
defined $ver or $ver = 'undef'; |
193
|
3
|
|
|
|
|
22
|
$module{$mod} = { |
194
|
|
|
|
|
|
|
distribution => $pkg, |
195
|
|
|
|
|
|
|
version => $ver, |
196
|
|
|
|
|
|
|
}; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
} else { |
200
|
1
|
|
|
|
|
55
|
$meta = {}; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
2
|
|
|
|
|
14
|
$cache->{module_index} = [ \%module, $meta ]; |
204
|
|
|
|
|
|
|
|
205
|
2
|
100
|
|
|
|
22
|
return wantarray ? ( \%module, $meta ) : \%module; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub fetch_registered_module_index { |
209
|
2
|
|
|
2
|
1
|
1714
|
my ( $self ) = @_; |
210
|
|
|
|
|
|
|
|
211
|
2
|
|
|
|
|
13
|
my $cache = $self->__cache(); |
212
|
0
|
|
|
|
|
0
|
exists $cache->{registered_module_index} |
213
|
|
|
|
|
|
|
and return wantarray ? |
214
|
2
|
0
|
|
|
|
10
|
@{ $cache->{registered_module_index} } : |
|
|
50
|
|
|
|
|
|
215
|
|
|
|
|
|
|
$cache->{registered_module_index}[0]; |
216
|
|
|
|
|
|
|
|
217
|
2
|
|
|
|
|
13
|
my $packages_details = $self->fetch( |
218
|
|
|
|
|
|
|
'modules/03modlist.data.gz' |
219
|
|
|
|
|
|
|
)->get_item_content(); |
220
|
|
|
|
|
|
|
|
221
|
2
|
|
|
|
|
10
|
my ( $meta, $reg ); |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
{ |
224
|
|
|
|
|
|
|
|
225
|
2
|
100
|
|
|
|
4
|
my $fh = IO::File->new( \$packages_details, '<' ) |
|
2
|
|
|
|
|
18
|
|
226
|
|
|
|
|
|
|
or __wail( "Unable to open string reference: $!" ); |
227
|
|
|
|
|
|
|
|
228
|
1
|
|
|
|
|
61
|
$meta = $self->_read_meta( $fh ); |
229
|
|
|
|
|
|
|
|
230
|
1
|
|
|
|
|
4
|
local $/ = undef; |
231
|
1
|
|
|
|
|
10
|
$reg = <$fh>; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
1
|
|
|
|
|
7
|
my $hash = _eval_string( "$reg\nCPAN::Modulelist->data();" ); |
235
|
|
|
|
|
|
|
|
236
|
1
|
|
|
|
|
109
|
$cache->{registered_module_index} = [ $hash, $meta ]; |
237
|
|
|
|
|
|
|
|
238
|
1
|
50
|
|
|
|
8
|
return wantarray ? ( $hash, $meta ) : $hash; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub flush { |
242
|
13
|
|
|
13
|
1
|
25
|
my ( $self ) = @_; |
243
|
13
|
|
|
|
|
31
|
delete $self->{'.cache'}; |
244
|
13
|
|
|
|
|
25
|
return $self; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub indexed_distributions { |
248
|
2
|
|
|
2
|
1
|
4
|
my ( $self ) = @_; |
249
|
|
|
|
|
|
|
|
250
|
2
|
|
|
|
|
8
|
my $cache = $self->__cache(); |
251
|
|
|
|
|
|
|
|
252
|
1
|
|
|
|
|
8
|
$cache->{indexed_distributions} |
253
|
2
|
100
|
|
|
|
8
|
and return @{ $cache->{indexed_distributions} }; |
254
|
|
|
|
|
|
|
|
255
|
1
|
|
|
|
|
5
|
my $inx = $self->fetch_module_index(); |
256
|
|
|
|
|
|
|
|
257
|
1
|
|
|
|
|
3
|
my %pkg; |
258
|
1
|
|
|
|
|
2
|
foreach my $info ( values %{ $inx } ) { |
|
1
|
|
|
|
|
4
|
|
259
|
3
|
|
|
|
|
10
|
$pkg{$info->{distribution}}++; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
1
|
|
|
|
|
2
|
return @{ $cache->{indexed_distributions} = [ sort keys %pkg ] }; |
|
1
|
|
|
|
|
10
|
|
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# Set up the accessor/mutators. All mutators interpret undef as being a |
266
|
|
|
|
|
|
|
# request to restore the default, from the configuration if that exists, |
267
|
|
|
|
|
|
|
# or from the configured default code. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
__PACKAGE__->__create_accessor_mutators( @attributes ); |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub _create_accessor_mutator_helper { |
272
|
40
|
|
|
40
|
|
58
|
my ( $class, $name, $code ) = @_; |
273
|
40
|
100
|
|
|
|
246
|
$class->can( $name ) |
274
|
|
|
|
|
|
|
and return; |
275
|
20
|
|
|
|
|
37
|
my $full_name = "${class}::$name"; |
276
|
4
|
|
|
4
|
|
28
|
no strict qw{ refs }; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
611
|
|
277
|
20
|
|
|
|
|
48
|
*$full_name = $code; |
278
|
20
|
|
|
|
|
31
|
return; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub __create_accessor_mutators { |
282
|
4
|
|
|
4
|
|
48
|
my ( $class, @attrs ) = @_; |
283
|
4
|
|
|
|
|
17
|
foreach my $name ( @attrs ) { |
284
|
20
|
50
|
|
|
|
168
|
$class->can( $name ) and next; |
285
|
20
|
|
|
|
|
44
|
my $full_name = "${class}::$name"; |
286
|
|
|
|
|
|
|
$class->_create_accessor_mutator_helper( |
287
|
20
|
|
|
12
|
|
96
|
"__attr__${name}__validate" => sub { return $_[1] } ); |
|
12
|
|
|
|
|
21
|
|
288
|
|
|
|
|
|
|
$class->_create_accessor_mutator_helper( |
289
|
20
|
|
|
51
|
|
105
|
"__attr__${name}__post_assignment" => sub { return $_[1] } ); |
|
51
|
|
|
|
|
69
|
|
290
|
4
|
|
|
4
|
|
23
|
no strict qw{ refs }; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
6574
|
|
291
|
|
|
|
|
|
|
*$full_name = sub { |
292
|
170
|
|
|
170
|
|
17427
|
my ( $self, @arg ) = @_; |
293
|
170
|
|
|
|
|
668
|
my $attr = $self->__attr(); |
294
|
170
|
100
|
|
|
|
469
|
if ( @arg ) { |
295
|
66
|
|
|
|
|
85
|
my $value = $arg[0]; |
296
|
66
|
100
|
100
|
|
|
423
|
not defined $value |
297
|
|
|
|
|
|
|
and 'config' ne $name |
298
|
|
|
|
|
|
|
and $value = $self->config()->{_}{$name}; |
299
|
66
|
|
|
|
|
109
|
my $code; |
300
|
66
|
100
|
100
|
|
|
516
|
not defined $value |
301
|
|
|
|
|
|
|
and $code = $self->can( "__attr__${name}__default" ) |
302
|
|
|
|
|
|
|
and $value = $code->( $self ); |
303
|
66
|
50
|
|
|
|
483
|
$code = $self->can( "__attr__${name}__validate" ) |
304
|
|
|
|
|
|
|
and $value = $code->( $self, $value ); |
305
|
63
|
|
|
|
|
209
|
$attr->{$name} = $value; |
306
|
63
|
50
|
|
|
|
415
|
$code = $self->can( "__attr__${name}__post_assignment" ) |
307
|
|
|
|
|
|
|
and $code->( $self ); |
308
|
63
|
|
|
|
|
219
|
return $self; |
309
|
|
|
|
|
|
|
} else { |
310
|
104
|
|
|
|
|
1370
|
return $attr->{$name}; |
311
|
|
|
|
|
|
|
} |
312
|
20
|
|
|
|
|
146
|
}; |
313
|
|
|
|
|
|
|
} |
314
|
4
|
|
|
|
|
10
|
return; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
{ |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# Compute the config file's name and location. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
( my $dist = __PACKAGE__ ) =~ s{ :: }{-}smxg; |
322
|
|
|
|
|
|
|
my $config_file = $dist . '.ini'; |
323
|
|
|
|
|
|
|
my $config_dir = File::HomeDir->my_dist_config( $dist ); |
324
|
|
|
|
|
|
|
my $config_path; |
325
|
|
|
|
|
|
|
defined $config_dir |
326
|
|
|
|
|
|
|
and $config_path = File::Spec->catfile( $config_dir, $config_file ); |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
sub __attr__config__default { |
329
|
12
|
|
|
12
|
|
54
|
my ( $self ) = @_; |
330
|
12
|
50
|
33
|
|
|
254
|
defined $config_path |
331
|
|
|
|
|
|
|
and -f $config_path |
332
|
|
|
|
|
|
|
and return Config::Tiny->read( $config_path ); |
333
|
12
|
|
|
|
|
104
|
return Config::Tiny->new(); |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
sub __attr__config__validate { |
338
|
13
|
|
|
13
|
|
29
|
my ( $self, $value ) = @_; |
339
|
|
|
|
|
|
|
|
340
|
13
|
|
|
|
|
28
|
my $err = "Attribute 'config' must be a file name or a " . |
341
|
|
|
|
|
|
|
"Config::Tiny reference"; |
342
|
13
|
50
|
|
|
|
39
|
if ( ref $value ) { |
343
|
13
|
100
|
|
|
|
24
|
eval { |
344
|
13
|
|
|
|
|
139
|
$value->isa( 'Config::Tiny' ); |
345
|
|
|
|
|
|
|
} or __wail( $err ); |
346
|
|
|
|
|
|
|
} else { |
347
|
0
|
0
|
|
|
|
0
|
-f $value |
348
|
|
|
|
|
|
|
or __wail( $err ); |
349
|
0
|
|
|
|
|
0
|
$value = Config::Tiny->read( $value ); |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
12
|
|
|
|
|
46
|
delete $value->{_}{config}; |
353
|
12
|
|
|
|
|
31
|
return $value; |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# The rationale of the default order is: |
357
|
|
|
|
|
|
|
# 1) Mini cpan: guaranteed to be local, and since it is non-core, |
358
|
|
|
|
|
|
|
# the user had to install it, and can be presumed to be using it. |
359
|
|
|
|
|
|
|
# 2) CPAN minus: since it is non-core, the user had to install it, |
360
|
|
|
|
|
|
|
# and can be presumed to be using it. |
361
|
|
|
|
|
|
|
# 3) CPAN: It is core, but it needs to be set up to be used, and the |
362
|
|
|
|
|
|
|
# wrapper will detect if it has not been set up. |
363
|
|
|
|
|
|
|
# 4) CPANPLUS: It is core as of 5.10, and works out of the box, so |
364
|
|
|
|
|
|
|
# we can not presume that the user actually uses it. |
365
|
|
|
|
|
|
|
sub __attr__default_cpan_source__default { |
366
|
6
|
|
|
6
|
|
15
|
return 'CPAN::Mini,cpanm,CPAN,CPANPLUS'; |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub DEFAULT_HTTP_ERROR_HANDLER { |
370
|
3
|
|
|
3
|
0
|
7
|
my ( $self, $path, $resp ) = @_; |
371
|
3
|
|
|
|
|
18
|
my $url = $self->cpan() . $path; |
372
|
3
|
|
|
|
|
33
|
__wail( "Failed to get $url: ", $resp->status_line() ); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub __attr__http_error_handler__default { |
376
|
13
|
|
|
13
|
|
34
|
return \&DEFAULT_HTTP_ERROR_HANDLER; |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
sub __attr__http_error_handler__validate { |
380
|
15
|
|
|
15
|
|
26
|
my ( $self, $value ) = @_; |
381
|
15
|
50
|
|
|
|
48
|
'CODE' eq ref $value |
382
|
|
|
|
|
|
|
or __wail( |
383
|
|
|
|
|
|
|
q{Attribute 'http_error_handler' must be a code reference} |
384
|
|
|
|
|
|
|
); |
385
|
15
|
|
|
|
|
32
|
return $value; |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
sub __attr__cpan__post_assignment { |
389
|
12
|
|
|
12
|
|
23
|
my ( $self ) = @_; |
390
|
|
|
|
|
|
|
|
391
|
12
|
|
|
|
|
72
|
$self->flush(); |
392
|
|
|
|
|
|
|
|
393
|
12
|
|
|
|
|
20
|
return; |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
sub __attr__cpan__validate { |
397
|
13
|
|
|
13
|
|
33
|
my ( $self, $value ) = @_; |
398
|
|
|
|
|
|
|
|
399
|
13
|
|
|
|
|
141
|
$value = "$value"; # Stringify |
400
|
13
|
|
|
|
|
86
|
$value =~ s{ (?
|
401
|
|
|
|
|
|
|
|
402
|
13
|
50
|
|
|
|
67
|
my $url = URI->new( $value ) |
403
|
|
|
|
|
|
|
or _wail( "Bad URL '$value'" ); |
404
|
13
|
|
|
|
|
15381
|
$value = $url; |
405
|
|
|
|
|
|
|
|
406
|
13
|
|
|
|
|
79
|
my $scheme = $value->scheme(); |
407
|
13
|
100
|
66
|
|
|
613
|
$value->can( 'authority' ) |
408
|
|
|
|
|
|
|
and LWP::Protocol::implementor( $scheme ) |
409
|
|
|
|
|
|
|
or __wail ( "URL scheme $scheme: is unsupported" ); |
410
|
|
|
|
|
|
|
|
411
|
12
|
|
|
|
|
59661
|
return $value; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# Check the file's checksum if appropriate. |
415
|
|
|
|
|
|
|
# |
416
|
|
|
|
|
|
|
# The argument is the HTTP::Response object that contains the data to |
417
|
|
|
|
|
|
|
# check. This object is expected to have its Content-Location set to the |
418
|
|
|
|
|
|
|
# path relative to the root of the site. |
419
|
|
|
|
|
|
|
# |
420
|
|
|
|
|
|
|
# Files are not checked unless they are in authors/id/, and are not |
421
|
|
|
|
|
|
|
# named CHECKSUM. |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub _checksum { |
424
|
17
|
|
|
17
|
|
34
|
my ( $self, $rslt ) = @_; |
425
|
17
|
50
|
|
|
|
50
|
defined( my $path = $rslt->header( 'Content-Location' ) ) |
426
|
|
|
|
|
|
|
or return; |
427
|
17
|
100
|
|
|
|
560
|
$path =~ m{ \A authors/id/ ( [^/] ) / ( \1 [^/] ) / \2 }smx |
428
|
|
|
|
|
|
|
or return; |
429
|
8
|
100
|
|
|
|
45
|
$path =~ m{ /CHECKSUMS \z }smx |
430
|
|
|
|
|
|
|
and return; |
431
|
4
|
|
|
|
|
11
|
my $cks_path = $path; |
432
|
4
|
50
|
|
|
|
29
|
$cks_path =~ s{ \A authors/id/ }{}smx |
433
|
|
|
|
|
|
|
or return; |
434
|
4
|
50
|
|
|
|
18
|
my $cksum = $self->fetch_distribution_checksums( $cks_path ) |
435
|
|
|
|
|
|
|
or return; |
436
|
0
|
0
|
|
|
|
0
|
$cksum->{sha256} |
437
|
|
|
|
|
|
|
or return; |
438
|
0
|
|
|
|
|
0
|
my $got = Digest::SHA::sha256_hex( $rslt->content() ); |
439
|
0
|
0
|
|
|
|
0
|
$got eq $cksum->{sha256} |
440
|
|
|
|
|
|
|
or __wail( "Checksum failure on $path" ); |
441
|
0
|
|
|
|
|
0
|
return; |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# Expand the default_cpan_source attribute into a list of class names, |
445
|
|
|
|
|
|
|
# each implementing one of the listed defaults. |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
{ |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
my $search_path = 'CPAN::Access::AdHoc::Default::CPAN'; |
450
|
|
|
|
|
|
|
my %defaulter = map { ( |
451
|
|
|
|
|
|
|
$_ => $_, |
452
|
|
|
|
|
|
|
substr( $_, length( $search_path ) + 2 ) => $_, |
453
|
|
|
|
|
|
|
) } Module::Pluggable::Object->new( |
454
|
|
|
|
|
|
|
search_path => $search_path, |
455
|
|
|
|
|
|
|
inner => 0, |
456
|
|
|
|
|
|
|
require => 1, |
457
|
|
|
|
|
|
|
)->plugins(); |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
sub __attr__default_cpan_source__validate { |
460
|
13
|
|
|
13
|
|
26
|
my ( $self, $value ) = @_; |
461
|
|
|
|
|
|
|
|
462
|
13
|
50
|
|
|
|
247
|
ref $value |
463
|
|
|
|
|
|
|
or $value = [ split qr{ \s* , \s* }smx, $value ]; |
464
|
|
|
|
|
|
|
|
465
|
13
|
50
|
|
|
|
66
|
'ARRAY' eq ref $value |
466
|
|
|
|
|
|
|
or __wail( q{Attribute 'default_cpan_source' takes an array } . |
467
|
|
|
|
|
|
|
q{reference or a comma-delimited string} ); |
468
|
13
|
|
|
|
|
26
|
my @rslt; |
469
|
13
|
|
|
|
|
20
|
foreach my $source ( @{ $value } ) { |
|
13
|
|
|
|
|
34
|
|
470
|
31
|
100
|
|
|
|
105
|
defined( my $class = $defaulter{$source} ) |
471
|
|
|
|
|
|
|
or __wail( "Unknown default_cpan_source '$source'" ); |
472
|
30
|
|
|
|
|
66
|
push @rslt, $class; |
473
|
|
|
|
|
|
|
} |
474
|
12
|
|
|
|
|
115
|
return \@rslt; |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
# Eval a string in a sandbox, and return the result. This was cribbed |
480
|
|
|
|
|
|
|
# _very_ heavily from CPAN::Distribution CHECKSUM_check_file(). |
481
|
|
|
|
|
|
|
sub _eval_string { |
482
|
5
|
|
|
5
|
|
13
|
my ( $string ) = @_; |
483
|
5
|
|
|
|
|
106
|
$string =~ s/ \015? \012 /\n/smxg; |
484
|
5
|
|
|
|
|
40
|
my $sandbox = Safe->new(); |
485
|
5
|
|
|
|
|
5025
|
$sandbox->permit_only( ':default' ); |
486
|
5
|
|
|
|
|
49
|
my $rslt = $sandbox->reval( $string ); |
487
|
5
|
50
|
|
|
|
2777
|
$@ and __wail( $@ ); |
488
|
5
|
|
|
|
|
26
|
return $rslt; |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
# Return the argument if it is a CPAN::Access::AdHoc::Archive; otherwise |
492
|
|
|
|
|
|
|
# just return. |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
sub _got_archive { |
495
|
6
|
|
|
6
|
|
15
|
my ( $rtn ) = @_; |
496
|
6
|
50
|
33
|
|
|
101
|
blessed( $rtn ) |
497
|
|
|
|
|
|
|
and $rtn->isa( 'CPAN::Access::AdHoc::Archive' ) |
498
|
|
|
|
|
|
|
and return $rtn; |
499
|
0
|
|
|
|
|
0
|
return; |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
# Get the repository URL from the first source that actually supplies |
503
|
|
|
|
|
|
|
# it. The CPAN::Access::AdHoc::Default::CPAN plug-ins are called in the |
504
|
|
|
|
|
|
|
# order specified in the default_cpan_source attribute, and the first |
505
|
|
|
|
|
|
|
# source that actually supplies a URL is used. If that source provides a |
506
|
|
|
|
|
|
|
# file: URL, the first such is returned. Otherwise the first URL is |
507
|
|
|
|
|
|
|
# returned, whatever its scheme. If no URL can be determined, we die. |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
sub __attr__cpan__default { |
510
|
9
|
|
|
9
|
|
19
|
my ( $self ) = @_; |
511
|
|
|
|
|
|
|
|
512
|
9
|
|
|
|
|
14
|
my $url; |
513
|
|
|
|
|
|
|
|
514
|
9
|
|
|
|
|
25
|
my $debug = $self->__debug(); |
515
|
|
|
|
|
|
|
|
516
|
9
|
|
|
|
|
20
|
foreach my $class ( @{ $self->default_cpan_source() } ) { |
|
9
|
|
|
|
|
27
|
|
517
|
|
|
|
|
|
|
|
518
|
9
|
50
|
|
|
|
103
|
my @url_list = $class->get_default() |
519
|
|
|
|
|
|
|
or next; |
520
|
|
|
|
|
|
|
|
521
|
9
|
|
|
|
|
32
|
foreach ( @url_list ) { |
522
|
12
|
100
|
|
|
|
50
|
m/ \A file: /smx |
523
|
|
|
|
|
|
|
or next; |
524
|
7
|
|
|
|
|
54
|
$url = $_; |
525
|
7
|
|
|
|
|
15
|
last; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
|
528
|
9
|
100
|
|
|
|
25
|
defined $url |
529
|
|
|
|
|
|
|
or $url = $url_list[0]; |
530
|
|
|
|
|
|
|
|
531
|
9
|
50
|
|
|
|
24
|
$debug |
532
|
|
|
|
|
|
|
and warn "Debug - Default cpan '$url' from $class\n"; |
533
|
|
|
|
|
|
|
|
534
|
9
|
|
|
|
|
32
|
return $url; |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
|
537
|
0
|
|
|
|
|
0
|
__wail( 'No CPAN URL obtained from ' . $self->default_cpan_source() ); |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
# modules/02packages.details.txt.gz and modules/03modlist.data.gz have |
541
|
|
|
|
|
|
|
# metadata at the top. This metadata is organized as lines of |
542
|
|
|
|
|
|
|
# key: value |
543
|
|
|
|
|
|
|
# with the key left-justified. Lines can be wrapped, with leading |
544
|
|
|
|
|
|
|
# spaces. |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
sub _read_meta { |
547
|
2
|
|
|
2
|
|
4
|
my ( $self, $fh ) = @_; |
548
|
2
|
|
|
|
|
5
|
my %meta; |
549
|
|
|
|
|
|
|
{ |
550
|
2
|
|
|
|
|
3
|
my ( $name, $value ); |
|
2
|
|
|
|
|
4
|
|
551
|
2
|
|
|
|
|
16
|
while ( <$fh> ) { |
552
|
14
|
|
|
|
|
21
|
chomp; |
553
|
14
|
100
|
|
|
|
45
|
m/ \S /smx or last; |
554
|
12
|
100
|
|
|
|
34
|
if ( s/ \A \s+ //smx ) { |
555
|
4
|
|
|
|
|
18
|
$meta{$name} .= " $_"; |
556
|
|
|
|
|
|
|
} else { |
557
|
8
|
|
|
|
|
56
|
( $name, $value ) = split qr{ : \s* }smx, $_, 2; |
558
|
8
|
|
|
|
|
46
|
$meta{$name} = $value; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
} |
561
|
|
|
|
|
|
|
} |
562
|
2
|
|
|
|
|
6
|
return \%meta; |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
1; |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
__END__ |