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