3 package Catalyst::Plugin::Cache;
4 use base qw/Class::Data::Inheritable Class::Accessor::Fast/;
10 use Catalyst::Utils ();
14 use Catalyst::Plugin::Cache::Curried;
16 __PACKAGE__->mk_classdata( "_cache_backends" );
17 __PACKAGE__->mk_accessors( "_default_curried_cache" );
22 # set it once per app, not once per plugin,
23 # and don't overwrite if some plugin was wicked
24 $app->_cache_backends({}) unless $app->_cache_backends;
26 my $ret = $app->NEXT::setup( @_ );
28 $app->setup_cache_backends;
33 sub get_default_cache_backend_config {
34 my ( $app, $name ) = @_;
35 $app->config->{cache}{backend} || $app->get_cache_backend_config("default");
38 sub get_cache_backend_config {
39 my ( $app, $name ) = @_;
40 $app->config->{cache}{backends}{$name};
43 sub setup_cache_backends {
46 # give plugins a chance to find things for themselves
47 $app->NEXT::setup_cache_backends;
49 foreach my $name ( keys %{ $app->config->{cache}{backends} } ) {
50 next if $app->get_cache_backend( $name );
51 $app->setup_generic_cache_backend( $name, $app->get_cache_backend_config( $name ) || {} );
54 if ( !$app->get_cache_backend("default") ) {
56 eval { $app->setup_generic_cache_backend( default => $app->get_default_cache_backend_config || {} ) };
60 sub default_cache_store {
62 $app->config->{cache}{default_store} || $app->guess_default_cache_store;
65 sub guess_default_cache_store {
68 my @stores = map { /Cache::Store::(.*)$/ ? $1 : () } $app->registered_plugins;
73 Carp::croak "You must configure a default store type unless you use exactly one store plugin.";
77 sub setup_generic_cache_backend {
78 my ( $app, $name, $config ) = @_;
79 my %config = %$config;
81 if ( my $class = delete $config{class} ) {
82 $app->setup_cache_backend_by_class( $name, $class, %config );
83 } elsif ( my $store = delete $config->{store} || $app->default_cache_store ) {
84 my $method = lc("setup_${store}_cache_backend");
86 Carp::croak "You must load the $store cache store plugin (if it exists). ".
87 "Please consult the Catalyst::Plugin::Cache documentation on how to configure hetrogeneous stores."
88 unless $app->can($method);
90 $app->$method( $name, %config );
92 $app->log->warn("Couldn't setup the cache backend named '$name'");
96 sub setup_cache_backend_by_class {
97 my ( $app, $name, $class, @args ) = @_;
98 Catalyst::Utils::ensure_class_loaded( $class );
99 $app->register_cache_backend( $name => $class->new( @args ) );
102 # end of spaghetti setup DWIM
105 my ( $c, @meta ) = @_;
109 return ( $c->get_preset_curried($name) || $c->get_cache_backend($name) );
111 # be nice and always return the same one for the simplest case
112 return ( $c->_default_curried_cache || $c->_default_curried_cache( $c->curry_cache( @meta ) ) );
114 return $c->curry_cache( @meta );
118 sub construct_curried_cache {
119 my ( $c, @meta ) = @_;
120 return $c->curried_cache_class( @meta )->new( @meta );
123 sub curried_cache_class {
124 my ( $c, @meta ) = @_;
125 $c->config->{cache}{curried_class} || "Catalyst::Plugin::Cache::Curried";
129 my ( $c, @meta ) = @_;
130 return $c->construct_curried_cache( $c, $c->_cache_caller_meta, @meta );
133 sub get_preset_curried {
134 my ( $c, $name ) = @_;
136 if ( ref( my $preset = $c->config->{cache}{profiles}{$name} ) ) {
137 return $preset if Scalar::Util::blessed($preset);
139 my @meta = ( ( ref $preset eq "HASH" ) ? %$preset : @$preset );
140 return $c->curry_cache( @meta );
146 sub get_cache_backend {
147 my ( $c, $name ) = @_;
148 $c->_cache_backends->{$name};
151 sub register_cache_backend {
152 my ( $c, $name, $backend ) = @_;
154 no warnings 'uninitialized';
155 Carp::croak("$backend does not look like a cache backend - "
156 . "it must be an object supporting get, set and remove")
157 unless eval { $backend->can("get") && $backend->can("set") && $backend->can("remove") };
159 $c->_cache_backends->{$name} = $backend;
162 sub unregister_cache_backend {
163 my ( $c, $name ) = @_;
164 delete $c->_cache_backends->{$name};
167 sub default_cache_backend {
169 $c->get_cache_backend( "default" ) || $c->temporary_cache_backend;
172 sub temporary_cache_backend {
174 die "FIXME - make up an in memory cache backend, that hopefully works well for the current engine";
177 sub _cache_caller_meta {
180 my ( $caller, $component, $controller );
182 for my $i ( 0 .. 15 ) { # don't look to far
183 my @info = caller(2 + $i) or last;
185 $caller ||= \@info unless $info[0] =~ /Catalyst::Plugin::Cache/;
186 $component ||= \@info if $info[0]->isa("Catalyst::Component");
187 $controller ||= \@info if $info[0]->isa("Catalyst::Controller");
189 last if $caller && $component && $controller;
194 component => $component,
195 controller => $controller,
199 # this gets a shit name so that the plugins can override a good name
200 sub choose_cache_backend_wrapper {
201 my ( $c, @meta ) = @_;
203 Carp::croak("meta data must be an even sized list") unless @meta % 2 == 0;
207 unless ( exists $meta{'caller'} ) {
208 my %caller = $c->_cache_caller_meta;
209 @meta{keys %caller} = values %caller;
212 # allow the cache client to specify who it wants to cache with (but loeave room for a hook)
213 if ( exists $meta{backend} ) {
214 if ( Scalar::Util::blessed($meta{backend}) ) {
215 return $meta{backend};
217 return $c->get_cache_backend( $meta{backend} ) || $c->default_cache_backend;
221 if ( my $chosen = $c->choose_cache_backend( %meta ) ) {
222 $chosen = $c->get_cache_backend( $chosen ) unless Scalar::Util::blessed($chosen); # if it's a name find it
223 return $chosen if Scalar::Util::blessed($chosen); # only return if it was an object or name lookup worked
226 # die "no such backend"?
227 # currently, we fall back to default
230 return $c->default_cache_backend;
233 sub choose_cache_backend { shift->NEXT::choose_cache_backend( @_ ) } # a convenient fallback
236 my ( $c, $key, $value, @meta ) = @_;
237 $c->choose_cache_backend_wrapper( key => $key, value => $value, @meta )->set( $key, $value );
241 my ( $c, $key, @meta ) = @_;
242 $c->choose_cache_backend_wrapper( key => $key, @meta )->get( $key );
246 my ( $c, $key, @meta ) = @_;
247 $c->choose_cache_backend_wrapper( key => $key, @meta )->remove( $key );
258 Catalyst::Plugin::Cache -
266 # configure a backend or use a store plugin
267 __PACKAGE__->config( cache => {
269 class => "Cache::Bounded",
274 # ... in a controller
277 my ( $self, $c, $id ) = @_;
279 my $cache = $c->cache;
283 unless ( $result = $cache->get( $id ) ) {
284 # ... calulate result ...
285 $c->cache->set( $id, $result );
291 This plugin allows you to use a very simple configuration API without losing
292 the possibility of flexibility when you need it later.
294 Amongst it's features are support for multiple backends, segmentation based on
295 component or controller, keyspace partitioning and so forth, in various sub
304 An object that responds to the methods detailed in
305 L<Catalyst::Plugin::Cache::Backend> (or more).
309 A plugin that provides backends of a certain type. This is a bit like a factory.
313 An object that responds to C<get>, C<set> and C<remove>, and will automatically
314 add meta data to calls to C<< $c->cache_get >>, etc.