3 package Catalyst::Plugin::Cache;
6 with 'Catalyst::ClassData';
11 use Catalyst::Utils ();
14 use Scalar::Util qw/ blessed /;
15 use Catalyst::Plugin::Cache::Curried;
17 __PACKAGE__->mk_classdata( "_cache_backends" );
18 has _default_curried_cache => (
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;
30 my $ret = $app->maybe::next::method( @_ );
32 $app->setup_cache_backends;
38 sub _get_cache_plugin_config {
40 my $config = $app->config->{'Plugin::Cache'};
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!'
55 sub get_default_cache_backend_config {
56 my ( $app, $name ) = @_;
57 $app->_get_cache_plugin_config->{backend} || $app->get_cache_backend_config("default");
60 sub get_cache_backend_config {
61 my ( $app, $name ) = @_;
62 $app->_get_cache_plugin_config->{backends}{$name};
65 sub setup_cache_backends {
68 # give plugins a chance to find things for themselves
69 $app->maybe::next::method;
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 ) || {} );
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
88 $app->setup_generic_cache_backend( default => $app->get_default_cache_backend_config || {} );
94 sub default_cache_store {
96 $app->_get_cache_plugin_config->{default_store} || $app->guess_default_cache_store;
99 sub guess_default_cache_store {
102 my @stores = map { /Cache::Store::(.*)$/ ? $1 : () } $app->registered_plugins;
104 if ( @stores == 1 ) {
107 Carp::croak "You must configure a default store type unless you use exactly one store plugin.";
111 sub setup_generic_cache_backend {
112 my ( $app, $name, $config ) = @_;
113 my %config = %$config;
115 if ( my $class = delete $config{class} ) {
117 ### try as list and as hashref, collect the
118 ### error if things go wrong
119 ### if all goes well, exit the loop
121 for my $aref ( [%config], [\%config] ) {
122 eval { $app->setup_cache_backend_by_class(
123 $name, $class, @$aref
125 } ? do { @errors = (); last }
126 : push @errors, "\t$@";
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;
132 } elsif ( my $store = delete $config->{store} || $app->default_cache_store ) {
133 my $method = lc("setup_${store}_cache_backend");
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);
139 $app->$method( $name, \%config );
141 $app->log->warn("Couldn't setup the cache backend named '$name'");
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 ) );
151 # end of spaghetti setup DWIM
154 my ( $c, @meta ) = @_;
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 ) ) );
163 return $c->curry_cache( @meta );
167 sub construct_curried_cache {
168 my ( $c, @meta ) = @_;
169 return $c->curried_cache_class( @meta )->new( @meta );
172 sub curried_cache_class {
173 my ( $c, @meta ) = @_;
174 $c->_get_cache_plugin_config->{curried_class} || "Catalyst::Plugin::Cache::Curried";
178 my ( $c, @meta ) = @_;
179 return $c->construct_curried_cache( $c, $c->_cache_caller_meta, @meta );
182 sub get_preset_curried {
183 my ( $c, $name ) = @_;
185 if ( ref( my $preset = $c->_get_cache_plugin_config->{profiles}{$name} ) ) {
186 return $preset if Scalar::Util::blessed($preset);
188 my @meta = ( ( ref $preset eq "HASH" ) ? %$preset : @$preset );
189 return $c->curry_cache( @meta );
195 sub get_cache_backend {
196 my ( $c, $name ) = @_;
197 $c->_cache_backends->{$name};
200 sub register_cache_backend {
201 my ( $c, $name, $backend ) = @_;
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") };
208 $c->_cache_backends->{$name} = $backend;
211 sub unregister_cache_backend {
212 my ( $c, $name ) = @_;
213 delete $c->_cache_backends->{$name};
216 sub default_cache_backend {
218 $c->get_cache_backend( "default" ) || $c->temporary_cache_backend;
221 sub temporary_cache_backend {
223 die "FIXME - make up an in memory cache backend, that hopefully works well for the current engine";
226 sub _cache_caller_meta {
229 my ( $caller, $component, $controller );
231 for my $i ( 0 .. 15 ) { # don't look to far
232 my @info = caller(2 + $i) or last;
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");
238 last if $caller && $component && $controller;
241 my ( $caller_pkg, $component_pkg, $controller_pkg ) =
242 map { $_ ? $_->[0] : undef } $caller, $component, $controller;
245 'caller' => $caller_pkg,
246 component => $component_pkg,
247 controller => $controller_pkg,
248 caller_frame => $caller,
249 component_frame => $component,
250 controller_frame => $controller,
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 ) = @_;
258 Carp::croak("metadata must be an even sized list") unless @meta % 2 == 0;
262 unless ( exists $meta{'caller'} ) {
263 my %caller = $c->_cache_caller_meta;
264 @meta{keys %caller} = values %caller;
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};
272 return $c->get_cache_backend( $meta{backend} ) || $c->default_cache_backend;
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
281 # die "no such backend"?
282 # currently, we fall back to default
285 return $c->default_cache_backend;
288 sub choose_cache_backend { shift->maybe::next::method( @_ ) } # a convenient fallback
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} : () );
297 my ( $c, $key, @meta ) = @_;
298 $c->choose_cache_backend_wrapper( key => $key, @meta )->get( $key );
302 my ( $c, $key, @meta ) = @_;
303 $c->choose_cache_backend_wrapper( key => $key, @meta )->remove( $key );
307 my ($c, $key, $code, %meta) = @_;
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} : () );
314 Carp::croak "must specify key and code" unless defined($key) && defined($code);
316 my $value = $c->cache_get( $key, %meta );
317 if ( !defined $value ) {
319 $c->cache_set( $key, $value, %meta );
332 Catalyst::Plugin::Cache - Flexible caching support for Catalyst.
340 # configure a backend or use a store plugin
341 __PACKAGE__->config->{'Plugin::Cache'}{backend} = {
342 class => "Cache::Bounded",
343 # ... params for Cache::Bounded...
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'],
357 my ( $self, $c, $id ) = @_;
359 my $cache = $c->cache;
363 unless ( $result = $cache->get( $id ) ) {
364 # ... calculate result ...
365 $c->cache->set( $id, $result );
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.
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.
383 =item cache $profile_name
387 Return a curried object with metadata from C<$profile_name> or as
388 explicitly specified.
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.
394 This method can also be called without arguments, in which case is
395 treated as though the C<%meta> hash was empty.
397 See L</METADATA> for details.
399 =item curry_cache %meta
401 Return a L<Catalyst::Plugin::Cache::Curried> object, curried with C<%meta>.
403 See L</METADATA> for details.
405 =item cache_set $key, $value, %meta
407 =item cache_get $key, %meta
409 =item cache_remove $key, %meta
411 =item cache_compute $key, $code, %meta
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
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>.
422 =item choose_cache_backend %meta
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>
428 This method is typically used by plugins.
430 =item get_cache_backend $name
432 Get a backend object by name.
434 =item default_cache_backend
436 Return the default backend object.
438 =item temporary_cache_backend
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
452 Whenever you set or retrieve a key you may specify additional metadata
453 that will be used to select a specific backend.
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
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.
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.
468 However, this is generally left as a hook for larger, more complex
469 applications. Most configurations should make due XXXX
471 The simplest way to dynamically select a backend is based on the
472 L</Cache Profiles> configuration.
474 =head2 Meta Data Keys
476 C<choose_cache_backend> is called with some default keys.
482 Supplied by C<cache_get>, C<cache_set>, and C<cache_remove>.
486 Supplied by C<cache_set>.
490 The package name of the innermost caller that doesn't match
491 C<qr/Plugin::Cache/>.
495 The entire C<caller($i)> frame of C<caller>.
499 The package name of the innermost caller who C<isa>
500 L<Catalyst::Component>.
502 =item component_frame
504 This entire C<caller($i)> frame of C<component>.
508 The package name of the innermost caller who C<isa>
509 L<Catalyst::Controller>.
511 =item controller_frame
513 This entire C<caller($i)> frame of C<controller>.
517 =head2 Metadata Currying
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>.
525 This is simpler than it sounds.
527 Here is an example using currying:
529 my $cache = $c->cache( %meta ); # cache is curried
531 $cache->set( $key, $value );
535 And here is an example without using currying:
537 $c->cache_set( $key, $value, %meta );
539 $c->cache_get( $key, %meta );
541 See L<Catalyst::Plugin::Cache::Curried> for details.
545 $c->config->{'Plugin::Cache'} = {
549 All configuration parameters should be provided in a hash reference
550 under the C<Plugin::Cache> key in the C<config> hash.
552 =head2 Backend Configuration
554 Configuring backend objects is done by adding hash entries under the
555 C<backends> key in the main config.
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>.
564 Instantiate a backend from a L<Cache> compatible class. E.g.
566 $c->config->{'Plugin::Cache'}{backends}{small_things} = {
567 class => "Cache::Bounded",
572 $c->config->{'Plugin::Cache'}{backends}{large_things} = {
573 class => "Cache::Memcached",
574 data => '1.2.3.4:1234',
577 The options in the hash are passed to the class's C<new> method.
579 The class will be C<required> as necessary during setup time.
583 Instantiate a backend using a store plugin, e.g.
585 $c->config->{'Plugin::Cache'}{backend} = {
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.
595 The store plugin must be loaded.
599 =head2 Cache Profiles
605 Supply your own predefined profiles for cache metadata, when using the
608 For example when you specify
610 $c->config->{'Plugin::Cache'}{profiles}{thumbnails} = {
611 backend => "large_things",
614 And then get a cache object like this:
616 $c->cache("thumbnails");
618 It is the same as if you had done:
620 $c->cache( backend => "large_things" );
624 =head2 Miscellaneous Configuration
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.
642 An object that responds to the methods detailed in
643 L<Catalyst::Plugin::Cache::Backend> (or more).
647 A plugin that provides backends of a certain type. This is a bit like a
652 Stored key/value pairs of data for easy re-access.
656 "Extra" information about the item being stored, which can be used to
657 locate an appropriate backend.
661 my $cache = $c->cache(type => 'thumbnails');
662 $cache->set('pic01', $thumbnaildata);
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.
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.
675 L<Cache> - the generic cache API on CPAN.
677 L<Catalyst::Plugin::Cache::Store> - how to write a store plugin.
679 L<Catalyst::Plugin::Cache::Curried> - the interface for curried caches.
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.
684 L<Catalyst::Plugin::Cache::ControllerNamespacing> - wrap backend objects in a
685 name mangler so that every controller gets its own keyspace.
689 Yuval Kogman, C<nothingmuch@woobling.org>
691 Jos Boumans, C<kane@cpan.org>
693 =head1 COPYRIGHT & LICENSE
695 Copyright (c) Yuval Kogman, 2006. All rights reserved.
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.