3 package Catalyst::Plugin::Cache;
4 use base qw/Class::Data::Inheritable Class::Accessor::Fast/;
12 use Catalyst::Utils ();
16 use Catalyst::Plugin::Cache::Curried;
18 __PACKAGE__->mk_classdata( "_cache_backends" );
19 __PACKAGE__->mk_accessors( "_default_curried_cache" );
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;
28 my $ret = $app->NEXT::setup( @_ );
30 $app->setup_cache_backends;
35 sub get_default_cache_backend_config {
36 my ( $app, $name ) = @_;
37 $app->config->{cache}{backend} || $app->get_cache_backend_config("default");
40 sub get_cache_backend_config {
41 my ( $app, $name ) = @_;
42 $app->config->{cache}{backends}{$name};
45 sub setup_cache_backends {
48 # give plugins a chance to find things for themselves
49 $app->NEXT::setup_cache_backends;
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 ) || {} );
56 if ( !$app->get_cache_backend("default") ) {
58 eval { $app->setup_generic_cache_backend( default => $app->get_default_cache_backend_config || {} ) };
62 sub default_cache_store {
64 $app->config->{cache}{default_store} || $app->guess_default_cache_store;
67 sub guess_default_cache_store {
70 my @stores = map { /Cache::Store::(.*)$/ ? $1 : () } $app->registered_plugins;
75 Carp::croak "You must configure a default store type unless you use exactly one store plugin.";
79 sub setup_generic_cache_backend {
80 my ( $app, $name, $config ) = @_;
81 my %config = %$config;
83 if ( my $class = delete $config{class} ) {
84 $app->setup_cache_backend_by_class( $name, $class, \%config );
85 } elsif ( my $store = delete $config->{store} || $app->default_cache_store ) {
86 my $method = lc("setup_${store}_cache_backend");
88 Carp::croak "You must load the $store cache store plugin (if it exists). ".
89 "Please consult the Catalyst::Plugin::Cache documentation on how to configure hetrogeneous stores."
90 unless $app->can($method);
92 $app->$method( $name, \%config );
94 $app->log->warn("Couldn't setup the cache backend named '$name'");
98 sub setup_cache_backend_by_class {
99 my ( $app, $name, $class, @args ) = @_;
100 Catalyst::Utils::ensure_class_loaded( $class );
101 $app->register_cache_backend( $name => $class->new( @args ) );
104 # end of spaghetti setup DWIM
107 my ( $c, @meta ) = @_;
111 return ( $c->get_preset_curried($name) || $c->get_cache_backend($name) );
113 # be nice and always return the same one for the simplest case
114 return ( $c->_default_curried_cache || $c->_default_curried_cache( $c->curry_cache( @meta ) ) );
116 return $c->curry_cache( @meta );
120 sub construct_curried_cache {
121 my ( $c, @meta ) = @_;
122 return $c->curried_cache_class( @meta )->new( @meta );
125 sub curried_cache_class {
126 my ( $c, @meta ) = @_;
127 $c->config->{cache}{curried_class} || "Catalyst::Plugin::Cache::Curried";
131 my ( $c, @meta ) = @_;
132 return $c->construct_curried_cache( $c, $c->_cache_caller_meta, @meta );
135 sub get_preset_curried {
136 my ( $c, $name ) = @_;
138 if ( ref( my $preset = $c->config->{cache}{profiles}{$name} ) ) {
139 return $preset if Scalar::Util::blessed($preset);
141 my @meta = ( ( ref $preset eq "HASH" ) ? %$preset : @$preset );
142 return $c->curry_cache( @meta );
148 sub get_cache_backend {
149 my ( $c, $name ) = @_;
150 $c->_cache_backends->{$name};
153 sub register_cache_backend {
154 my ( $c, $name, $backend ) = @_;
156 no warnings 'uninitialized';
157 Carp::croak("$backend does not look like a cache backend - "
158 . "it must be an object supporting get, set and remove")
159 unless eval { $backend->can("get") && $backend->can("set") && $backend->can("remove") };
161 $c->_cache_backends->{$name} = $backend;
164 sub unregister_cache_backend {
165 my ( $c, $name ) = @_;
166 delete $c->_cache_backends->{$name};
169 sub default_cache_backend {
171 $c->get_cache_backend( "default" ) || $c->temporary_cache_backend;
174 sub temporary_cache_backend {
176 die "FIXME - make up an in memory cache backend, that hopefully works well for the current engine";
179 sub _cache_caller_meta {
182 my ( $caller, $component, $controller );
184 for my $i ( 0 .. 15 ) { # don't look to far
185 my @info = caller(2 + $i) or last;
187 $caller ||= \@info unless $info[0] =~ /Plugin::Cache/;
188 $component ||= \@info if $info[0]->isa("Catalyst::Component");
189 $controller ||= \@info if $info[0]->isa("Catalyst::Controller");
191 last if $caller && $component && $controller;
194 my ( $caller_pkg, $component_pkg, $controller_pkg ) =
195 map { $_ ? $_->[0] : undef } $caller, $component, $controller;
198 'caller' => $caller_pkg,
199 component => $component_pkg,
200 controller => $controller_pkg,
201 caller_frame => $caller,
202 component_frame => $component,
203 controller_frame => $controller,
207 # this gets a shit name so that the plugins can override a good name
208 sub choose_cache_backend_wrapper {
209 my ( $c, @meta ) = @_;
211 Carp::croak("metadata must be an even sized list") unless @meta % 2 == 0;
215 unless ( exists $meta{'caller'} ) {
216 my %caller = $c->_cache_caller_meta;
217 @meta{keys %caller} = values %caller;
220 # allow the cache client to specify who it wants to cache with (but loeave room for a hook)
221 if ( exists $meta{backend} ) {
222 if ( Scalar::Util::blessed($meta{backend}) ) {
223 return $meta{backend};
225 return $c->get_cache_backend( $meta{backend} ) || $c->default_cache_backend;
229 if ( my $chosen = $c->choose_cache_backend( %meta ) ) {
230 $chosen = $c->get_cache_backend( $chosen ) unless Scalar::Util::blessed($chosen); # if it's a name find it
231 return $chosen if Scalar::Util::blessed($chosen); # only return if it was an object or name lookup worked
234 # die "no such backend"?
235 # currently, we fall back to default
238 return $c->default_cache_backend;
241 sub choose_cache_backend { shift->NEXT::choose_cache_backend( @_ ) } # a convenient fallback
244 my ( $c, $key, $value, %meta ) = @_;
245 $c->choose_cache_backend_wrapper( key => $key, value => $value, %meta )
246 ->set( $key, $value, exists $meta{expires} ? $meta{expires} : () );
250 my ( $c, $key, @meta ) = @_;
251 $c->choose_cache_backend_wrapper( key => $key, @meta )->get( $key );
255 my ( $c, $key, @meta ) = @_;
256 $c->choose_cache_backend_wrapper( key => $key, @meta )->remove( $key );
267 Catalyst::Plugin::Cache - Flexible caching support for Catalyst.
275 # configure a backend or use a store plugin
276 __PACKAGE__->config->{cache}{backend} = {
277 class => "Cache::Bounded",
284 my ( $self, $c, $id ) = @_;
286 my $cache = $c->cache;
290 unless ( $result = $cache->get( $id ) ) {
291 # ... calculate result ...
292 $c->cache->set( $id, $result );
298 This plugin gives you access to a variety of systems for caching
299 data. It allows you to use a very simple configuration API, while
300 maintaining the possibility of flexibility when you need it later.
302 Among its features are support for multiple backends, segmentation based
303 on component or controller, keyspace partitioning, and so more, in
304 various subsidiary plugins.
310 =item cache $profile_name
314 Return a curried object with metadata from C<$profile_name> or as
315 explicitly specified.
317 If a profile by the name C<$profile_name> doesn't exist, but a backend
318 object by that name does exist, the backend will be returned instead,
319 since the interface for curried caches and backends is almost identical.
321 This method can also be called without arguments, in which case is
322 treated as though the C<%meta> hash was empty.
324 See L</METADATA> for details.
326 =item curry_cache %meta
328 Return a L<Catalyst::Plugin::Cache::Curried> object, curried with C<%meta>.
330 See L</METADATA> for details.
332 =item cache_set $key, $value, %meta
334 =item cache_get $key, %meta
336 =item cache_remove $key, %meta
338 These cache operations will call L<choose_cache_backend> with %meta, and
339 then call C<set>, C<get>, or C<remove> on the resulting backend object.
341 =item choose_cache_backend %meta
343 Select a backend object. This should return undef if no specific backend
344 was selected - its caller will handle getting C<default_cache_backend>
347 This method is typically used by plugins.
349 =item get_cache_backend $name
351 Get a backend object by name.
353 =item default_cache_backend
355 Return the default backend object.
357 =item temporary_cache_backend
359 When no default cache backend is configured this method might return a
360 backend known to work well with the current L<Catalyst::Engine>. This is
371 Whenever you set or retrieve a key you may specify additional metadata
372 that will be used to select a specific backend.
374 This metadata is very freeform, and the only key that has any meaning by
375 default is the C<backend> key which can be used to explicitly choose a backend
378 The C<choose_cache_backend> method can be overridden in order to
379 facilitate more intelligent backend selection. For example,
380 L<Catalyst::Plugin::Cache::Choose::KeyRegexes> overrides that method to
381 select a backend based on key regexes.
383 Another example is a L<Catalyst::Plugin::Cache::ControllerNamespacing>,
384 which wraps backends in objects that perform key mangling, in order to
385 keep caches namespaced per controller.
387 However, this is generally left as a hook for larger, more complex
388 applications. Most configurations should make due XXXX
390 The simplest way to dynamically select a backend is based on the
391 L</Cache Profiles> configuration.
393 =head2 Meta Data Keys
395 C<choose_cache_backend> is called with some default keys.
401 Supplied by C<cache_get>, C<cache_set>, and C<cache_remove>.
405 Supplied by C<cache_set>.
409 The package name of the innermost caller that doesn't match
410 C<qr/Plugin::Cache/>.
414 The entire C<caller($i)> frame of C<caller>.
418 The package name of the innermost caller who C<isa>
419 L<Catalyst::Component>.
421 =item component_frame
423 This entire C<caller($i)> frame of C<component>.
427 The package name of the innermost caller who C<isa>
428 L<Catalyst::Controller>.
430 =item controller_frame
432 This entire C<caller($i)> frame of C<controller>.
436 =head2 Metadata Currying
438 In order to avoid specifying C<%meta> over and over again you may call
439 C<cache> or C<curry_cache> with C<%meta> once, and get back a B<curried
440 cache object>. This object responds to the methods C<get>, C<set>, and
441 C<remove>, by appending its captured metadata and delegating them to
442 C<cache_get>, C<cache_set>, and C<cache_remove>.
444 This is simpler than it sounds.
446 Here is an example using currying:
448 my $cache = $c->cache( %meta ); # cache is curried
450 $cache->set( $key, $value );
454 And here is an example without using currying:
456 $c->cache_set( $key, $value, %meta );
458 $c->cache_get( $key, %meta );
460 See L<Catalyst::Plugin::Cache::Curried> for details.
464 $c->config->{cache} = {
468 All configuration parameters should be provided in a hash reference
469 under the C<cache> key in the C<config> hash.
471 =head2 Backend Configuration
473 Configuring backend objects is done by adding hash entries under the
474 C<backends> key in the main config.
476 A special case is that the hash key under the C<backend> (singular) key
477 of the main config is assumed to be the backend named C<default>.
483 Instantiate a backend from a L<Cache> compatible class. E.g.
485 $c->config->{cache}{backends}{small_things} = {
486 class => "Cache::Bounded",
491 $c->config->{cache}{backends}{large_things} = {
492 class => "Cache::Memcached::Managed",
493 data => '1.2.3.4:1234',
496 The options in the hash are passed to the class's C<new> method.
498 The class will be C<required> as necessary during setup time.
502 Instantiate a backend using a store plugin, e.g.
504 $c->config->{cache}{backend} = {
508 Store plugins typically require less configuration because they are
509 specialized for L<Catalyst> applications. For example
510 L<Catalyst::Plugin::Cache::Store::FastMmap> will specify a default
511 C<share_file>, and additionally use a subclass of L<Cache::FastMmap>
512 that can also store non reference data.
514 The store plugin must be loaded.
518 =head2 Cache Profiles
524 Supply your own predefined profiles for cache metadata, when using the
527 For example when you specify
529 $c->config->{cache}{profiles}{thumbnails} = {
530 backend => "large_things",
533 And then get a cache object like this:
535 $c->cache("thumbnails");
537 It is the same as if you had done:
539 $c->cache( backend => "large_things" );
543 =head2 Miscellaneous Configuration
549 When you do not specify a C<store> parameter in the backend
550 configuration this one will be used instead. This configuration
551 parameter is not necessary if only one store plugin is loaded.
561 An object that responds to the methods detailed in
562 L<Catalyst::Plugin::Cache::Backend> (or more).
566 A plugin that provides backends of a certain type. This is a bit like a
571 Stored key/value pairs of data for easy re-access.
575 "Extra" information about the item being stored, which can be used to
576 locate an appropriate backend.
580 my $cache = $c->cache(type => 'thumbnails');
581 $cache->set('pic01', $thumbnaildata);
583 A cache which has been pre-configured with a particular set of
584 namespacing data. In the example the cache returned could be one
585 specifically tuned for storing thumbnails.
587 An object that responds to C<get>, C<set>, and C<remove>, and will
588 automatically add metadata to calls to C<< $c->cache_get >>, etc.
594 L<Cache> - the generic cache API on CPAN.
596 L<Catalyst::Plugin::Cache::Store> - how to write a store plugin.
598 L<Catalyst::Plugin::Cache::Curried> - the interface for curried caches.
600 L<Catalyst::Plugin::Cache::Choose::KeyRegexes> - choose a backend based on
601 regex matching on the keys. Can be used to partition the keyspace.
603 L<Catalyst::Plugin::Cache::ControllerNamespacing> - wrap backend objects in a
604 name mangler so that every controller gets its own keyspace.
608 Yuval Kogman, C<nothingmuch@woobling.org>
610 =head1 COPYRIGHT & LICENSE
612 Copyright (c) Yuval Kogman, 2006. All rights reserved.
614 This library is free software, you can redistribute it and/or modify it under
615 the same terms as Perl itself, as well as under the terms of the MIT license.