0d6e94f93e2e6813b058e958846c11f2268196b5
[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 )
246         ->set( $key, $value, exists $meta{expires} ? $meta{expires} : () );
247 }
248
249 sub cache_get {
250     my ( $c, $key, @meta ) = @_;
251     $c->choose_cache_backend_wrapper( key => $key, @meta )->get( $key );
252 }
253
254 sub cache_remove {
255     my ( $c, $key, @meta ) = @_;
256     $c->choose_cache_backend_wrapper( key => $key, @meta )->remove( $key );
257 }
258
259 __PACKAGE__;
260
261 __END__
262
263 =pod
264
265 =head1 NAME
266
267 Catalyst::Plugin::Cache - Flexible caching support for Catalyst.
268
269 =head1 SYNOPSIS
270
271         use Catalyst qw/
272         Cache
273     /;
274
275     # configure a backend or use a store plugin 
276     __PACKAGE__->config->{cache}{backend} = {
277         class => "Cache::Bounded",
278         # ... params ...
279     };
280
281     # In a controller:
282
283     sub foo : Local {
284         my ( $self, $c, $id ) = @_;
285
286         my $cache = $c->cache;
287
288         my $result;
289
290         unless ( $result = $cache->get( $id ) ) {
291             # ... calculate result ...
292             $c->cache->set( $id, $result );
293         }
294     };
295
296 =head1 DESCRIPTION
297
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.
301
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.
305
306 =head1 METHODS
307
308 =over 4
309
310 =item cache $profile_name
311
312 =item cache %meta
313
314 Return a curried object with metadata from C<$profile_name> or as
315 explicitly specified.
316
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.
320
321 This method can also be called without arguments, in which case is
322 treated as though the C<%meta> hash was empty.
323
324 See L</METADATA> for details.
325
326 =item curry_cache %meta
327
328 Return a L<Catalyst::Plugin::Cache::Curried> object, curried with C<%meta>.
329
330 See L</METADATA> for details.
331
332 =item cache_set $key, $value, %meta
333
334 =item cache_get $key, %meta
335
336 =item cache_remove $key, %meta
337
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.
340
341 =item choose_cache_backend %meta
342
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>
345 on its own.
346
347 This method is typically used by plugins.
348
349 =item get_cache_backend $name
350
351 Get a backend object by name.
352
353 =item default_cache_backend
354
355 Return the default backend object.
356
357 =item temporary_cache_backend
358
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
361 a stub.
362
363 =item 
364
365 =back
366
367 =head1 METADATA
368
369 =head2 Introduction
370
371 Whenever you set or retrieve a key you may specify additional metadata
372 that will be used to select a specific backend.
373
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
376 by name.
377
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.
382
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.
386
387 However, this is generally left as a hook for larger, more complex
388 applications. Most configurations should make due XXXX
389
390 The simplest way to dynamically select a backend is based on the
391 L</Cache Profiles> configuration.
392
393 =head2 Meta Data Keys
394
395 C<choose_cache_backend> is called with some default keys.
396
397 =over 4
398
399 =item key
400
401 Supplied by C<cache_get>, C<cache_set>, and C<cache_remove>.
402
403 =item value
404
405 Supplied by C<cache_set>.
406
407 =item caller
408
409 The package name of the innermost caller that doesn't match
410 C<qr/Plugin::Cache/>.
411
412 =item caller_frame
413
414 The entire C<caller($i)> frame of C<caller>.
415
416 =item component
417
418 The package name of the innermost caller who C<isa>
419 L<Catalyst::Component>.
420
421 =item component_frame
422
423 This entire C<caller($i)> frame of C<component>.
424
425 =item controller
426
427 The package name of the innermost caller who C<isa>
428 L<Catalyst::Controller>.
429
430 =item controller_frame
431
432 This entire C<caller($i)> frame of C<controller>.
433
434 =back
435
436 =head2 Metadata Currying
437
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>.
443
444 This is simpler than it sounds.
445
446 Here is an example using currying:
447
448     my $cache = $c->cache( %meta ); # cache is curried
449
450     $cache->set( $key, $value );
451
452     $cache->get( $key );
453
454 And here is an example without using currying:
455
456     $c->cache_set( $key, $value, %meta );
457
458     $c->cache_get( $key, %meta );
459
460 See L<Catalyst::Plugin::Cache::Curried> for details.
461
462 =head1 CONFIGURATION
463
464     $c->config->{cache} = {
465         ...
466     };
467
468 All configuration parameters should be provided in a hash reference
469 under the C<cache> key in the C<config> hash.
470
471 =head2 Backend Configuration
472
473 Configuring backend objects is done by adding hash entries under the
474 C<backends> key in the main config.
475
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>.
478
479 =over 4
480
481 =item class
482
483 Instantiate a backend from a L<Cache> compatible class. E.g.
484
485     $c->config->{cache}{backends}{small_things} = {
486         class    => "Cache::Bounded",
487         interval => 1000,
488         size     => 10000,
489     };
490     
491     $c->config->{cache}{backends}{large_things} = {
492         class => "Cache::Memcached::Managed",
493         data  => '1.2.3.4:1234',
494     };
495
496 The options in the hash are passed to the class's C<new> method.
497
498 The class will be C<required> as necessary during setup time.
499
500 =item store
501
502 Instantiate a backend using a store plugin, e.g.
503
504     $c->config->{cache}{backend} = {
505         store => "FastMmap",
506     };
507
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.
513
514 The store plugin must be loaded.
515
516 =back
517
518 =head2 Cache Profiles
519
520 =over 4
521
522 =item profiles
523
524 Supply your own predefined profiles for cache metadata, when using the
525 C<cache> method.
526
527 For example when you specify
528
529     $c->config->{cache}{profiles}{thumbnails} = {
530         backend => "large_things",
531     };
532
533 And then get a cache object like this:
534
535     $c->cache("thumbnails");
536
537 It is the same as if you had done:
538
539     $c->cache( backend => "large_things" );
540
541 =back
542
543 =head2 Miscellaneous Configuration
544
545 =over 4
546
547 =item default_store
548
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.
552
553 =back
554
555 =head1 TERMINOLOGY
556
557 =over 4
558
559 =item backend
560
561 An object that responds to the methods detailed in
562 L<Catalyst::Plugin::Cache::Backend> (or more).
563
564 =item store
565
566 A plugin that provides backends of a certain type. This is a bit like a
567 factory.
568
569 =item cache
570
571 Stored key/value pairs of data for easy re-access.
572
573 =item metadata
574
575 "Extra" information about the item being stored, which can be used to
576 locate an appropriate backend.
577
578 =item curried cache
579
580   my $cache = $c->cache(type => 'thumbnails');
581   $cache->set('pic01', $thumbnaildata);
582
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.
586
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.
589
590 =back
591
592 =head1 SEE ALSO
593
594 L<Cache> - the generic cache API on CPAN.
595
596 L<Catalyst::Plugin::Cache::Store> - how to write a store plugin.
597
598 L<Catalyst::Plugin::Cache::Curried> - the interface for curried caches.
599
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.
602
603 L<Catalyst::Plugin::Cache::ControllerNamespacing> - wrap backend objects in a
604 name mangler so that every controller gets its own keyspace.
605
606 =head1 AUTHOR
607
608 Yuval Kogman, C<nothingmuch@woobling.org>
609
610 =head1 COPYRIGHT & LICENSE
611
612 Copyright (c) Yuval Kogman, 2006. All rights reserved.
613
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.
616
617 =cut
618