File Coverage

blib/lib/File/Store.pm
Criterion Covered Total %
statement 98 147 66.6
branch 28 54 51.8
condition 2 8 25.0
subroutine 13 15 86.6
pod 9 10 90.0
total 150 234 64.1


line stmt bran cond sub pod time code
1             #! /usr/bin/perl
2              
3             =head1
4              
5             File::Store - a file content caching system
6              
7             =head1 SYNOPSIS
8              
9             use File::Store;
10              
11             my $contents1 = File::Store::get('/etc/passwd');
12             my $contents2 = File::Store::get('/etc/passwd');
13              
14             or
15              
16             use File::Store;
17             my $store = new File::Store;
18              
19             my $contents1 = $store->get('/etc/passwd');
20             my $contents2 = $store->get('/etc/passwd');
21             $store->clear();
22              
23              
24             =head1 DESCRIPTION
25              
26             This modules acts as an in-memory cache for files. Each file is read once
27             unless the modification date changes; in which case the file is
28             reread. Files can be automatically flushed based on time, size or
29             number of files.
30              
31             Files are read from the file system via the function I and
32             cached in memory. Subsequent calls for the same file returns the
33             cached file contents. If the file has been updated on disc, the file
34             is re-read.
35              
36             If no File::Store object is specified then a global store is used.
37              
38             =cut
39              
40             package File::Store;
41              
42 3     3   163416 use 5;
  3         14  
  3         154  
43 3     3   21 use strict;
  3         6  
  3         107  
44 3     3   16 use warnings;
  3         10  
  3         92  
45              
46 3     3   17 use Carp;
  3         6  
  3         285  
47             require bytes;
48              
49 3     3   20 use vars qw($VERSION $EXPIRE $SIZE $MAX);
  3         6  
  3         6539  
