File Coverage

blib/lib/PAR/Repository/Client/Local.pm
Criterion Covered Total %
statement 67 82 81.7
branch 13 26 50.0
condition 4 9 44.4
subroutine 11 11 100.0
pod 2 2 100.0
total 97 130 74.6


line stmt bran cond sub pod time code
1             package PAR::Repository::Client::Local;
2              
3 9     9   224 use 5.006;
  9         35  
  9         1048  
4 9     9   49 use strict;
  9         16  
  9         307  
5 9     9   44 use warnings;
  9         16  
  9         260  
6              
7 9     9   43 use base 'PAR::Repository::Client';
  9         13  
  9         950  
8              
9 9     9   48 use Carp qw/croak/;
  9         16  
  9         12103  
10             require File::Copy;
11              
12             our $VERSION = '0.24';
13              
14             =head1 NAME
15              
16             PAR::Repository::Client::Local - PAR repo. on the local file system
17              
18             =head1 SYNOPSIS
19              
20             use PAR::Repository::Client;
21            
22             my $client = PAR::Repository::Client->new(
23             uri => 'file:///foo/repository',
24             );
25              
26             =head1 DESCRIPTION
27              
28             This module implements repository accesses on the local filesystem.
29              
30             If you create a new L object and pass it
31             an uri parameter which starts with C or just a path,
32             it will create an object of this class. It inherits from
33             C.
34              
35             =head2 EXPORT
36              
37             None.
38              
39             =head1 METHODS
40              
41             Following is a list of class and instance methods.
42             (Instance methods until otherwise mentioned.)
43              
44             =cut
45              
46             =head2 fetch_par
47              
48             Fetches a .par distribution from the repository and stores it
49             locally. Returns the name of the local file or the empty list on
50             failure.
51              
52             First argument must be the distribution name to fetch.
53              
54             =cut
55              
56             sub fetch_par {
57 4     4 1 8 my $self = shift;
58 4         18 $self->{error} = undef;
59 4         13 my $dist = shift;
60 4 50       20 if (not defined $dist) {
61 0         0 $self->{error} = "undef passed as argument to fetch_par()";
62 0         0 return();
63             }
64              
65 4         15 my $path = $self->{uri};
66 4         26 $path =~ s/(?:\/|\\)$//;
67 4         25 $path =~ s!^file://!!i;
68              
69 4         28 my ($dname, $vers, $arch, $perl) = PAR::Dist::parse_dist_name($dist);
70 4         1348 my $file = File::Spec->catfile(
71             File::Spec->catdir($path, $arch, $perl),
72             "$dname-$vers-$arch-$perl.par"
73             );
74              
75 4 50       251 if (not -f $file) {
76 0         0 $self->{error} = "Could not find distribution in local repository at '$file'";
77 0         0 return();
78             }
79              
80 4         21 return $file;
81             }
82              
83             =head2 validate_repository
84              
85             Makes sure the repository is valid. Returns the empty list
86             if that is not so and a true value if the repository is valid.
87              
88             Checks that the repository version is compatible.
89              
90             The error message is available as C<$client->error()> on
91             failure.
92              
93             =cut
94              
95             sub validate_repository {
96 9     9 1 24 my $self = shift;
97 9         25 $self->{error} = undef;
98              
99 9         90 my $mod_db = $self->modules_dbm;
100              
101 9 50       47 return() unless defined $mod_db;
102              
103 9 100       140 return() unless $self->validate_repository_version;
104              
105 8         47 return 1;
106             }
107              
108             =head2 _repository_info
109              
110             Returns a YAML::Tiny object representing the repository meta
111             information.
112              
113             This is a private method.
114              
115             =cut
116              
117             sub _repository_info {
118 9     9   24 my $self = shift;
119 9         23 $self->{error} = undef;
120 9 50       48 return $self->{info} if defined $self->{info};
121              
122 9         28 my $path = $self->{uri};
123 9         47 $path =~ s/(?:\/|\\)$//;
124 9         52 $path =~ s!^file://!!i;
125              
126 9         216 my $file = File::Spec->catfile($path, PAR::Repository::Client::REPOSITORY_INFO_FILE());
127              
128 9 50 33     320 if (not defined $file or not -f $file) {
129 0         0 $self->{error} = "File '$file' does not exist in repository.";
130 0         0 return();
131             }
132              
133 9         104 my $yaml = YAML::Tiny->new->read($file);
134 9 50       67233 if (not defined $yaml) {
135 0         0 $self->{error} = "Error reading repository info from YAML file.";
136 0         0 return();
137             }
138              
139             # workaround for possible YAML::Syck/YAML::Tiny bug
140             # This is not the right way to do it!
141 9 50       53 @$yaml = ($yaml->[1]) if @$yaml > 1;
142 9         33 $self->{info} = $yaml;
143 9         54 return $yaml;
144             }
145              
146             =head2 _fetch_dbm_file
147              
148             This is a private method.
149              
150             Fetches a dbm (index) file from the repository and
151             returns the name of the local file or the
152             empty list on failure.
153              
154             An error message is available via the C
155             method in case of failure.
156              
157             =cut
158              
159             sub _fetch_dbm_file {
160 13     13   25 my $self = shift;
161 13         29 $self->{error} = undef;
162 13         25 my $file = shift;
163 13 50       39 return if not defined $file;
164              
165 13         34 my $path = $self->{uri};
166 13         50 $path =~ s/(?:\/|\\)$//;
167 13         63 $path =~ s!^file://!!i;
168              
169 13         172 my $url = File::Spec->catfile( $path, $file );
170              
171 13 50       254 if (not -f $url) {
172 0         0 $self->{error} = "Could not find dbm file in local repository at '$url'";
173 0         0 return();
174             }
175              
176 13         102 my ($tempfh, $tempfile) = File::Temp::tempfile(
177             'temp_zip_dbm_XXXXX',
178             UNLINK => 1, # because we cache the suckers by default
179             DIR => $self->{cache_dir},
180             EXLOCK => 0, # FIXME no exclusive locking or else we block on BSD. What's the right solution?
181             );
182              
183 13         307108 File::Copy::copy($url, $tempfile);
184              
185 13         65915 return $tempfile;
186             }
187              
188              
189              
190             =head2 _dbm_checksums
191              
192             This is a private method.
193              
194             If the repository has a checksums file (new feature of
195             C 0.15), this method returns a hash
196             associating the DBM file names (e.g. C)
197             with their MD5 hashes (base 64).
198              
199             This method B queries the repository and never caches
200             the information locally. That's the whole point of having the
201             checksums.
202              
203             In case the repository does not have checksums, this method
204             returns the empty list, so check the return value!
205             The error message (see the C method) will be
206             I<"Repository does not support checksums"> in that case.
207              
208             =cut
209              
210             sub _dbm_checksums {
211 45     45   78 my $self = shift;
212 45         90 $self->{error} = undef;
213              
214 45         105 my $path = $self->{uri};
215 45         162 $path =~ s/(?:\/|\\)$//;
216 45         216 $path =~ s!^file://!!i;
217              
218             # if we're running on a "trust-the-checksums-for-this-long" basis...
219             # ... return if the timeout hasn't elapsed
220 45 50 66     343 if ($self->{checksums} and $self->{checksums_timeout}) {
221 0         0 my $time = time();
222 0 0       0 if ($time - $self->{last_checksums_refresh} < $self->{checksums_timeout}) {
223 0         0 return($self->{checksums});
224             }
225             }
226              
227 45         778 my $file = File::Spec->catfile($path, PAR::Repository::Client::DBM_CHECKSUMS_FILE());
228              
229 45 50 33     2631 if (not defined $file or not -f $file) {
230 0         0 $self->{error} = "Repository does not support checksums";
231 0         0 return();
232             }
233              
234 45         274 return $self->_parse_dbm_checksums($file);
235             }
236              
237              
238             =head2 _init
239              
240             This private method is called by the C method of
241             L. It is used to initialize
242             the client object and C passes it a hash ref to
243             its arguments.
244              
245             Should return a true value on success.
246              
247             =cut
248              
249             sub _init {
250             # We implement additional object attributes here
251             # Currently no extra attributes...
252 9     9   26 return 1;
253             }
254              
255              
256             1;
257             __END__