File Coverage

blib/lib/Mirror/URI.pm
Criterion Covered Total %
statement 112 139 80.5
branch 18 38 47.3
condition 4 15 26.6
subroutine 35 38 92.1
pod 0 22 0.0
total 169 252 67.0


line stmt bran cond sub pod time code
1             package Mirror::URI;
2              
3 6     6   1062 use 5.006;
  6         21  
  6         314  
4 6     6   34 use strict;
  6         11  
  6         400  
5 6     6   33 use Carp ();
  6         20  
  6         104  
6 6     6   28 use File::Spec ();
  6         11  
  6         98  
7 6     6   15457 use Time::HiRes ();
  6         31041  
  6         198  
8 6     6   8954 use Time::Local ();
  6         13770  
  6         146  
9 6     6   6341 use URI ();
  6         60633  
  6         346  
10 6     6   11902 use URI::file ();
  6         49534  
  6         451  
11 6     6   9076 use URI::http ();
  6         18107  
  6         171  
12 6     6   9048 use Params::Util qw{ _STRING _POSINT _ARRAY0 _INSTANCE };
  6         37080  
  6         1128  
13 6     6   6132 use LWP::Simple ();
  6         650061  
  6         187  
14              
15             # Time values have an extra 5 minute fudge factor
16 6     6   51 use constant ONE_DAY => 86700;
  6         11  
  6         537  
17 6     6   33 use constant TWO_DAYS => 172800;
  6         9  
  6         255  
18 6     6   30 use constant THIRTY_DAYS => 2592000;
  6         13  
  6         707  
19              
20 6     6   37 use vars qw{$VERSION};
  6         10  
  6         323  
