File Coverage

blib/lib/CPAN/Cache.pm
Criterion Covered Total %
statement 31 87 35.6
branch 0 32 0.0
condition 0 8 0.0
subroutine 11 22 50.0
pod 7 10 70.0
total 49 159 30.8


line stmt bran cond sub pod time code
1             package CPAN::Cache;
2              
3             =pod
4              
5             =head1 NAME
6              
7             CPAN::Cache - Abstract locally-cached logical subset of a CPAN mirror
8              
9             =head1 DESCRIPTION
10              
11             There have been any number of scripts and modules written that contain
12             as part of their functionality some form of locally stored partial
13             mirror of the CPAN dataset.
14              
15             B does the same thing, except that in addition it has the
16             feature that the downloading and storage of CPAN data is B that it
17             does, so it should not introduce any additional dependencies or bloat,
18             and should be much easier to reuse that existing modules, which generally
19             are more task-specific.
20              
21             The intent is that this module will be usable by everything that is in the
22             business of pulling modules from CPAN, storing them locally, and doing
23             something with them.
24              
25             In this way, it really does little other than mirror data from a remote
26             URI, except that B also provides some additional
27             intelligence about which files are and are not static (will never change)
28             which aren't, and is typed specifically as a mirror of CPAN, instead of
29             any other sort of mirror.
30              
31             By building this module as a seperate distribution, it is hoped we can
32             improve seperation of concerns in the CPAN-related modules and ensure
33             cleaner, smaller, and more robust tools that interact with the CPAN
34             in the most correct ways.
35              
36             =head1 METHODS
37              
38             =cut
39              
40 1     1   1679 use 5.005;
  1         4  
  1         45  
41 1     1   6 use strict;
  1         1  
  1         32  
42 1     1   13 use Carp ();
  1         2  
  1         11  
43 1     1   4 use File::Spec ();
  1         1  
  1         12  
44 1     1   4 use File::Path ();
  1         2  
  1         19  
45 1     1   887 use File::HomeDir ();
  1         6371  
  1         22  
46 1     1   778 use URI::ToDisk ();
  1         18648  
  1         30  
47 1     1   10 use Params::Util '_INSTANCE';
  1         2  
  1         45  
48 1     1   1046 use LWP::Simple ();
  1         130505  
  1         39  
49              
50 1     1   12 use vars qw{$VERSION};
  1         3  
  1         66  
