clean up the logic a little bit
[dbsrgits/DBIx-Class-Cursor-Cached.git] / lib / DBIx / Class / Cursor / Cached.pm
CommitLineData
f3a32bd9 1package DBIx::Class::Cursor::Cached;
2
3use strict;
4use warnings;
5use 5.6.1;
6use Storable ();
7use Digest::SHA1 ();
a7f2916a 8use Carp::Clan qw/^DBIx::Class/;
f3a32bd9 9
748c3136 10use vars qw($VERSION);
11
e0d8ca03 12$VERSION = '1.000001';
748c3136 13
f3a32bd9 14sub new {
15 my $class = shift;
16 my ($storage, $args, $attrs) = @_;
17 $class = ref $class if ref $class;
18 # This gives us the class the storage object -would- have used
19 # (since cursor_class is inherited Class::Accessor::Grouped type)
20 my $inner_class = (ref $storage)->cursor_class;
21 my $inner = $inner_class->new(@_);
22 if ($attrs->{cache_for}) {
23 my %args = (
24 inner => $inner,
25 cache_for => delete $attrs->{cache_for},
26 cache_object => delete $attrs->{cache_object},
27 # this must be here to ensure the deletes have happened
28 cache_key => $class->_build_cache_key(@_),
29 pos => 0
30 );
31 return bless(\%args, $class);
32 }
33 return $inner; # return object that -would- have been constructed.
34}
35
36sub next {
37 my ($self) = @_;
e0d8ca03 38 return @{($self->{data} ||= $self->_fill_data)->[$self->{pos}++]||[]};
f3a32bd9 39}
40
41sub all {
42 my ($self) = @_;
43 return @{$self->{data} ||= $self->_fill_data};
44}
45
46sub reset {
47 shift->{pos} = 0;
48}
49
50sub _build_cache_key {
51 my ($class, $storage, $args, $attrs) = @_;
c9495327 52 # compose the query and bind values, like as_query(),
53 # so the cache key is only affected by what the database sees
54 # and not any other cruft in $attrs
55 my $ref = $storage->_select_args_to_query(@{$args}[0..2], $attrs);
a7f2916a 56
57 my ($connect_info, $dbname, $username);
58 $connect_info = $storage->_dbi_connect_info;
59 if (my $dbh = $storage->_dbh) {
c9495327 60 $dbname = $dbh->{Name};
61 $username = $dbh->{Username} || '';
a7f2916a 62 } elsif (! ref($connect_info->[0]) ) {
c9495327 63 $dbname = $connect_info->[0];
64 $username = $connect_info->[1] || '';
a7f2916a 65 } else {
66 carp "Invoking connector coderef $connect_info->[0] in order to obtain cache-lookup information";
67 my $dbh = $connect_info->[0]->();
68 $dbname = $dbh->{Name};
69 $username = $dbh->{Username} || '';
c9495327 70 }
71
72 local $Storable::canonical = 1;
73 return Digest::SHA1::sha1_hex(Storable::nfreeze( [ $ref, $dbname, $username ] ));
74
f3a32bd9 75}
76
77sub _fill_data {
78 my ($self) = @_;
79 my $cache = $self->{cache_object};
80 my $key = $self->{cache_key};
81 return $cache->get($key) || do {
82 my $data = [ $self->{inner}->all ];
83 $cache->set($key, $data, $self->{cache_for});
84 $data;
85 };
86}
87
88sub clear_cache {
89 my ($self) = @_;
90 $self->{cache_object}->remove($self->{cache_key});
91 delete $self->{data};
92}
93
2ebeca8e 94sub cache_key { shift->{cache_key} }
95
f3a32bd9 961;
97
98=head1 NAME
99
100DBIx::Class::Cursor::Cached - cursor class with built-in caching support
101
102=head1 SYNOPSIS
103
104 my $schema = SchemaClass->connect(
105 $dsn, $user, $pass, { cursor_class => 'DBIx::Class::Cursor::Cached' }
106 );
107
108 $schema->default_resultset_attributes({
109 cache_object => Cache::FileCache->new({ namespace => 'SchemaClass' }),
110 });
111
112 my $rs = $schema->resultset('CD')->search(undef, { cache_for => 300 });
113
114 my @cds = $rs->all; # fills cache
115
116 $rs = $schema->resultset('CD')->search(undef, { cache_for => 300 });
117 # refresh resultset
118
119 @cds = $rs->all; # uses cache, no SQL run
120
121 $rs->cursor->clear_cache; # deletes data from cache
122
123 @cds = $rs->all; # refills cache
124
125=head1 AUTHOR
126
127Matt S Trout <mst@shadowcat.co.uk> http://www.shadowcat.co.uk/
128
129Initial development sponsored by and (c) Takkle, Inc. 2007
130
131=head1 LICENSE
132
133This library is free software under the same license as perl itself
134
135=cut