21             BEGIN {
22 6     6   23044 $VERSION = '0.90';
23             }
24              
25              
26              
27              
28              
29             #####################################################################
30             # Constructor and Accessors
31              
32             sub new {
33 7     7 0 20 my $class = shift;
34 7         174 my $self = bless { @_ }, $class;
35              
36             # Clean up params
37 7         52 $self->{class} = $class;
38 7         52 $self->{valid} = !! $self->valid;
39 7 100       32 if ( $self->valid ) {
40 6 50       42 if ( _STRING($self->master) ) {
41 6         22 $self->{master} = URI->new( $self->master );
42             }
43 6 50       1087 unless ( _INSTANCE($self->master, 'URI') ) {
44 0         0 Carp::croak("Missing or invalid 'master' value");
45             }
46 6 50 33     426 if ( _STRING($self->{timestamp}) and ! _POSINT($self->{timestamp}) ) {
47 6 50       126 unless ( $self->{timestamp} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/ ) {
48 0         0 Carp::croak("Invalid timestamp format");
49             }
50 6         61 $self->{timestamp} = Time::Local::timegm( $6, $5, $4, $3, $2 - 1, $1 );
51             }
52 6 50 33     363 if ( $self->{filename} and $self->{filename} ne $self->filename ) {
53 0         0 Carp::croak("Invalid or unsupported offset '$self->{filename}'");
54             }
55 6         57 my $mirrors = $self->{mirrors};
56 6 50       33 unless ( _ARRAY0($mirrors) ) {
57 0         0 croak("Invalid mirror list");
58             }
59 6         28 foreach my $i ( 0 .. $#$mirrors ) {
60 262 50       29194 next unless _STRING($mirrors->[$i]);
61 262         787 $mirrors->[$i] = URI->new( $mirrors->[$i] );
62             }
63             }
64              
65 7         415 return $self;
66             }
67              
68             sub filename {
69 0   0 0 0 0 my $class = ref($_[0]) || $_[0];
70 0         0 die("$class does not implement filename");
71             }
72              
73             sub class {
74 2     2 0 17 $_[0]->{class};
75             }
76              
77             sub version {
78 3     3 0 784 $_[0]->{version};
79             }
80              
81             sub uri {
82 8     8 0 86 $_[0]->{uri};
83             }
84              
85             sub name {
86 5     5 0 1748 $_[0]->{name};
87             }
88              
89             sub master {
90 25     25 0 730 $_[0]->{master};
91             }
92              
93             sub timestamp {
94 3     3 0 22 $_[0]->{timestamp};
95             }
96              
97             sub mirrors {
98 2     2 0 4 return ( @{ $_[0]->{mirrors} } );
  2         13  
99             }
100              
101             sub valid {
102 19     19 0 2961 $_[0]->{valid};
103             }
104              
105             sub lastget {
106 3     3 0 76 $_[0]->{lastget};
107             }
108              
109             sub lag {
110 3     3 0 31 $_[0]->{lag};
111             }
112              
113             sub age {
114 3     3 0 30 $_[0]->{lastget} - $_[0]->{timestamp};
115             }
116              
117             sub as_string {
118 2     2 0 20 $_[0]->uri->as_string;
119             }
120              
121             sub is_cached {
122 2     2 0 1740 $_[0]->uri->isa('URI::file');
123             }
124              
125             sub is_master {
126 2     2 0 7 my $self = shift;
127             return (
128 2   33     12 ! $self->valid
129             and
130             $self->as_string eq $self->uri->as_string
131             );
132             }
133              
134              
135              
136              
137              
138             #####################################################################
139             # Load Methods
140              
141             sub read {
142 4     4 0 415893 my $class = shift;
143              
144             # Check the file to read
145 4         10 my $root = shift;
146 4 50 33     128 unless ( defined _STRING($root) and -d $root ) {
147 0         0 Carp::croak("Directory '$root' does not exist");
148             }
149              
150             # Convert to a usable URI
151 4         227 my $uri = URI::file->new(
152             File::Spec->canonpath(
153             File::Spec->rel2abs($root)
154             )
155             )->canonical;
156              
157             # In a URI a directory must have an explicit trailing slash
158 4         14897 $uri->path( $uri->path . '/' );
159              
160             # Hand off to the URI fetcher
161 4         331 return $class->get( $uri, dir => $root, @_ );
162             }
163              
164             sub get {
165 7     7 0 161 my $class = shift;
166              
167             # Check the URI
168 7         27 my $base = shift;
169 7 50       256 unless ( _INSTANCE($base, 'URI') ) {
170 0         0 Carp::croak("Missing or invalid URI");
171             }
172 7 50       112 unless ( $base->path =~ /\/$/ ) {
173 0         0 Carp::croak("URI must have a trailing slash");
174             }
175              
176             # Find the file within the root path
177 7         154 my %self = (
178             uri => URI->new($class->filename)->abs($base)->canonical,
179             );
180              
181             # Pull the file and time it
182 7         3257 $self{lastget} = Time::HiRes::time;
183 7         55 $self{string} = LWP::Simple::get($self{uri});
184 7         604326 $self{lag} = Time::HiRes::time - $self{lastget};
185 7 100       53 unless ( defined $self{string} ) {
186 1         23 return $class->new( %self, valid => 0 );
187             }
188              
189             # Parse the file
190 6         79 my $hash = $class->parse( $self{string} );
191 6 50       45 unless ( ref $hash eq 'HASH' ) {
192 0         0 return $class->new( %self, valid => 0 );
193             }
194              
195 6         93 $class->new( %$hash, %self, valid => 1 );
196             }
197              
198              
199              
200              
201              
202             #####################################################################
203             # Populate Elements
204              
205             sub get_master {
206 1     1 0 3 my $self = shift;
207 1 50       5 if ( _INSTANCE($self->master, 'URI') ) {
208             # Load the master
209 1         24 my $master = $self->class->get($self->master);
210 1         5 $self->{master} = $master;
211             }
212 1         6 return $self->master;
213             }
214              
215             sub get_mirror {
216 1     1 0 3 my $self = shift;
217 1         3 my $i = shift;
218 1         5 my $uri = $self->{mirrors}->[$i];
219 1 50       10 unless ( defined $uri ) {
220 0         0 Carp::croak("No mirror with index $i");
221             }
222 1 50       397 if ( _INSTANCE($uri, 'URI') ) {
223 1         16 my $mirror = $self->class->get($uri);
224 1         6 $self->{mirrors}->[$i] = $mirror;
225             }
226 1         7 return $self->{mirrors}->[$i];
227             }
228              
229              
230              
231              
232              
233             #####################################################################
234             # High Level Methods
235              
236             sub update {
237 0     0 0   my $self = shift;
238              
239             # Handle various shortcuts
240 0 0         unless ( $self->valid ) {
241 0           Carp::croak("Cannot update invalid mirror");
242             }
243 0 0         if ( $self->is_master ) {
244 0           return 1;
245             }
246              
247             # Pull the master and overwrite ourself with it
248 0           my $master = $self->get_master;
249 0 0         unless ( _INSTANCE($master, $self->class) ) {
250 0           Carp::croak("Failed to fetch master record");
251             }
252              
253             # Overwrite the current version with the master
254 0           foreach ( qw{
255             version uri name lastget timestamp
256             mirrors lag valid master
257             } ) {
258 0           $self->{$_} = delete $master->{$_};
259             }
260              
261 0           return 1;
262             }
263              
264             # Get all the mirrors
265             sub get_mirrors {
266 0     0 0   my $self = shift;
267 0           my $mirrors = $self->{mirrors};
268 0           foreach ( 0 .. $#$mirrors ) {
269 0           $self->get_mirror($_);
270             }
271 0           return 1;
272             }
273              
274             1;
275              
276             __END__