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