51             BEGIN {
52 1     1   1107 $VERSION = '0.02';
53             }
54              
55              
56              
57              
58              
59             #####################################################################
60             # Constructor and Accessors
61              
62             =pod
63              
64             =head2 new
65              
66             my $cache = CPAN::Cache->new(
67             remote_uri => 'http://search.cpan.org/CPAN/',
68             local_dir => '/tmp/cpan',
69             );
70              
71             =cut
72              
73             sub new {
74 0     0 1   my $class = shift;
75 0           my $self = bless { @_ }, $class;
76              
77             # Apply boolean flags cleanly
78 0           $self->{verbose} = !! $self->{verbose};
79 0           $self->{readonly} = !! $self->{readonly};
80              
81             # More thorough checking for the
82 0   0       my $uri = $self->{remote_uri}
83             || 'http://search.cpan.org/CPAN/';
84 0   0       my $path = $self->{local_dir}
85             || File::Spec->catdir(
86             File::HomeDir->my_data, '.perl', 'CPAN-Cache'
87             );
88              
89             # Strip superfluous trailing slashes
90 0           $path =~ s/\/+$//;
91 0           $uri =~ s/\/+$//;
92              
93             # Create the mirror_local path if needed
94 0 0         -e $path or File::Path::mkpath($path);
95 0 0         -d $path or Carp::croak("mirror_local: Path '$path' is not a directory");
96 0 0         -w $path or Carp::croak("mirror_local: No write permissions to path '$path'");
97              
98             # Create the mirror object and save the updated values
99 0 0         $self->{_mirror} = URI::ToDisk->new( $path => $uri )
100             or Carp::croak("Unexpected error creating HTML::Location object");
101              
102 0           $self;
103             }
104              
105             =pod
106              
107             =head2 remote_uri
108              
109             The C accessor returns a L object for the remote CPAN
110             repository.
111              
112             =cut
113              
114             sub remote_uri {
115 0     0 1   $_[0]->{_mirror}->URI;
116             }
117              
118             =pod
119              
120             =head2 local_dir
121              
122             The C accessor returns the filesystem path for the root
123             root directory of the CPAN cache.
124              
125             =cut
126              
127             sub local_dir {
128 0     0 1   $_[0]->{_mirror}->path;
129             }
130              
131             # Undocumented until it is usable
132             sub trace {
133 0     0 0   $_[0]->{trace};
134             }
135              
136             # Undocumented until it is usable
137             sub verbose {
138 0     0 0   $_[0]->{verbose};
139             }
140              
141             # Undocumented until it is usable
142             sub readonly {
143 0     0 0   $_[0]->{readonly};
144             }
145              
146              
147              
148              
149              
150             #####################################################################
151             # Interface Methods
152              
153             =pod
154              
155             =head2 file path/to/file.txt
156              
157             The C method takes the path of a file within the
158             repository, and returns a L object representing
159             it's location on both the server, and on the local filesystem.
160              
161             Paths should B be provided in unix/web format, B the
162             local filesystem's format.
163              
164             Returns a L or throws an exception if passed a
165             bad path.
166              
167             =cut
168              
169             sub file {
170 0     0 1   my $self = shift;
171 0           my $path = $self->_path(shift);
172              
173             # Split into parts and find the location for it.
174 0           $self->{_mirror}->catfile( split /\//, $path );
175             }
176              
177             =pod
178              
179             =head2 get path/to/file.txt
180              
181             The C method takes the path of a file within the
182             repository, and fetches it from the remote repository, storing
183             it at the appropriate local path.
184              
185             Paths should B be provided in unix/web format, not the local
186             filesystem's format.
187              
188             Returns the L for the file if retrieved successfully,
189             false false if the file does not exist within the repository, or throws
190             an exception on error.
191              
192             =cut
193              
194             sub get {
195 0     0 1   my $self = shift;
196 0           my $file = $self->file(shift);
197              
198             # Check local dir exists
199 0           my $dir = File::Basename::dirname($file->path);
200 0 0         -d $dir or File::Path::mkpath($dir);
201              
202             # Fetch the file from the server
203 0           my $rc = LWP::Simple::getstore( $file->uri, $file->path );
204 0 0         if ( LWP::Simple::is_success($rc) ) {
    0          
205 0           return $file;
206             } elsif ( $rc == LWP::Simple::RC_NOT_FOUND ) {
207 0           return undef;
208             } else {
209 0           Carp::croak("$rc error retrieving " . $file->uri);
210             }
211             }
212              
213             =pod
214              
215             =head2 mirror path/to/file.txt
216              
217             The C method takes the path of a file within the
218             repository, and mirrors it from the remote repository, storing
219             it at the appropriate local path.
220              
221             Using this method if preferable for items like indexs for which
222             want to ensure you have the current version, but do not want to
223             freshly download each time.
224              
225             Paths should B be provided in unix/web format, not the local
226             filesystem's format.
227              
228             Returns the L for the file if mirrored successfully,
229             false if the file did not exist in the repository, or throws an
230             exception on error.
231              
232             =cut
233              
234             sub mirror {
235 0     0 1   my $self = shift;
236 0           my $path = $self->_path(shift);
237 0           my $file = $self->file($path);
238              
239             # If any only if a path is "stable" and the file already exists,
240             # it is guarenteed not to change, and we don't have to do the
241             # mirroring operation.
242 0 0 0       if ( $self->_static($path) and -f $file->path ) {
243 0           return $file;
244             }
245              
246             # Check local dir exists
247 0           my $dir = File::Basename::dirname($file->path);
248 0 0         -d $dir or File::Path::mkpath($dir);
249              
250             # Fetch the file from the server
251 0           my $rc = LWP::Simple::mirror( $file->uri => $file->path );
252 0 0         if ( LWP::Simple::is_success($rc) ) {
    0          
    0          
253 0           return $file;
254             } elsif ( $rc == LWP::Simple::RC_NOT_MODIFIED ) {
255 0           return $file;
256             } elsif ( $rc == LWP::Simple::RC_NOT_FOUND ) {
257 0           return '';
258             } else {
259 0           Carp::croak("HTTP $rc error mirroring " . $file->uri);
260             }
261             }
262              
263             =pod
264              
265             =head2 static
266              
267             The C method determines whether a given path within CPAN is
268             able to change or not.
269              
270             In the CPAN, some files such as index files and checksum can change,
271             while other files such as the tarball files will be static, and once
272             committed to the repository will never be changed (altough they may
273             be deleted).
274              
275             In a caching scenario, this means that if the file exists locally, we
276             will never need to return to the server to check for a new version,
277             we enables additional optimisations for CPAN-related algorithms.
278              
279             Returns true if the file will never change, false if not, or throws
280             an exception on error.
281              
282             =cut
283              
284             sub static {
285 0     0 1   my $self = shift;
286 0           my $path = $self->_path(shift);
287              
288             # All checksum files will change
289 0 0         if ( $path =~ m~/CHECKSUMS$~ ) {
290 0           return '';
291             }
292              
293             # The .readme files can apparently be changed
294 0 0         if ( $path =~ m~.readme$~ ) {
295 0           return '';
296             }
297              
298             # The authors directory is otherwise immutable
299 0 0         if ( $path =~ m~^authors/~ ) {
300 0           return 1;
301             }
302              
303             # The safe option is to default to false for the rest
304 0           return '';
305             }
306              
307              
308              
309              
310              
311             #####################################################################
312             # Support Methods
313              
314             # Validate a CPAN file path
315             sub _path {
316 0     0     my $self = shift;
317 0 0         my $path = shift or Carp::croak("No CPAN path provided");
318              
319             # Strip any leading slash
320 0           $path =~ s(^\/)();
321              
322 0           $path;
323             }
324              
325             1;
326              
327             =pod
328              
329             =head1 TO DO
330              
331             - Write a proper test suite, not just a compile test
332             (even though this was taken from working JSAN code)
333              
334             =head1 SUPPORT
335              
336             Bugs should be reported via the CPAN bug tracker
337              
338             L
339              
340             For other issues, contact the author.
341              
342             =head1 AUTHOR
343              
344             Adam Kennedy Eadamk@cpan.orgE
345              
346             =head1 SEE ALSO
347              
348             L, L, L
349              
350             =head1 COPYRIGHT
351              
352             Copyright 2006 Adam Kennedy.
353              
354             This program is free software; you can redistribute
355             it and/or modify it under the same terms as Perl itself.
356              
357             The full text of the license can be found in the
358             LICENSE file included with this module.
359              
360             =cut