File Coverage

blib/lib/Cache/Memcached/Indexable.pm
Criterion Covered Total %
statement 12 180 6.6
branch 0 68 0.0
condition 0 27 0.0
subroutine 4 31 12.9
pod 21 23 91.3
total 37 329 11.2


line stmt bran cond sub pod time code
1             package Cache::Memcached::Indexable;
2              
3 4     4   241521 use strict;
  4         10  
  4         225  
4 4     4   24 use warnings;
  4         8  
  4         120  
5 4     4   3598 use UNIVERSAL::require;
  4         12288  
  4         47  
6 4     4   130 use Carp;
  4         9  
  4         10808  
7              
8             our $VERSION = '0.03';
9             our $DEFAULT_LOGIC = 'Cache::Memcached::Indexable::Logic::Default';
10              
11             sub new {
12 0     0 1   my($class, $args) = @_;
13 0           my $self = bless $args, $class;
14              
15 0 0         if (exists $self->{logic}) {
16 0           my $logic = delete $self->{logic};
17 0           my $logic_args = delete $self->{logic_args};
18 0           $self->set_logic($logic, $logic_args);
19             }
20              
21 0           my $memd;
22 0 0         if (exists $self->{memd}) {
23 0           $memd = delete $self->{memd};
24             }
25 0           my %memd_args = map { $_ => $self->{$_} } keys %$self;
  0            
26 0           $self->{_memd_args} = \%memd_args;
27              
28 0 0         if ($memd) {
29 0           $self->set_memd($memd, $self->{_memd_args});
30             }
31              
32 0           return $self;
33             }
34              
35             sub logic {
36 0     0 1   my $self = shift;
37 0 0         if (my $logic = $self->{_logic}) {
38 0           return $logic;
39             }
40 0           $self->set_logic($DEFAULT_LOGIC);
41             }
42              
43             sub set_logic {
44 0     0 0   my $self = shift;
45 0           my $class = shift;
46 0 0         if (ref($class)) {
47 0           $self->{_logic} = $class;
48             }
49             else {
50 0 0         $class->use or croak $@;
51 0           my $logic = $class->new(@_);
52 0           $self->{_logic} = $logic;
53             }
54 0           return $self->{_logic};
55             }
56              
57             sub memd {
58 0     0 1   my $self = shift;
59 0 0         if (my $memd = $self->{_memd}) {
60 0           return $memd;
61             }
62 0           $self->set_memd('Cache::Memcached', $self->{_memd_args});
63             }
64              
65             sub set_memd {
66 0     0 0   my $self = shift;
67 0           my $class = shift;
68 0 0         if (ref($class)) {
69 0           $self->{_memd} = $class;
70             }
71             else {
72 0 0         $class->use or croak $@;
73 0           my $memd = $class->new(@_);
74 0           $self->{_memd} = $memd;
75             }
76 0           return $self->{_memd};
77             }
78              
79 0     0 1   sub set_servers { shift->memd->set_servers(@_) }
80              
81 0     0 1   sub set_debug { shift->memd->set_debug(@_) }
82              
83 0     0 1   sub set_readonly { shift->memd->set_readonly(@_) }
84              
85 0     0 1   sub set_norehash { shift->memd->set_norehash(@_) }
86              
87 0     0 1   sub set_compress_threshold { shift->memd->set_compress_threshold(@_) }
88              
89 0     0 1   sub enable_compress { shift->memd->enable_compress(@_) }
90              
91             sub get {
92 0     0 1   my($self, $key) = @_;
93 0           my $r = $self->get_multi($key);
94 0 0         my $kval = ref($key) ? $key->[1] : $key;
95 0           return $r->{$kval};
96             }
97              
98             sub get_multi {
99 0     0 1   my $self = shift;
100              
101 0           my %val = ();
102 0           my $logic = $self->logic;
103 0           my $memd = $self->memd;
104              
105 0           for my $key (@_) {
106 0           my $branch_key = $logic->branch_key($key);
107 0           my $stored = $memd->get($branch_key);
108 0 0 0       unless ($stored && ref($stored) eq 'HASH') {
109 0           $val{$key} = ();
110 0           next;
111             }
112 0           my $value = $stored->{$key};
113 0 0         next unless defined $value;
114 0 0         if (ref($value) eq 'ARRAY') {
115 0           my $expires = $value->[1];
116 0 0 0       if ($expires && time() > $expires) {
117 0           $self->delete($key);
118 0           $val{$key} = ();
119 0           next;
120             }
121 0           $val{$key} = $value->[0];
122 0           next;
123             }
124 0           $val{$key} = $value;
125             }
126              
127 0 0         if ($memd->{'debug'}) {
128 0           while (my($k, $v) = each %val) {
129 0           print STDERR "MemCache-Indexable: got $k = $v\n";
130             }
131             }
132              
133 0           return \%val;
134             }
135              
136             sub _exists {
137 0     0     my($self, $key) = @_;
138              
139 0           my $logic = $self->logic;
140 0           my $memd = $self->memd;
141              
142 0           my $branch_key = $logic->branch_key($key);
143 0           my $stored = $memd->get($branch_key);
144 0 0 0       return unless $stored && ref($stored) eq 'HASH';
145              
146 0           my $value = $stored->{$key};
147 0 0         return unless defined $value;
148              
149 0 0         return defined $value unless ref($value) eq 'ARRAY';
150              
151 0           my $expires = $value->[1];
152 0 0 0       if ($expires && time() > $expires) {
153 0           $self->delete($key);
154 0           return;
155             }
156 0           return defined $value->[0];
157             }
158              
159             sub set {
160 0     0 1   my($self, $key, $value, $exptime) = @_;
161              
162 0           my $check = $self->__deleted_keys_as_hashref;
163 0 0         if ($check->{$key}) {
164 0           $self->delete($key);
165 0           return;
166             }
167              
168 0 0         my $set_value = $exptime ? [ $value, (time() + $exptime) ] : $value;
169              
170 0           my $memd = $self->memd;
171 0           my $logic = $self->logic;
172 0           my $branch_key = $logic->branch_key($key);
173 0           my $stored = $memd->get($branch_key);
174              
175 0 0 0       unless ($stored && ref($stored) eq 'HASH') {
176 0           $stored = {};
177             }
178 0           $stored->{$key} = $set_value;
179 0           return $memd->set($branch_key => $stored);
180             }
181              
182             sub add {
183 0     0 1   my $self = shift;
184 0           my($key) = @_;
185 0 0         return if $self->_exists($key);
186 0           return $self->set(@_);
187             }
188              
189             sub replace {
190 0     0 1   my $self = shift;
191 0           my($key) = @_;
192 0 0         return unless $self->_exists($key);
193 0           return $self->set(@_);
194             }
195              
196             sub delete {
197 0     0 1   my($self, $key, $exptime) = @_;
198              
199 0           my $memd = $self->memd;
200 0           my $logic = $self->logic;
201 0           my $branch_key = $logic->branch_key($key);
202 0           my $stored = $memd->get($branch_key);
203 0           my $result;
204 0 0 0       if ($stored && ref($stored) eq 'HASH') {
205 0           my $deleted = delete $stored->{$key};
206 0           $result = defined $deleted;
207 0 0         $memd->set($branch_key => $stored) if $result;
208             }
209             else {
210 0           $memd->set($branch_key => {});
211             }
212              
213 0 0         if ($exptime) {
214 0           $self->_set_delete_expires($key => $exptime);
215             }
216              
217 0 0         return $result ? $result : ();
218             }
219              
220             sub incr {
221 0     0 1   my($self, $key, $value) = @_; # XXX a simple emulation of original incr()
222 0 0         $value = 1 unless defined $value;
223 0           $self->replace($key => $self->get($key) + $value);
224             }
225              
226             sub decr {
227 0     0 1   my($self, $key, $value) = @_; # XXX a simple emulation of original decr()
228 0 0         $value = 1 unless defined $value;
229 0           $self->replace($key => $self->get($key) - $value);
230             }
231              
232 0     0 1   sub stats { shift->memd->stats(@_) }
233              
234 0     0 1   sub disconnect_all { shift->memd->disconnect_all(@_) }
235              
236 0     0 1   sub flush_all { shift->memd->flush_all(@_) }
237              
238             sub keys {
239 0     0 1   my $self = shift;
240              
241 0           my $memd = $self->memd;
242 0           my $logic = $self->logic;
243              
244 0           my $deleted = $self->__deleted_keys_as_hashref;
245              
246 0           my @keys = ();
247 0           for my $trunk_key ($logic->trunk_keys) {
248 0           my $stored = $memd->get($trunk_key);
249 0 0 0       if ($stored && ref($stored) eq 'HASH') {
250 0           push(@keys, (grep { ! $deleted->{$_} } keys %$stored));
  0            
251             }
252             }
253 0           return @keys;
254             }
255              
256             sub _set_delete_expires {
257 0     0     my($self, $key, $exptime) = @_;
258              
259 0           my $memd = $self->memd;
260 0           my $deleted_key = $self->logic->_deleted_key;
261 0           my $deleted = $self->memd->get($deleted_key);
262 0 0 0       unless ($deleted && ref($deleted) eq 'HASH') {
263 0           $deleted = {};
264             }
265 0           $deleted->{$key} = time() + $exptime;
266 0           $memd->set($deleted_key => $deleted);
267             }
268              
269             sub _deleted_keys {
270 0     0     my $self = shift;
271              
272 0           my $memd = $self->memd;
273 0           my $deleted_key = $self->logic->_deleted_key;
274 0           my $deleted = $self->memd->get($deleted_key);
275 0 0 0       return unless $deleted && ref($deleted) eq 'HASH';
276              
277 0           my %new = ();
278 0           my @deleted_keys = ();
279              
280 0           for my $key (CORE::keys %$deleted) {
281 0 0         next if $deleted->{$key} < time();
282 0           push @deleted_keys, $key;
283 0           $new{$key} = $deleted->{$key};
284             }
285 0           $memd->set($deleted_key => \%new);
286 0           return @deleted_keys;
287             }
288              
289             sub __deleted_keys_as_hashref {
290 0     0     my $self = shift;
291 0           return +{ map { $_ => 1 } $self->_deleted_keys };
  0            
292             }
293              
294             1;
295             __END__