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