line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Catalyst::Plugin::Cache; |
4
|
5
|
|
|
5
|
|
145384
|
use Moose; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
with 'Catalyst::ClassData'; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = "0.12"; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
use Scalar::Util (); |
11
|
|
|
|
|
|
|
use Catalyst::Utils (); |
12
|
|
|
|
|
|
|
use Carp (); |
13
|
|
|
|
|
|
|
use MRO::Compat; |
14
|
|
|
|
|
|
|
use Scalar::Util qw/ blessed /; |
15
|
|
|
|
|
|
|
use Catalyst::Plugin::Cache::Curried; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( "_cache_backends" ); |
18
|
|
|
|
|
|
|
has _default_curried_cache => ( |
19
|
|
|
|
|
|
|
is => 'rw', |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
no Moose; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub setup { |
24
|
|
|
|
|
|
|
my $app = shift; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# set it once per app, not once per plugin, |
27
|
|
|
|
|
|
|
# and don't overwrite if some plugin was wicked |
28
|
|
|
|
|
|
|
$app->_cache_backends({}) unless $app->_cache_backends; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my $ret = $app->maybe::next::method( @_ ); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
$app->setup_cache_backends; |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
$ret; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
{ |
37
|
|
|
|
|
|
|
my %has_warned_for; |
38
|
|
|
|
|
|
|
sub _get_cache_plugin_config { |
39
|
|
|
|
|
|
|
my ($app) = @_; |
40
|
|
|
|
|
|
|
my $config = $app->config->{'Plugin::Cache'}; |
41
|
|
|
|
|
|
|
if (!$config) { |
42
|
|
|
|
|
|
|
$config = $app->config->{cache}; |
43
|
|
|
|
|
|
|
my $appname = ref($app); |
44
|
|
|
|
|
|
|
if (! $has_warned_for{$appname}++ ) { |
45
|
|
|
|
|
|
|
$app->log->warn($config ? |
46
|
|
|
|
|
|
|
'Catalyst::Plugin::Cache config found in deprecated $c->config->{cache}, please move to $c->config->{"Plugin::Cache"}.' |
47
|
|
|
|
|
|
|
: 'Catalyst::Plugin::Cache config not found, using empty config!' |
48
|
|
|
|
|
|
|
); |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
return $config || {}; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub get_default_cache_backend_config { |
56
|
|
|
|
|
|
|
my ( $app, $name ) = @_; |
57
|
|
|
|
|
|
|
$app->_get_cache_plugin_config->{backend} || $app->get_cache_backend_config("default"); |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub get_cache_backend_config { |
61
|
|
|
|
|
|
|
my ( $app, $name ) = @_; |
62
|
|
|
|
|
|
|
$app->_get_cache_plugin_config->{backends}{$name}; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub setup_cache_backends { |
66
|
|
|
|
|
|
|
my $app = shift; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# give plugins a chance to find things for themselves |
69
|
|
|
|
|
|
|
$app->maybe::next::method; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# FIXME - Don't know why the _get_cache_plugin_config method doesn't work here! |
72
|
|
|
|
|
|
|
my $conf = $app->_get_cache_plugin_config->{backends}; |
73
|
|
|
|
|
|
|
foreach my $name ( keys %$conf ) { |
74
|
|
|
|
|
|
|
next if $app->get_cache_backend( $name ); |
75
|
|
|
|
|
|
|
$app->setup_generic_cache_backend( $name, $app->get_cache_backend_config( $name ) || {} ); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
if ( !$app->get_cache_backend("default") ) { |
79
|
|
|
|
|
|
|
### XXX currently we dont have a fallback scenario |
80
|
|
|
|
|
|
|
### so die here with the error message. Once we have |
81
|
|
|
|
|
|
|
### an in memory fallback, we may consider silently |
82
|
|
|
|
|
|
|
### logging the error and falling back to that. |
83
|
|
|
|
|
|
|
### If we dont die here, the app will silently start |
84
|
|
|
|
|
|
|
### up and then explode at the first cache->get or |
85
|
|
|
|
|
|
|
### cache->set request with a FIXME error |
86
|
|
|
|
|
|
|
#local $@; |
87
|
|
|
|
|
|
|
#eval { |
88
|
|
|
|
|
|
|
$app->setup_generic_cache_backend( default => $app->get_default_cache_backend_config || {} ); |
89
|
|
|
|
|
|
|
#}; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub default_cache_store { |
95
|
|
|
|
|
|
|
my $app = shift; |
96
|
|
|
|
|
|
|
$app->_get_cache_plugin_config->{default_store} || $app->guess_default_cache_store; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub guess_default_cache_store { |
100
|
|
|
|
|
|
|
my $app = shift; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
my @stores = map { /Cache::Store::(.*)$/ ? $1 : () } $app->registered_plugins; |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
if ( @stores == 1 ) { |
105
|
|
|
|
|
|
|
return $stores[0]; |
106
|
|
|
|
|
|
|
} else { |
107
|
|
|
|
|
|
|
Carp::croak "You must configure a default store type unless you use exactly one store plugin."; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub setup_generic_cache_backend { |
112
|
|
|
|
|
|
|
my ( $app, $name, $config ) = @_; |
113
|
|
|
|
|
|
|
my %config = %$config; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
if ( my $class = delete $config{class} ) { |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
### try as list and as hashref, collect the |
118
|
|
|
|
|
|
|
### error if things go wrong |
119
|
|
|
|
|
|
|
### if all goes well, exit the loop |
120
|
|
|
|
|
|
|
my @errors; |
121
|
|
|
|
|
|
|
for my $aref ( [%config], [\%config] ) { |
122
|
|
|
|
|
|
|
eval { $app->setup_cache_backend_by_class( |
123
|
|
|
|
|
|
|
$name, $class, @$aref |
124
|
|
|
|
|
|
|
); |
125
|
|
|
|
|
|
|
} ? do { @errors = (); last } |
126
|
|
|
|
|
|
|
: push @errors, "\t$@"; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
### and die with the errors if we have any |
130
|
|
|
|
|
|
|
die "Couldn't construct $class with either list style or hash ref style param passing:\n @errors" if @errors; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
} elsif ( my $store = delete $config->{store} || $app->default_cache_store ) { |
133
|
|
|
|
|
|
|
my $method = lc("setup_${store}_cache_backend"); |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
Carp::croak "You must load the $store cache store plugin (if it exists). ". |
136
|
|
|
|
|
|
|
"Please consult the Catalyst::Plugin::Cache documentation on how to configure hetrogeneous stores." |
137
|
|
|
|
|
|
|
unless $app->can($method); |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
$app->$method( $name, \%config ); |
140
|
|
|
|
|
|
|
} else { |
141
|
|
|
|
|
|
|
$app->log->warn("Couldn't setup the cache backend named '$name'"); |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub setup_cache_backend_by_class { |
146
|
|
|
|
|
|
|
my ( $app, $name, $class, @args ) = @_; |
147
|
|
|
|
|
|
|
Catalyst::Utils::ensure_class_loaded( $class ); |
148
|
|
|
|
|
|
|
$app->register_cache_backend( $name => $class->new( @args ) ); |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# end of spaghetti setup DWIM |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub cache { |
154
|
|
|
|
|
|
|
my ( $c, @meta ) = @_; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
if ( @meta == 1 ) { |
157
|
|
|
|
|
|
|
my $name = $meta[0]; |
158
|
|
|
|
|
|
|
return ( $c->get_preset_curried($name) || $c->get_cache_backend($name) ); |
159
|
|
|
|
|
|
|
} elsif ( !@meta && blessed $c ) { |
160
|
|
|
|
|
|
|
# be nice and always return the same one for the simplest case |
161
|
|
|
|
|
|
|
return ( $c->_default_curried_cache || $c->_default_curried_cache( $c->curry_cache( @meta ) ) ); |
162
|
|
|
|
|
|
|
} else { |
163
|
|
|
|
|
|
|
return $c->curry_cache( @meta ); |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub construct_curried_cache { |
168
|
|
|
|
|
|
|
my ( $c, @meta ) = @_; |
169
|
|
|
|
|
|
|
return $c->curried_cache_class( @meta )->new( @meta ); |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub curried_cache_class { |
173
|
|
|
|
|
|
|
my ( $c, @meta ) = @_; |
174
|
|
|
|
|
|
|
$c->_get_cache_plugin_config->{curried_class} || "Catalyst::Plugin::Cache::Curried"; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub curry_cache { |
178
|
|
|
|
|
|
|
my ( $c, @meta ) = @_; |
179
|
|
|
|
|
|
|
return $c->construct_curried_cache( $c, $c->_cache_caller_meta, @meta ); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub get_preset_curried { |
183
|
|
|
|
|
|
|
my ( $c, $name ) = @_; |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
if ( ref( my $preset = $c->_get_cache_plugin_config->{profiles}{$name} ) ) { |
186
|
|
|
|
|
|
|
return $preset if Scalar::Util::blessed($preset); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
my @meta = ( ( ref $preset eq "HASH" ) ? %$preset : @$preset ); |
189
|
|
|
|
|
|
|
return $c->curry_cache( @meta ); |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
return; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub get_cache_backend { |
196
|
|
|
|
|
|
|
my ( $c, $name ) = @_; |
197
|
|
|
|
|
|
|
$c->_cache_backends->{$name}; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub register_cache_backend { |
201
|
|
|
|
|
|
|
my ( $c, $name, $backend ) = @_; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
no warnings 'uninitialized'; |
204
|
|
|
|
|
|
|
Carp::croak("$backend does not look like a cache backend - " |
205
|
|
|
|
|
|
|
. "it must be an object supporting get, set and remove") |
206
|
|
|
|
|
|
|
unless eval { $backend->can("get") && $backend->can("set") && $backend->can("remove") }; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
$c->_cache_backends->{$name} = $backend; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub unregister_cache_backend { |
212
|
|
|
|
|
|
|
my ( $c, $name ) = @_; |
213
|
|
|
|
|
|
|
delete $c->_cache_backends->{$name}; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
sub default_cache_backend { |
217
|
|
|
|
|
|
|
my $c = shift; |
218
|
|
|
|
|
|
|
$c->get_cache_backend( "default" ) || $c->temporary_cache_backend; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub temporary_cache_backend { |
222
|
|
|
|
|
|
|
my $c = shift; |
223
|
|
|
|
|
|
|
die "FIXME - make up an in memory cache backend, that hopefully works well for the current engine"; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub _cache_caller_meta { |
227
|
|
|
|
|
|
|
my $c = shift; |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
my ( $caller, $component, $controller ); |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
for my $i ( 0 .. 15 ) { # don't look to far |
232
|
|
|
|
|
|
|
my @info = caller(2 + $i) or last; |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
$caller ||= \@info unless $info[0] =~ /Plugin::Cache/; |
235
|
|
|
|
|
|
|
$component ||= \@info if $info[0]->isa("Catalyst::Component"); |
236
|
|
|
|
|
|
|
$controller ||= \@info if $info[0]->isa("Catalyst::Controller"); |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
last if $caller && $component && $controller; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
my ( $caller_pkg, $component_pkg, $controller_pkg ) = |
242
|
|
|
|
|
|
|
map { $_ ? $_->[0] : undef } $caller, $component, $controller; |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
return ( |
245
|
|
|
|
|
|
|
'caller' => $caller_pkg, |
246
|
|
|
|
|
|
|
component => $component_pkg, |
247
|
|
|
|
|
|
|
controller => $controller_pkg, |
248
|
|
|
|
|
|
|
caller_frame => $caller, |
249
|
|
|
|
|
|
|
component_frame => $component, |
250
|
|
|
|
|
|
|
controller_frame => $controller, |
251
|
|
|
|
|
|
|
); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# this gets a shit name so that the plugins can override a good name |
255
|
|
|
|
|
|
|
sub choose_cache_backend_wrapper { |
256
|
|
|
|
|
|
|
my ( $c, @meta ) = @_; |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
Carp::croak("metadata must be an even sized list") unless @meta % 2 == 0; |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
my %meta = @meta; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
unless ( exists $meta{'caller'} ) { |
263
|
|
|
|
|
|
|
my %caller = $c->_cache_caller_meta; |
264
|
|
|
|
|
|
|
@meta{keys %caller} = values %caller; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# allow the cache client to specify who it wants to cache with (but loeave room for a hook) |
268
|
|
|
|
|
|
|
if ( exists $meta{backend} ) { |
269
|
|
|
|
|
|
|
if ( Scalar::Util::blessed($meta{backend}) ) { |
270
|
|
|
|
|
|
|
return $meta{backend}; |
271
|
|
|
|
|
|
|
} else { |
272
|
|
|
|
|
|
|
return $c->get_cache_backend( $meta{backend} ) || $c->default_cache_backend; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
}; |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
if ( my $chosen = $c->choose_cache_backend( %meta ) ) { |
277
|
|
|
|
|
|
|
$chosen = $c->get_cache_backend( $chosen ) unless Scalar::Util::blessed($chosen); # if it's a name find it |
278
|
|
|
|
|
|
|
return $chosen if Scalar::Util::blessed($chosen); # only return if it was an object or name lookup worked |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# FIXME |
281
|
|
|
|
|
|
|
# die "no such backend"? |
282
|
|
|
|
|
|
|
# currently, we fall back to default |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
return $c->default_cache_backend; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub choose_cache_backend { shift->maybe::next::method( @_ ) } # a convenient fallback |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
sub cache_set { |
291
|
|
|
|
|
|
|
my ( $c, $key, $value, %meta ) = @_; |
292
|
|
|
|
|
|
|
$c->choose_cache_backend_wrapper( key => $key, value => $value, %meta ) |
293
|
|
|
|
|
|
|
->set( $key, $value, exists $meta{expires} ? $meta{expires} : () ); |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub cache_get { |
297
|
|
|
|
|
|
|
my ( $c, $key, @meta ) = @_; |
298
|
|
|
|
|
|
|
$c->choose_cache_backend_wrapper( key => $key, @meta )->get( $key ); |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub cache_remove { |
302
|
|
|
|
|
|
|
my ( $c, $key, @meta ) = @_; |
303
|
|
|
|
|
|
|
$c->choose_cache_backend_wrapper( key => $key, @meta )->remove( $key ); |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
sub cache_compute { |
307
|
|
|
|
|
|
|
my ($c, $key, $code, %meta) = @_; |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
my $backend = $c->choose_cache_backend_wrapper( key => $key, %meta ); |
310
|
|
|
|
|
|
|
if ($backend->can('compute')) { |
311
|
|
|
|
|
|
|
return $backend->compute( $key, $code, exists $meta{expires} ? $meta{expires} : () ); |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
Carp::croak "must specify key and code" unless defined($key) && defined($code); |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
my $value = $c->cache_get( $key, %meta ); |
317
|
|
|
|
|
|
|
if ( !defined $value ) { |
318
|
|
|
|
|
|
|
$value = $code->(); |
319
|
|
|
|
|
|
|
$c->cache_set( $key, $value, %meta ); |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
return $value; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
__PACKAGE__; |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
__END__ |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=pod |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=head1 NAME |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
Catalyst::Plugin::Cache - Flexible caching support for Catalyst. |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
=head1 SYNOPSIS |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
use Catalyst qw/ |
337
|
|
|
|
|
|
|
Cache |
338
|
|
|
|
|
|
|
/; |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# configure a backend or use a store plugin |
341
|
|
|
|
|
|
|
__PACKAGE__->config->{'Plugin::Cache'}{backend} = { |
342
|
|
|
|
|
|
|
class => "Cache::Bounded", |
343
|
|
|
|
|
|
|
# ... params for Cache::Bounded... |
344
|
|
|
|
|
|
|
}; |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
# typical example for Cache::Memcached::libmemcached |
347
|
|
|
|
|
|
|
__PACKAGE__->config->{'Plugin::Cache'}{backend} = { |
348
|
|
|
|
|
|
|
class => "Cache::Memcached::libmemcached", |
349
|
|
|
|
|
|
|
servers => ['127.0.0.1:11211'], |
350
|
|
|
|
|
|
|
debug => 2, |
351
|
|
|
|
|
|
|
}; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# In a controller: |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
sub foo : Local { |
357
|
|
|
|
|
|
|
my ( $self, $c, $id ) = @_; |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
my $cache = $c->cache; |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
my $result; |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
unless ( $result = $cache->get( $id ) ) { |
364
|
|
|
|
|
|
|
# ... calculate result ... |
365
|
|
|
|
|
|
|
$c->cache->set( $id, $result ); |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
}; |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=head1 DESCRIPTION |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
This plugin gives you access to a variety of systems for caching |
372
|
|
|
|
|
|
|
data. It allows you to use a very simple configuration API, while |
373
|
|
|
|
|
|
|
maintaining the possibility of flexibility when you need it later. |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
Among its features are support for multiple backends, segmentation based |
376
|
|
|
|
|
|
|
on component or controller, keyspace partitioning, and so more, in |
377
|
|
|
|
|
|
|
various subsidiary plugins. |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head1 METHODS |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=over 4 |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=item cache $profile_name |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=item cache %meta |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
Return a curried object with metadata from C<$profile_name> or as |
388
|
|
|
|
|
|
|
explicitly specified. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
If a profile by the name C<$profile_name> doesn't exist, but a backend |
391
|
|
|
|
|
|
|
object by that name does exist, the backend will be returned instead, |
392
|
|
|
|
|
|
|
since the interface for curried caches and backends is almost identical. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
This method can also be called without arguments, in which case is |
395
|
|
|
|
|
|
|
treated as though the C<%meta> hash was empty. |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
See L</METADATA> for details. |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=item curry_cache %meta |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
Return a L<Catalyst::Plugin::Cache::Curried> object, curried with C<%meta>. |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
See L</METADATA> for details. |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=item cache_set $key, $value, %meta |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=item cache_get $key, %meta |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=item cache_remove $key, %meta |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
=item cache_compute $key, $code, %meta |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
These cache operations will call L<choose_cache_backend> with %meta, and |
414
|
|
|
|
|
|
|
then call C<set>, C<get>, C<remove>, or C<compute> on the resulting backend |
415
|
|
|
|
|
|
|
object. |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
If the backend object does not support C<compute> then we emulate it by |
418
|
|
|
|
|
|
|
calling L<cache_get>, and if the returned value is undefined we call the passed |
419
|
|
|
|
|
|
|
code reference, stores the returned value with L<cache_set>, and then returns |
420
|
|
|
|
|
|
|
the value. Inspired by L<CHI>. |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=item choose_cache_backend %meta |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
Select a backend object. This should return undef if no specific backend |
425
|
|
|
|
|
|
|
was selected - its caller will handle getting C<default_cache_backend> |
426
|
|
|
|
|
|
|
on its own. |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
This method is typically used by plugins. |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=item get_cache_backend $name |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
Get a backend object by name. |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=item default_cache_backend |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
Return the default backend object. |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=item temporary_cache_backend |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
When no default cache backend is configured this method might return a |
441
|
|
|
|
|
|
|
backend known to work well with the current L<Catalyst::Engine>. This is |
442
|
|
|
|
|
|
|
a stub. |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=item |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=back |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=head1 METADATA |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=head2 Introduction |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Whenever you set or retrieve a key you may specify additional metadata |
453
|
|
|
|
|
|
|
that will be used to select a specific backend. |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
This metadata is very freeform, and the only key that has any meaning by |
456
|
|
|
|
|
|
|
default is the C<backend> key which can be used to explicitly choose a backend |
457
|
|
|
|
|
|
|
by name. |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
The C<choose_cache_backend> method can be overridden in order to |
460
|
|
|
|
|
|
|
facilitate more intelligent backend selection. For example, |
461
|
|
|
|
|
|
|
L<Catalyst::Plugin::Cache::Choose::KeyRegexes> overrides that method to |
462
|
|
|
|
|
|
|
select a backend based on key regexes. |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
Another example is a L<Catalyst::Plugin::Cache::ControllerNamespacing>, |
465
|
|
|
|
|
|
|
which wraps backends in objects that perform key mangling, in order to |
466
|
|
|
|
|
|
|
keep caches namespaced per controller. |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
However, this is generally left as a hook for larger, more complex |
469
|
|
|
|
|
|
|
applications. Most configurations should make due XXXX |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
The simplest way to dynamically select a backend is based on the |
472
|
|
|
|
|
|
|
L</Cache Profiles> configuration. |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=head2 Meta Data Keys |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
C<choose_cache_backend> is called with some default keys. |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=over 4 |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=item key |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
Supplied by C<cache_get>, C<cache_set>, and C<cache_remove>. |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=item value |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
Supplied by C<cache_set>. |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=item caller |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
The package name of the innermost caller that doesn't match |
491
|
|
|
|
|
|
|
C<qr/Plugin::Cache/>. |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=item caller_frame |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
The entire C<caller($i)> frame of C<caller>. |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=item component |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
The package name of the innermost caller who C<isa> |
500
|
|
|
|
|
|
|
L<Catalyst::Component>. |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=item component_frame |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
This entire C<caller($i)> frame of C<component>. |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=item controller |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
The package name of the innermost caller who C<isa> |
509
|
|
|
|
|
|
|
L<Catalyst::Controller>. |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=item controller_frame |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
This entire C<caller($i)> frame of C<controller>. |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=back |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=head2 Metadata Currying |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
In order to avoid specifying C<%meta> over and over again you may call |
520
|
|
|
|
|
|
|
C<cache> or C<curry_cache> with C<%meta> once, and get back a B<curried |
521
|
|
|
|
|
|
|
cache object>. This object responds to the methods C<get>, C<set>, and |
522
|
|
|
|
|
|
|
C<remove>, by appending its captured metadata and delegating them to |
523
|
|
|
|
|
|
|
C<cache_get>, C<cache_set>, and C<cache_remove>. |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
This is simpler than it sounds. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
Here is an example using currying: |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
my $cache = $c->cache( %meta ); # cache is curried |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
$cache->set( $key, $value ); |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
$cache->get( $key ); |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
And here is an example without using currying: |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
$c->cache_set( $key, $value, %meta ); |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
$c->cache_get( $key, %meta ); |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
See L<Catalyst::Plugin::Cache::Curried> for details. |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=head1 CONFIGURATION |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
$c->config->{'Plugin::Cache'} = { |
546
|
|
|
|
|
|
|
... |
547
|
|
|
|
|
|
|
}; |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
All configuration parameters should be provided in a hash reference |
550
|
|
|
|
|
|
|
under the C<Plugin::Cache> key in the C<config> hash. |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
=head2 Backend Configuration |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
Configuring backend objects is done by adding hash entries under the |
555
|
|
|
|
|
|
|
C<backends> key in the main config. |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
A special case is that the hash key under the C<backend> (singular) key |
558
|
|
|
|
|
|
|
of the main config is assumed to be the backend named C<default>. |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
=over 4 |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=item class |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
Instantiate a backend from a L<Cache> compatible class. E.g. |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
$c->config->{'Plugin::Cache'}{backends}{small_things} = { |
567
|
|
|
|
|
|
|
class => "Cache::Bounded", |
568
|
|
|
|
|
|
|
interval => 1000, |
569
|
|
|
|
|
|
|
size => 10000, |
570
|
|
|
|
|
|
|
}; |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
$c->config->{'Plugin::Cache'}{backends}{large_things} = { |
573
|
|
|
|
|
|
|
class => "Cache::Memcached", |
574
|
|
|
|
|
|
|
data => '1.2.3.4:1234', |
575
|
|
|
|
|
|
|
}; |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
The options in the hash are passed to the class's C<new> method. |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
The class will be C<required> as necessary during setup time. |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=item store |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
Instantiate a backend using a store plugin, e.g. |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
$c->config->{'Plugin::Cache'}{backend} = { |
586
|
|
|
|
|
|
|
store => "FastMmap", |
587
|
|
|
|
|
|
|
}; |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
Store plugins typically require less configuration because they are |
590
|
|
|
|
|
|
|
specialized for L<Catalyst> applications. For example |
591
|
|
|
|
|
|
|
L<Catalyst::Plugin::Cache::Store::FastMmap> will specify a default |
592
|
|
|
|
|
|
|
C<share_file>, and additionally use a subclass of L<Cache::FastMmap> |
593
|
|
|
|
|
|
|
that can also store non reference data. |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
The store plugin must be loaded. |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
=back |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=head2 Cache Profiles |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=over 4 |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=item profiles |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
Supply your own predefined profiles for cache metadata, when using the |
606
|
|
|
|
|
|
|
C<cache> method. |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
For example when you specify |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
$c->config->{'Plugin::Cache'}{profiles}{thumbnails} = { |
611
|
|
|
|
|
|
|
backend => "large_things", |
612
|
|
|
|
|
|
|
}; |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
And then get a cache object like this: |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
$c->cache("thumbnails"); |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
It is the same as if you had done: |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
$c->cache( backend => "large_things" ); |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=back |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=head2 Miscellaneous Configuration |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=over 4 |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=item default_store |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
When you do not specify a C<store> parameter in the backend |
631
|
|
|
|
|
|
|
configuration this one will be used instead. This configuration |
632
|
|
|
|
|
|
|
parameter is not necessary if only one store plugin is loaded. |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=back |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=head1 TERMINOLOGY |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
=over 4 |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=item backend |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
An object that responds to the methods detailed in |
643
|
|
|
|
|
|
|
L<Catalyst::Plugin::Cache::Backend> (or more). |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=item store |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
A plugin that provides backends of a certain type. This is a bit like a |
648
|
|
|
|
|
|
|
factory. |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=item cache |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
Stored key/value pairs of data for easy re-access. |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=item metadata |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
"Extra" information about the item being stored, which can be used to |
657
|
|
|
|
|
|
|
locate an appropriate backend. |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=item curried cache |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
my $cache = $c->cache(type => 'thumbnails'); |
662
|
|
|
|
|
|
|
$cache->set('pic01', $thumbnaildata); |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
A cache which has been pre-configured with a particular set of |
665
|
|
|
|
|
|
|
namespacing data. In the example the cache returned could be one |
666
|
|
|
|
|
|
|
specifically tuned for storing thumbnails. |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
An object that responds to C<get>, C<set>, and C<remove>, and will |
669
|
|
|
|
|
|
|
automatically add metadata to calls to C<< $c->cache_get >>, etc. |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=back |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=head1 SEE ALSO |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
L<Cache> - the generic cache API on CPAN. |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
L<Catalyst::Plugin::Cache::Store> - how to write a store plugin. |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
L<Catalyst::Plugin::Cache::Curried> - the interface for curried caches. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
L<Catalyst::Plugin::Cache::Choose::KeyRegexes> - choose a backend based on |
682
|
|
|
|
|
|
|
regex matching on the keys. Can be used to partition the keyspace. |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
L<Catalyst::Plugin::Cache::ControllerNamespacing> - wrap backend objects in a |
685
|
|
|
|
|
|
|
name mangler so that every controller gets its own keyspace. |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=head1 AUTHOR |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Yuval Kogman, C<nothingmuch@woobling.org> |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
Jos Boumans, C<kane@cpan.org> |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
Copyright (c) Yuval Kogman, 2006. All rights reserved. |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
This library is free software, you can redistribute it and/or modify it under |
698
|
|
|
|
|
|
|
the same terms as Perl itself, as well as under the terms of the MIT license. |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=cut |
701
|
|
|
|
|
|
|
|