File Coverage

blib/lib/MojoX/Plugin/AnyCache.pm
Criterion Covered Total %
statement 76 76 100.0
branch 43 46 93.4
condition 12 15 80.0
subroutine 22 22 100.0
pod 1 12 8.3
total 154 171 90.0


line stmt bran cond sub pod time code
1             package MojoX::Plugin::AnyCache;
2              
3 8     8   7924 use Mojo::Base 'Mojolicious::Plugin';
  8         26376  
  8         78  
4              
5             our $VERSION = '0.04';
6              
7             has '_raw';
8             has 'app';
9             has 'backend';
10             has 'config';
11              
12             sub register {
13 6     6 1 8644 my ($self, $app, $config) = @_;
14              
15 6         189 $self->app($app);
16 6         241 $self->config($config);
17 6     1   77 $app->helper(cache => sub { $self });
  1         978  
18              
19 6 100       555 if(exists $config->{backend}) {
20 5         14 eval {
21 5         446 eval "require $config->{backend};";
22 5 0 33     167 warn "Require failed: $@" if $self->config->{debug} && $@;
23 5         110 my $backend = $config->{backend}->new;
24 5         172 $backend->config($config);
25 5         178 $self->backend($backend);
26 5         30 my $method = "init";
27 5 100       93 $backend->$method() if $backend->can($method);
28             };
29 5 50       29 die("Failed to load backend $config->{backend}: $@") if $@;
30             }
31             }
32              
33             sub check_mode {
34 96     96 0 158 my ($self, $cb) = @_;
35 96 100       2899 die("No backend available") if !$self->backend;
36 87 100 100     1947 die("Backend " . ref($self->backend) ." doesn't support asynchronous requests") if $cb && !$self->backend->support_async;
37 85 100 100     2738 die("Backend " . ref($self->backend) ." doesn't support synchronous requests") if !$cb && !$self->backend->support_sync;
38             }
39              
40             sub raw {
41 1     1 0 651 my ($self) = @_;
42              
43 1         5 my $clone = $self->new;
44              
45             # Deep copy
46 1         34 $clone->app ( $self->app );
47 1         34 $clone->backend( $self->backend );
48 1         33 $clone->config ( $self->config );
49              
50 1         30 $clone->_raw(1);
51              
52 1         9 return $clone;
53             }
54              
55             sub get {
56 38 100   38 0 61849 my $cb = ref($_[-1]) eq 'CODE' ? pop : undef;
57 38         82 my ($self, $key) = @_;
58 38         119 $self->check_mode($cb);
59 33 100 100     1342 if( !$self->_raw && (my $serialiser = $self->backend->get_serialiser)) {
60 7 100   3   160 return $self->backend->get($key, sub { $cb->($serialiser->deserialise(@_)) }) if $cb;
  3         120  
61 4         97 return $serialiser->deserialise($self->backend->get($key));
62             } else {
63 26 100   11   514 return $self->backend->get($key, sub { $cb->(@_) }) if $cb;
  11         359  
64 15         325 return $self->backend->get($key);
65             }
66             }
67              
68             sub set {
69 18 100   18 0 19606 my $cb = ref($_[-1]) eq 'CODE' ? pop : undef;
70 18         46 my ($self, $key, $value, $ttl) = @_;
71 18         59 $self->check_mode($cb);
72 17 100 66     664 if( !$self->_raw && (my $serialiser = $self->backend->get_serialiser)) {
73 5 100   2   93 return $self->backend->set($key, $serialiser->serialise($value), $ttl, sub { $cb->(@_) }) if $cb;
  2         203  
74 3         70 return $self->backend->set($key => $serialiser->serialise($value), $ttl);
75             } else {
76 12 100   6   588 return $self->backend->set($key, $value, $ttl, sub { $cb->(@_) }) if $cb;
  6         24378  
77 6         136 return $self->backend->set($key => $value, $ttl);
78             }
79             }
80              
81             sub incr {
82 12 100   12 0 41004 my $cb = ref($_[-1]) eq 'CODE' ? pop : undef;
83 12         44 my ($self, $key, $amount) = @_;
84 12         52 $self->check_mode($cb);
85 10 100   2   709 return $self->backend->incr($key, $amount, sub { $cb->(@_) }) if $cb;
  2         6229  
86 8         199 return $self->backend->incr($key => $amount);
87             }
88              
89             sub decr {
90 12 100   12 0 20194 my $cb = ref($_[-1]) eq 'CODE' ? pop : undef;
91 12         34 my ($self, $key, $amount) = @_;
92 12         38 $self->check_mode($cb);
93 10 100   2   344 return $self->backend->decr($key, $amount, sub { $cb->(@_) }) if $cb;
  2         2689  
94 8         195 return $self->backend->decr($key => $amount);
95             }
96              
97             sub del {
98 7 100   7 0 21829 my $cb = ref($_[-1]) eq 'CODE' ? pop : undef;
99 7         18 my ($self, $key) = @_;
100 7         32 $self->check_mode($cb);
101 5 100   2   164 return $self->backend->del($key, sub { $cb->(@_) }) if $cb;
  2         2713  
102 3         77 return $self->backend->del($key);
103             }
104              
105             sub ttl {
106 9 100   9 0 5535 my $cb = ref($_[-1]) eq 'CODE' ? pop : undef;
107 9         24 my ($self, $key) = @_;
108 9         55 $self->check_mode($cb);
109 8 100   4   234 return $self->backend->ttl($key, sub { $cb->(@_) }) if $cb;
  4         162  
110 4         91 return $self->backend->ttl($key);
111             }
112              
113 1     1 0 845 sub increment { shift->incr(@_) }
114 1     1 0 1024 sub decrement { shift->decr(@_) }
115 1     1 0 972 sub delete { shift->del(@_) }
116              
117             1;
118              
119             =encoding utf8
120              
121             =head1 NAME
122              
123             MojoX::Plugin::AnyCache - Cache plugin with blocking and non-blocking support
124              
125             =head1 SYNOPSIS
126              
127             $app->plugin('MojoX::Plugin::AnyCache' => {
128             backend => 'MojoX::Plugin::AnyCache::Backend::Redis',
129             server => '127.0.0.1:6379',
130             });
131              
132             # For synchronous backends (blocking)
133             $app->cache->set('key', 'value');
134             my $value = $app->cache->get('key');
135              
136             # For asynchronous backends (non-blocking)
137             $app->cache->set('key', 'value' => sub {
138             # ...
139             });
140             $app->cache->get('key' => sub {
141             my $value = shift;
142             # ...
143             });
144              
145             =head1 DESCRIPTION
146              
147             MojoX::Plugin::AnyCache provides an interface to both blocking and non-blocking
148             caching backends, for example Redis or Memcached.
149              
150             It also has a built-in replicator backend (L)
151             which automatically replicates values across multiple backend cache nodes.
152              
153             =head2 SERIALISATION
154              
155             The cache backend module supports an optional serialiser module.
156              
157             $app->plugin('MojoX::Plugin::AnyCache' => {
158             backend => 'MojoX::Plugin::AnyCache::Backend::Redis',
159             server => '127.0.0.1:6379',
160             serialiser => 'MojoX::Plugin::AnyCache::Serialiser::MessagePack'
161             });
162              
163             =head4 SERIALISER WARNING
164              
165             If you use a serialiser, C or C a value, then retrieve
166             the value using C, the value returned is deserialised.
167              
168             With the FakeSerialiser used in tests, this means C<1> is translated to an C.
169              
170             This 'bug' can be avoided by reading the value from the cache backend
171             directly, bypassing the backend serialiser:
172              
173             $self->cache->set('foo', 1);
174             $self->cache->backend->get('foo');
175              
176             =head2 TTL / EXPIRES
177              
178             =head3 Redis
179              
180             Full TTL support is available with a Redis backend. Pass the TTL (in seconds)
181             to the C method.
182              
183             $cache->set("key", "value", 10);
184              
185             $cache->set("key", "value", 10, sub {
186             # ...
187             });
188              
189             And to get the TTL (seconds remaining until expiry)
190              
191             my $ttl = $cache->ttl("key");
192              
193             $cache->ttl("key", sub {
194             my ($ttl) = @_;
195             # ...
196             });
197              
198             =head3 Memcached
199              
200             Full TTL set support is available with a Memcached backend. Pass the TTL (in seconds)
201             to the C method.
202              
203             $cache->set("key", "value", 10);
204              
205             $cache->set("key", "value", 10, sub {
206             # ...
207             });
208              
209             Unlike a Redis backend, 'get' TTL mode in Memcached is emulated, and the time
210             remaining is calculated using timestamps, and stored in a separate prefixed key.
211              
212             To enable this, set C on the backend:
213              
214             $cache->backend->get_ttl_support(1);
215              
216             This must be done before setting a value. You can then get the TTL as normal:
217              
218             my $ttl = $cache->ttl("key");
219              
220             $cache->ttl("key", sub {
221             my ($ttl) = @_;
222             # ...
223             });