f232541b378756eb4c59da199152877d025898ad
[srvgit] / Koha / Cache / Object.pm
1 package Koha::Cache::Object;
2
3 # Copyright 2013 C & P Bibliography Services
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 =head1 NAME
21
22 Koha::Cache::Object - Tie-able class for caching objects
23
24 =head1 SYNOPSIS
25
26     my $cache = Koha::Cache->new();
27     my $scalar = Koha::Cache->create_scalar(
28         {
29             'key'         => 'whatever',
30             'timeout'     => 2,
31             'constructor' => sub { return 'stuff'; },
32         }
33     );
34     my %hash = Koha::Cache->create_hash(
35         {
36             'key'         => 'whateverelse',
37             'timeout'     => 2,
38             'constructor' => sub { return { 'stuff' => 'nonsense' }; },
39         }
40     );
41
42 =head1 DESCRIPTION
43
44 Do not use this class directly. It is tied to variables by Koha::Cache
45 for transparent cache access. If you choose to ignore this warning, you
46 should be aware that it is disturbingly polymorphic and supports both
47 scalars and hashes, with arrays a potential future addition.
48
49 =head1 TIE METHODS
50
51 =cut
52
53 use strict;
54 use warnings;
55 use Carp;
56
57 use base qw(Class::Accessor);
58
59 __PACKAGE__->mk_ro_accessors(
60     qw( allowupdate arguments cache cache_type constructor destructor inprocess key lastupdate timeout unset value )
61 );
62
63 # General/SCALAR routines
64
65 sub TIESCALAR {
66     my ( $class, $self ) = @_;
67
68     $self->{'datatype'}  ||= 'SCALAR';
69     $self->{'arguments'} ||= [];
70     if ( defined $self->{'preload'} ) {
71         $self->{'value'} = &{ $self->{'preload'} }( @{ $self->{'arguments'} } );
72         if ( defined( $self->{'cache'} ) ) {
73             $self->{'cache'}->set_in_cache( $self->{'key'}, $self->{'value'},
74                 { expiry => $self->{'timeout'} } );
75         }
76         $self->{'lastupdate'} = time;
77     }
78     return bless $self, $class;
79 }
80
81 sub FETCH {
82     my ( $self, $index ) = @_;
83
84     my $now = time;
85
86     if ( !( $self->{'inprocess'} && defined( $self->{'value'} ) )
87         && $self->{'cache'} )
88     {
89         $self->{'value'} = $self->{'cache'}->get_from_cache( $self->{'key'} );
90         $self->{'lastupdate'} = $now;
91     }
92
93     if (   !defined $self->{'value'}
94         || ( defined $index && !exists $self->{'value'}->{$index} )
95         || !defined $self->{'lastupdate'}
96         || ( $now - $self->{'lastupdate'} > $self->{'timeout'} ) )
97     {
98         $self->{'value'} =
99           &{ $self->{'constructor'} }( @{ $self->{'arguments'} },
100             $self->{'value'}, $index );
101         if ( defined( $self->{'cache'} ) ) {
102             $self->{'cache'}->set_in_cache( $self->{'key'}, $self->{'value'},
103                 { expiry => $self->{'timeout'} } );
104         }
105         $self->{'lastupdate'} = $now;
106     }
107     if ( $self->{'datatype'} eq 'HASH' && defined $index ) {
108         return $self->{'value'}->{$index};
109     }
110     return $self->{'value'};
111 }
112
113 sub STORE {
114     my $value = pop @_;
115     my ( $self, $index ) = @_;
116
117     if ( $self->{'datatype'} eq 'HASH' && defined($index) ) {
118         $self->{'value'}->{$index} = $value;
119     }
120     else {
121         $self->{'value'} = $value;
122     }
123     if (   defined( $self->{'allowupdate'} )
124         && $self->{'allowupdate'}
125         && defined( $self->{'cache'} ) )
126     {
127         $self->{'cache'}->set_in_cache( $self->{'key'}, $self->{'value'},
128             { expiry => $self->{'timeout'} },
129         );
130     }
131
132     return $self->{'value'};
133 }
134
135 sub DESTROY {
136     my ($self) = @_;
137
138     if ( defined( $self->{'destructor'} ) ) {
139         &{ $self->{'destructor'} }( @{ $self->{'arguments'} } );
140     }
141
142     if (   defined( $self->{'unset'} )
143         && $self->{'unset'}
144         && defined( $self->{'cache'} ) )
145     {
146         $self->{'cache'}->clear_from_cache( $self->{'key'} );
147     }
148
149     undef $self->{'value'};
150
151     return $self;
152 }
153
154 # HASH-specific routines
155
156 sub TIEHASH {
157     my ( $class, $self, @args ) = @_;
158     $self->{'datatype'} = 'HASH';
159     return TIESCALAR( $class, $self, @args );
160 }
161
162 sub DELETE {
163     my ( $self, $index ) = @_;
164     delete $self->{'value'}->{$index};
165     return $self->STORE( $self->{'value'} );
166 }
167
168 sub EXISTS {
169     my ( $self, $index ) = @_;
170     $self->FETCH($index);
171     return exists $self->{'value'}->{$index};
172 }
173
174 sub FIRSTKEY {
175     my ($self) = @_;
176     $self->FETCH;
177     $self->{'iterator'} = [ keys %{ $self->{'value'} } ];
178     return $self->NEXTKEY;
179 }
180
181 sub NEXTKEY {
182     my ($self) = @_;
183     return shift @{ $self->{'iterator'} };
184 }
185
186 sub SCALAR {
187     my ($self) = @_;
188     $self->FETCH;
189     return scalar %{ $self->{'value'} }
190       if ( ref( $self->{'value'} ) eq 'HASH' );
191     return;
192 }
193
194 sub CLEAR {
195     my ($self) = @_;
196     return $self->DESTROY;
197 }
198
199 # ARRAY-specific routines
200
201 =head1 SEE ALSO
202
203 Koha::Cache, tie, perltie
204
205 =head1 AUTHOR
206
207 Jared Camins-Esakov, E<lt>jcamins@cpbibliography.comE<gt>
208
209 =cut
210
211 1;
212
213 __END__