50              
51             $VERSION = '1.00';
52              
53             $EXPIRE = 0;
54             $MAX = 0;
55             $SIZE = 0;
56              
57             =head1 DEFAULT OPTIONS
58              
59             The default options are
60              
61             =over 4
62              
63             =item I
64              
65             How long, in seconds, to keep files in the cache. The default is always (0).
66              
67             =item I
68              
69             The maximum size, in bytes, of files kept in the cache.
70             The default is 0 (infinite).
71              
72             =item I
73              
74             The maximum number of files kept in the cache.
75             The default is 0 (infinite).
76              
77             =back 4
78              
79             These defaults can be changed globally via the packages variables
80             C<$File::Store::EXPIRE>,
81             C<$File::Store::SIZE>
82             and
83             C<$File::Store::MAX> respectively.
84              
85             =cut
86              
87             =head1 FUNCTIONS
88              
89             =over 4
90              
91             =item C
92              
93             my $store = new File::Store ();
94              
95             Create a new File::Store object with options.
96              
97             =cut
98              
99 11     11 0 30 sub debug { };
100             #sub debug { print STDERR (caller(1))[2], ' ', @_; };
101              
102             sub new
103             {
104 4     4 1 22 my ($class, @args) = @_;
105 4 50       26 croak "odd number of option arguments" unless ($#args % 2);
106              
107 4         8 my $self = {};
108 4         13 bless $self, $class;
109              
110 4         39 $self->{option} = {
111             expire => $EXPIRE,
112             max => $MAX,
113             size => $SIZE,
114             };
115              
116 4         16 $self->{cache} = {};
117 4         30 $self->{queue} = [];
118 4         9 $self->{count} = 0;
119 4         8 $self->{size} = 0;
120              
121 4         12 $self->configure(@args);
122              
123 4         15 $self;
124             }
125              
126             # Default file store.
127             our $base = new File::Store();
128              
129             =item C
130              
131             $store->configure();
132              
133             Configure a File::Store.
134              
135             =cut
136              
137             sub configure
138             {
139 4 50   4 1 18 if (ref $_[0] ne 'File::Store') { unshift @_, $base; }
  0         0  
140 4         9 my ($this, @args) = @_;
141              
142 4 50       17 croak "odd number of option arguments" unless ($#args % 2);
143 4         11 my %args = @args;
144              
145 4 50       14 $this->{option}->{expire} = delete $args{expire}
146             if exists $args{expire};
147 4 50       12 $this->{option}->{size} = delete $args{size}
148             if exists $args{size};
149 4 50       11 $this->{option}->{max} = delete $args{max}
150             if exists $args{max};
151              
152 4 50       13 croak "unknown configuration keys '", join("', '", keys %args) . "'" if (%args);
153              
154             # purge any files from the cache.
155 4         85 $this->purge();
156              
157 4         9 $this;
158             }
159              
160             =item C
161              
162             $store->get($file);
163              
164             Return the contents of the specified file from the cache, reading the
165             file from disc if necessary.
166              
167             =cut
168             # return any cached file, or load it if needed.
169             sub get
170             {
171 3 100   3 1 864 if (ref $_[0] ne 'File::Store') { unshift @_, $base; }
  2         7  
172 3         8 my ($this, $file) = @_;
173              
174 3         17 debug "getting '$file'\n";
175              
176             # Check the cache first.
177 3         66 my $mtime = (stat($file))[9];
178              
179             # Does the file exist?
180 3 100       54 unless (defined $mtime)
181             {
182             # no such file.
183 1         5 $this->clear($file); # just in case.
184 1         3 return undef;
185             }
186              
187 2 100       10 unless ($this->cached($file) == $mtime)
188             {
189 1         6 debug "reading $file from disc\n";
190              
191             # clear, just in case.
192 1         5 $this->clear($file);
193              
194             # Open file.
195 1         3 local (*F);
196 1 50       42 open (F, '<', $file) || return undef;
197              
198             # Read file.
199 1         5 local ($/) = undef;
200 1         23 my $str = ;
201 1         12 close (F);
202              
203             # Remember
204 1         7 $this->{cache}->{$file}->{mtime} = $mtime;
205 1         6 $this->{cache}->{$file}->{content} = $str;
206              
207 1         3 $this->{count}++;
208 1         12 $this->{size} += bytes::length($str);
209             }
210              
211             # remember when it was last used.
212 2         1403 $this->{cache}->{$file}->{when} = time;
213              
214             # requeue
215 2         4 local $_;
216 2         4 @{$this->{queue}} = grep {$_ ne $file} @{$this->{queue}};
  2         6  
  1         4  
  2         6  
217 2         4 push @{$this->{queue}}, $file;
  2         515  
218              
219             # reorder the cache
220             #my $tmp = $this->{cache}->{$file};
221             #debug "List0 ", join(' ', keys %{$this->{cache}}), "\n";
222             #$this->{tie}->DELETE($file);
223             #debug "List1 ", join(' ', keys %{$this->{cache}}), "\n\n";
224             #$this->{cache}->{$file} = $tmp;
225              
226             # There is a slight chance that purging will
227             # delete this file. So remember the contents before
228             # purging.
229 2         8 my $contents = $this->{cache}->{$file}->{content};
230              
231             # spring clean
232 2         8 $this->purge();
233              
234 2         14 $contents;
235             }
236              
237             =item C
238              
239             $store->clear();
240             $store->clear($file1, $file2, ...);
241              
242             Clear the caches inside a File::Store. If files are specified,
243             information about those files are clear. Otherwise the whole cache is
244             cleared.
245              
246             =cut
247              
248             sub clear
249             {
250 3 100   3 1 547 if (ref $_[0] ne 'File::Store') { unshift @_, $base; }
  1         5  
251 3         9 my ($this, @files) = @_;
252              
253 3 100       13 unless (@files)
254             {
255 1         3 debug "clearing cache.\n";
256              
257 1         2 $this->{count} = 0;
258 1         3 $this->{size} = 0;
259 1         3 $this->{cache} = {};
260 1         4 $this->{queue} = [];
261              
262 1         5 return $this;
263             }
264              
265 2         6 for my $f (@files)
266             {
267 2 50       14 next unless exists $this->{cache}->{$f};
268              
269 0         0 debug "clearing '$f'.\n";
270 0         0 local $_;
271              
272 0         0 $this->{count} --;
273 0         0 $this->{size} -= bytes::length($this->{cache}->{$f}->{content});
274 0         0 @{$this->{queue}} = grep {$_ ne $f} @{$this->{queue}};
  0         0  
  0         0  
  0         0  
275              
276 0         0 delete $this->{cache}->{$f};
277             }
278            
279 2         7 $this;
280             }
281              
282             =item C
283              
284             $store->purge();
285              
286             Remove any items in the cache according to the options I,
287             I and I. If the cache is too large, then the oldest items
288             (according to their last use) are removed.
289              
290             =cut
291              
292             sub purge
293             {
294 6 50   6 1 26 if (ref $_[0] ne 'File::Store') { unshift @_, $base; }
  0         0  
295 6         11 my ($this) = @_;
296              
297 6         10 debug "list ", join(' ', @{$this->{queue}}), "\n";
  6         33  
298              
299 6         10 my @files;
300              
301             # Look through the list and expire the cache
302 6 50       27 if ($this->{option}->{expire} > 0)
303             {
304 0         0 for my $f (keys %{$this->{cache}})
  0         0  
305             {
306 0 0       0 if ($this->{cache}->{$f}->{when} < time - $this->{option}->{expire})
307             {
308 0         0 $this->clear($f);
309 0         0 push @files, $f;
310 0         0 debug "purged expired '$f'.\n";
311             }
312             }
313             }
314              
315             # Have we cached too much data?
316 6 50 33     61 if ($this->{option}->{size} > 0 && $this->{size} > $this->{option}->{size})
317             {
318 0         0 my @list = sort { $this->{cache}->{$a}->{when} <=> $this->{cache}->{$b}->{when}; }
  0         0  
319 0         0 keys %{$this->{cache}};
320              
321 0         0 while ($this->{size} > $this->{option}->{size})
322             {
323             # too much.
324 0         0 my $f = shift @list;
325 0         0 $this->clear($f);
326 0         0 push @files, $f;
327 0         0 debug "purged size excess '$f'.\n";
328             }
329             }
330              
331             # Have we cached too many files?
332 6 50 33     35 if ($this->{option}->{max} > 0 && $this->{count} > $this->{option}->{max})
333             {
334 0         0 my @list = sort { $this->{cache}->{$a}->{when} <=> $this->{cache}->{$b}->{when}; }
  0         0  
335 0         0 keys %{$this->{cache}};
336              
337 0         0 @list = @{$this->{queue}};
  0         0  
338              
339 0         0 while ($this->{count} > $this->{option}->{max})
340             {
341             # too many.
342 0         0 my $f = shift @list;
343 0         0 $this->clear($f);
344 0         0 push @files, $f;
345 0         0 debug "purged count excess '$f'; count=$this->{option}->{max}.\n";
346             }
347             }
348              
349             # return the list of purged files.
350 6         17 @files;
351             }
352              
353              
354             =item C
355              
356             $store->count();
357              
358             Return the number of files in the File::Store.
359              
360             =cut
361              
362             sub count
363             {
364 1 50   1 1 491 if (ref $_[0] ne 'File::Store') { unshift @_, $base; }
  0         0  
365 1         2 my ($this) = @_;
366              
367 1         7 $this->{count};
368             }
369              
370             =item C
371              
372             $store->size();
373              
374             Return the size, in bytes, of the File::Store.
375              
376             =cut
377              
378             sub size
379             {
380 0 0   0 1 0 if (ref $_[0] ne 'File::Store') { unshift @_, $base; }
  0         0  
381 0         0 my ($this) = @_;
382              
383 0         0 $this->{size};
384             }
385              
386             =item C
387              
388             $store->cached($file);
389              
390             Return the last modification time of a file contained in the cache.
391             Return 0 if the file isn't cached.
392              
393             =cut
394             sub cached
395             {
396 2 50   2 1 12 if (ref $_[0] ne 'File::Store') { unshift @_, $base; }
  0         0  
397 2         4 my ($this, $file) = @_;
398 2 50       9 croak "No file specified" unless $file;
399              
400 2 100       20 return -1 unless exists $this->{cache}->{$file};
401 1         7 $this->{cache}->{$file}->{mtime};
402             }
403              
404             =item C
405              
406             $store->fresh($file1, $file2, ...);
407              
408             Return whether the list of files are up to date or not. Returns 1 if
409             all files are fresh and undef otherwise.
410              
411             =cut
412             # Check the file list for freshness.
413             sub fresh
414             {
415 0 0   0 1   if (ref $_[0] ne 'File::Store') { unshift @_, $base; }
  0            
416 0           my $this = shift(@_);
417              
418 0           for my $f (@_)
419             {
420 0 0         return undef unless exists $this->{cache}->{$f};
421              
422 0   0       my $m = (stat($f))[9] || 0;
423            
424 0 0         return undef if ($m != $this->{cache}->{$f}->{mtime});
425             }
426              
427             # all is fresh
428 0           1;
429             }
430              
431             =head1 SEE ALSO
432              
433             Perl, Cache::Cache
434              
435             =head1 VERSION
436              
437             This is version 1.0 released 2008.
438              
439             =head1 AUTHOR
440              
441             Anthony Fletcher arif+perl@cpan.org
442              
443             =head1 COPYRIGHT
444              
445             Copyright (c) 1998-2008 Anthony Fletcher. All rights reserved. This
446             module is free software; you can redistribute them and/or modify them
447             under the same terms as Perl itself.
448              
449             This code is supplied as-is - use at your own risk.
450              
451             =cut
452              
453             1;
454