File Coverage

blib/lib/Class/Simple/Readonly/Cached.pm
Criterion Covered Total %
statement 89 95 93.6
branch 44 56 78.5
condition 2 6 33.3
subroutine 8 9 88.8
pod 3 3 100.0
total 146 169 86.3


line stmt bran cond sub pod time code
1             package Class::Simple::Readonly::Cached;
2              
3 4     4   464228 use strict;
  4         30  
  4         113  
4 4     4   22 use warnings;
  4         9  
  4         95  
5 4     4   23 use Carp;
  4         22  
  4         219  
6 4     4   2121 use Class::Simple;
  4         22918  
  4         3177  
7              
8             my @ISA = ('Class::Simple');
9              
10             =head1 NAME
11              
12             Class::Simple::Readonly::Cached - cache messages to an object
13              
14             =head1 VERSION
15              
16             Version 0.07
17              
18             =cut
19              
20             our $VERSION = '0.07';
21              
22             =head1 SYNOPSIS
23              
24             A sub-class of L<Class::Simple> which caches calls to read
25             the status of an object that are otherwise expensive.
26              
27             It is up to the caller to maintain the cache if the object comes out of sync with the cache,
28             for example by changing its state.
29              
30             You can use this class to create a caching layer to an object of any class
31             that works on objects which doesn't change its state based on input:
32              
33             $val = $obj->val();
34             $val = $obj->val(a => 'b');
35              
36             =head1 SUBROUTINES/METHODS
37              
38             =head2 new
39              
40             Creates a Class::Simple::Readonly::Cached object.
41              
42             It takes one mandatory parameter: cache,
43             which is either an object which understands get() and set() calls,
44             such as an L<CHI> object;
45             or is a reference to a hash where the return values are to be stored.
46              
47             It takes one optional argument: object,
48             which is an object which is taken to be the object to be cached.
49             If not given, an object of the class L<Class::Simple> is instantiated
50             and that is used.
51              
52             use Gedcom;
53              
54             my %hash;
55             my $person = Gedcom::Person->new();
56             ... # Set up some data
57             my $object = Class::Simple::Readonly::Cached(object => $person, cache => \%hash);
58             my $father1 = $object->father(); # Will call gedcom->father() to get the person's father
59             my $father2 = $object->father(); # Will retrieve the father from the cache without calling person->father()
60              
61             =cut
62              
63             sub new {
64 5     5 1 117944 my $proto = shift;
65 5   33     36 my $class = ref($proto) || $proto;
66              
67 5 50       18 return unless(defined($class));
68              
69 5         13 my %args;
70 5 100       38 if(ref($_[0]) eq 'HASH') {
    50          
    50          
71 1         2 %args = %{$_[0]};
  1         4  
72             } elsif(ref($_[0])) {
73 0         0 Carp::carp('Usage: ', __PACKAGE__, '->new(cache => $cache [, object => $object ], %args)');
74 0         0 return;
75             } elsif(@_ % 2 == 0) {
76 4         16 %args = @_;
77             }
78              
79 5 100       23 if(!$args{'cache'}) {
80 2         6 Carp::carp('Usage: ', __PACKAGE__, '->new(cache => $cache [, object => $object ], %args)');
81 2         855 return;
82             }
83              
84 3 50       14 if(!defined($args{'object'})) {
85 0         0 $args{'object'} = Class::Simple->new(%args);
86             }
87              
88 3         10 return bless \%args, $class;
89             }
90              
91             =head2 object
92              
93             Return the encapsulated object
94              
95             =cut
96              
97             sub object
98             {
99 1     1 1 4 my $self = shift;
100              
101 1         5 return $self->{'object'};
102             }
103              
104             sub _caller_class
105             {
106 0     0   0 my $self = shift;
107              
108 0 0       0 if(ref($self->{'object'}) eq 'Class::Simple') {
109             # return $self->SUPER::_caller_class(@_);
110 0         0 return $self->Class::Simple::_caller_class(@_);
111             }
112             }
113              
114             =head2 state
115              
116             Returns the state of the object
117              
118             print Data::Dumper->new([$obj->state()]->Dump();
119              
120             =cut
121              
122             sub state {
123 5     5 1 3598 my $self = shift;
124              
125 5         27 return { hits => $self->{_hits}, misses => $self->{_misses} };
126             }
127              
128             sub AUTOLOAD {
129 41     41   13663 our $AUTOLOAD;
130 41         80 my $param = $AUTOLOAD;
131 41         258 $param =~ s/.*:://;
132              
133 41         86 my $self = shift;
134 41         84 my $cache = $self->{'cache'};
135              
136 41 100       115 if($param eq 'DESTROY') {
137 3 50       12 if($cache) {
138 3 100       15 if(ref($cache) eq 'HASH') {
139 2         5 while(my($key, $value) = each %{$cache}) {
  12         37  
140 10         20 delete $cache->{$key};
141             }
142 2         16 return;
143             }
144 1 50 33     46 if(defined($^V) && ($^V ge 'v5.14.0')) {
145 1 50       6 return if ${^GLOBAL_PHASE} eq 'DESTRUCT'; # >= 5.14.0 only
146             }
147 1         11 $cache->clear();
148             }
149 1         31 return;
150             }
151              
152             # my $func = $self->{'object'} . "::$param";
153 38         60 my $func = $param;
154              
155             # if($param =~ /^[gs]et_/) {
156             # # $param = "SUPER::$param";
157             # return $object->$func(\@_);
158             # }
159              
160 38         158 my $key = $param . '::' . join('::', grep defined, @_);
161              
162             # Retrieving a value
163 38         92 my $rc;
164 38 100       115 if(ref($cache) eq 'HASH') {
165 21         42 $rc = $cache->{$key};
166             } else {
167 17         61 $rc = $cache->get($key);
168             }
169 38 100       1202 if(defined($rc)) {
170 18 50       59 die $key if($rc eq 'never');
171 18 100       46 if(ref($rc) eq 'ARRAY') {
172 6         15 $self->{_hits}{$key}++;
173 6         10 my @foo = @{$rc};
  6         18  
174 6 100       17 if(wantarray) {
175 5 100       14 if(defined($foo[0])) {
176 4 50       11 die $key if($foo[0] eq __PACKAGE__ . ">UNDEF<");
177 4 50       12 die $key if($foo[0] eq 'never');
178             }
179 5         7 return @{$rc};
  5         42  
180             }
181 1         5 return pop @foo;
182             }
183 12 100       29 if($rc eq __PACKAGE__ . '>UNDEF<') {
184 2         6 $self->{_hits}{$key}++;
185 2         10 return;
186             }
187 10 100       24 if(!wantarray) {
188 9         26 $self->{_hits}{$key}++;
189 9         47 return $rc;
190             }
191             }
192 21         75 $self->{_misses}{$key}++;
193 21         51 my $object = $self->{'object'};
194 21 100       50 if(wantarray) {
195 8         30 my @rc = $object->$func(@_);
196 8 100       45 if(scalar(@rc) == 0) {
197 2         7 return;
198             }
199 6 100       17 if(ref($cache) eq 'HASH') {
200 3         8 $cache->{$key} = \@rc;
201             } else {
202 3         11 $cache->set($key, \@rc, 'never');
203             }
204 6         421 return @rc;
205             }
206 13         44 $rc = $object->$func(@_);
207 13 100       78 if(!defined($rc)) {
208 2 100       9 if(ref($cache) eq 'HASH') {
209 1         4 $cache->{$key} = __PACKAGE__ . '>UNDEF<';
210             } else {
211 1         4 $cache->set($key, __PACKAGE__ . '>UNDEF<', 'never');
212             }
213 2         128 return;
214             }
215 11 100       28 if(ref($cache) eq 'HASH') {
216 7         41 return $cache->{$key} = $rc;
217             }
218 4         14 return $cache->set($key, $rc, 'never');
219             }
220              
221             =head1 AUTHOR
222              
223             Nigel Horne, C<< <njh at bandsman.co.uk> >>
224              
225             =head1 BUGS
226              
227             Doesn't work with L<Memoize>.
228              
229             Please report any bugs or feature requests to C<bug-class-simple-readonly-cached at rt.cpan.org>,
230             or through the web interface at
231             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Class-Simple-Readonly-Cached>.
232             I will be notified, and then you'll
233             automatically be notified of progress on your bug as I make changes.
234              
235             params() returns a ref which means that calling routines can change the hash
236             for other routines.
237             Take a local copy before making amendments to the table if you don't want unexpected
238             things to happen.
239              
240             =head1 SEE ALSO
241              
242             L<Class::Simple>, L<CHI>
243              
244             =head1 SUPPORT
245              
246             You can find documentation for this module with the perldoc command.
247              
248             perldoc Class::Simple::Readonly::Cached
249              
250             You can also look for information at:
251              
252             =over 4
253              
254             =item * MetaCPAN
255              
256             L<https://metacpan.org/release/Class-Simple-Readonly-Cached>
257              
258             =item * RT: CPAN's request tracker
259              
260             L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=Class-Simple-Readonly-Cached>
261              
262             =item * CPANTS
263              
264             L<http://cpants.cpanauthors.org/dist/Class-Simple-Readonly-Cached>
265              
266             =item * CPAN Testers' Matrix
267              
268             L<http://matrix.cpantesters.org/?dist=Class-Simple-Readonly-Cached>
269              
270             =item * CPAN Ratings
271              
272             L<http://cpanratings.perl.org/d/Class-Simple-Readonly-Cached>
273              
274             =item * CPAN Testers Dependencies
275              
276             L<http://deps.cpantesters.org/?module=Class::Simple::Readonly::Cached>
277              
278             =item * Search CPAN
279              
280             L<http://search.cpan.org/dist/Class-Simple-Readonly-Cached/>
281              
282             =back
283              
284             =head1 LICENSE AND COPYRIGHT
285              
286             Author Nigel Horne: C<njh@bandsman.co.uk>
287             Copyright (C) 2019-2021 Nigel Horne
288              
289             Usage is subject to licence terms.
290             The licence terms of this software are as follows:
291             Personal single user, single computer use: GPL2
292             All other users (including Commercial, Charity, Educational, Government)
293             must apply in writing for a licence for use from Nigel Horne at the
294             above e-mail.
295             =cut
296              
297             1;