File Coverage

blib/lib/Cache/Meh.pm
Criterion Covered Total %
statement 84 89 94.3
branch 31 40 77.5
condition 2 3 66.6
subroutine 16 16 100.0
pod 7 7 100.0
total 140 155 90.3


line stmt bran cond sub pod time code
1 4     4   131655 use strict;
  4         10  
  4         261  
2 4     4   23 use warnings;
  4         7  
  4         224  
3             package Cache::Meh;
4             $Cache::Meh::VERSION = '0.04';
5 4     4   24 use Carp qw(confess);
  4         13  
  4         343  
6 4     4   5449 use Storable qw(nstore retrieve);
  4         16683  
  4         332  
7 4     4   3778 use File::Spec::Functions qw(tmpdir catfile);
  4         3945  
  4         396  
8 4     4   5639 use File::Temp qw(tempfile);
  4         140913  
  4         314  
9 4     4   3474 use File::Copy qw(move);
  4         10309  
  4         3521  
10              
11             # ABSTRACT: A cache of indifferent quality
12              
13              
14             sub filename {
15 9     9 1 382 my ($self, $f) = @_;
16              
17 9 100       25 if ( defined $f ) {
18 3         15 $self->{filename} = $f;
19             }
20              
21 9         42 return $self->{filename};
22             }
23              
24              
25             sub only_memory {
26 11     11 1 622 my ($self, $m) = @_;
27              
28 11 100       34 if ( defined $m ) {
29 1         6 $self->{only_memory} = $m;
30             }
31              
32 11         48 return $self->{only_memory};
33             }
34              
35              
36             sub validity {
37 6     6 1 12 my $self = shift;
38 6         12 my $validity = shift;
39              
40 6 100       21 if ( defined $validity ) {
41 3 50       9 if ( $validity > 0 ) {
42 3         8 $self->{validity} = int($validity);
43             }
44             else {
45 0         0 confess "$validity is not a positive integer\n";
46             }
47             }
48              
49 6         23 return $self->{validity};
50             }
51              
52              
53             sub lookup {
54 2     2 1 2 my $self = shift;
55 2         4 my $coderef = shift;
56              
57 2 50       15 if ( ref($coderef) ne "CODE" ) {
58 0         0 return $self->{lookup};
59             }
60             else {
61 2         4 $self->{lookup} = $coderef;
62             }
63              
64 2         5 return $self->{lookup};
65             }
66              
67              
68             sub new {
69 3     3 1 1798 my $class = shift;
70 3         17 my %args = @_;
71              
72 3         7 my $self = {};
73              
74 3         9 bless $self, $class;
75              
76 3 100       16 if ( exists $args{only_memory} ) {
    50          
77 1         6 $self->only_memory($args{only_memory});
78             }
79             elsif ( exists $args{filename} ) {
80 2         10 $self->filename($args{filename});
81             }
82             else {
83 0         0 confess "You must give a filename or set only_memory";
84             }
85              
86              
87 3         14 $self->{'~~~~cache'} = $self->_load();
88              
89 3 50       116 if ( exists $args{validity} ) {
90 3         13 $self->validity($args{validity});
91             }
92             else {
93 0         0 $self->validity(300);
94             }
95              
96 3 100       18 $self->lookup($args{lookup}) if exists $args{lookup};
97              
98 3         8 return $self;
99             }
100              
101             sub _load {
102 3     3   6 my $self = shift;
103              
104 3 100       9 return {} if $self->only_memory();
105              
106 2         15 my $fname = catfile(tmpdir(), $self->filename());
107              
108 2 100       63 if ( -e $fname ) {
109 1 50       17 if ( -r $fname ) {
110 1         6 return retrieve($fname);
111             }
112             else {
113 0         0 confess "$fname exists but is not readable.\n";
114             }
115             }
116              
117 1         3 return {};
118             }
119              
120             # This method stores the new cache file into a temporary file, then renames the
121             # tempfile to the cache state file name, which should help protect against
122             # new file write failures, leaving at least *some* state that will persist. I
123             # guess you could call this "atomic" but there are still a ton of race
124             # conditions in the IO layer which could bite you in the rear-end.
125              
126             sub _store {
127 6     6   10 my $self = shift;
128              
129 6 100       18 return 1 if $self->only_memory();
130              
131 2         16 my ($fh, $filename) = tempfile();
132              
133 2 50       1105 nstore($self->{'~~~~cache'}, $filename) or
134             confess "Couldn't store cache in $filename: $!\n";
135              
136             # Unix doesn't care if the filehandle is still open, but Windows
137             # will not allow a move unless there are no open handles to the
138             # tempfile.
139 2 50       477 close $fh or confess "Couldn't close filehandle for $filename: $!\n";
140              
141 2         11 my $fname = catfile(tmpdir(), $self->filename());
142 2 50       13 move($filename, $fname) or
143             confess "Couldn't rename $filename to $fname: $!\n";
144              
145 2         301 return 1;
146             }
147              
148              
149             sub get {
150 6     6 1 2000522 my ($self, $key) = @_;
151              
152 6 100       57 if ( exists $self->{'~~~~cache'}->{$key} ) {
153 3         26 my $i = $self->{'~~~~cache'}->{$key}->{'insert_time'} + $self->validity;
154 3 100       29 return $self->{'~~~~cache'}->{$key}->{'value'} if ( time < $i ) ;
155             }
156              
157 5 100 66     46 if ( exists $self->{lookup} && ref($self->{lookup}) eq 'CODE' ) {
158 4         16 my $value = $self->{lookup}->($key);
159 4         51 $self->set( $key, $value );
160 4         34 return $value;
161             }
162              
163 1 50       43 if ( exists $self->{'~~~~cache'}->{$key} ) {
164 1         6 delete $self->{'~~~~cache'}->{$key};
165 1         6 $self->_store();
166             }
167              
168 1         4 return undef;
169             }
170              
171              
172             sub set {
173 5     5 1 401 my ($self, $key, $value) = @_;
174              
175 5         48 $self->{'~~~~cache'}->{$key}->{'value'} = $value;
176 5         25 $self->{'~~~~cache'}->{$key}->{'insert_time'} = time;
177              
178 5         18 $self->_store();
179              
180 5         9 return $self;
181             }
182              
183             1;
184              
185             __END__