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