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