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