File Coverage

blib/lib/Class/Cache.pm
Criterion Covered Total %
statement 102 116 87.9
branch 20 32 62.5
condition n/a
subroutine 18 19 94.7
pod 6 8 75.0
total 146 175 83.4


line stmt bran cond sub pod time code
1             package Class::Cache;
2 7     7   278310 use strict;
  7         15  
  7         307  
3              
4             BEGIN {
5 7     7   35 use Exporter ();
  7         12  
  7         137  
6 7     7   32 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  7         12  
  7         1604  
7 7     7   44 ($VERSION) = ('$Revision: 1.12 $' =~ m/([\.\d]+)/) ;
8 7         127 @ISA = qw(Exporter);
9 7         17 @EXPORT = qw();
10 7         12 @EXPORT_OK = qw();
11 7         545 %EXPORT_TAGS = ();
12             }
13              
14 7     7   12422 use Class::Prototyped;
  7         91288  
  7         59  
15 7     7   1392 use Data::Dumper;
  7         11880  
  7         588  
16              
17 7     7   10036 use Params::Validate qw(:all);
  7         317916  
  7         1854  
18 7     7   65 use Carp qw(carp croak cluck confess);
  7         15  
  7         9086  
19              
20              
21             =head1 NAME
22              
23             Class::Cache - object factory with revivifying cache
24              
25             =head1 SYNOPSIS
26              
27             use Class::Cache;
28              
29             my $class_cache = Class::Cache->new(
30             # expire cache items when retrieved (on_get). The other option is
31             # to never expire them, by setting this key's value to 0. Timed
32             # expiry is not implemented or entirely expected in the application
33             # domain of this module.
34             expires => 'on_get',
35              
36             # default constructor is new for items constructed by simple_* call
37             new => 'new',
38              
39             # call the constructor eagerly?
40             lazy => 0,
41              
42             # constructor takes no args by default
43             args => [],
44              
45             # IMPORTANT:
46             # There is *_NO_* default package for object construction. If the
47             # key C does not exist in the configuration hash for a cache
48             # item, then it is assumed that the cache item key is the package
49             # name
50              
51             );
52              
53             # All ofthe above constructor parms are the defaults, so the same
54             # Class::Cache could have been created via Class::Cache->new();
55              
56              
57             # Key and package are assumed to have the same name if "pkg" is not
58             # part of the configuration hashref. Therefore, in this case
59             # constructor name is "build". Do not expire this cache entry.
60             $class_cache->set(
61             'html::footer' => { new => 'build', expires => 0 },
62             );
63              
64             # Here, key and package have the same name. Constructor is new and we
65             # supply args for it:
66             $class_cache->set(
67             'Class::Cache::Adder' => { args => [1,2,3] },
68             )
69              
70             # key and package same name, constructor is new, takes no args
71             $class_cache->set(
72             'Super::SimpleClass' => 1,
73             );
74              
75             $class_cache->set(
76             # key is lazy_adder, lazily call as Lazy->Adder->new(1,2,3);
77             lazy_adder => { lazy => 1, pkg => 'Lazy::Adder', args => [1,2,3] }
78             );
79              
80             # Write a constructor as opposed to having this module build it.
81             # Do not forget to use or require the module you need for your
82             # custom factory to work!
83             $class_cache->set(
84             compo => { lazy => 1, new => sub {
85             my $pkg = 'Uber::Super::Cali::Fragi::Listic::Complex::Package';
86             my $x = $pkg->this; $pkg->that; $pkg->give_object;
87             }
88             }
89              
90             );
91              
92              
93              
94             =head1 DESCRIPTION
95              
96             In mod_perl, one wants to pre-load as many things as
97             possible. However, the objects created from the classes that I was
98             loading can only be used once, after which they have to be
99             recreated. So, to save oneself the trouble of keeping track of which
100             class instances have been used and then writing code to re-vivify
101             them, this module handles that.
102              
103              
104             =head1 METHODS
105              
106             =head2 new
107              
108             Usage : ->new(%factory_config)
109             Purpose : creates a new class cache. All object instances will be
110             created per %default_factory_config, unless overridden in
111             the call to set(). The possible configuration options
112             were documented in the SYNOPSIS. The values given to
113             these options are the default values.
114             Returns : returns a Class::Cache object
115             Argument : %factory_config
116             Throws : Exceptions and other anomolies: none
117              
118             =cut
119              
120              
121             sub new {
122 7     7 1 128 my $class = shift;
123              
124 7         91 my %default_factory_config = (
125             expires => { default => 'on_get' },
126             new => { default => 'new' },
127             lazy => { default => 0 },
128             args => { default => [] },
129             );
130              
131 7         18 my %self;
132              
133 7         297 my %factory_config = validate (@_, \%default_factory_config);
134              
135             # the cache of manufactured objects
136 7         88 $self{C} = Class::Prototyped->new;
137              
138             # the attributes for the manufactured objects - Class::Prototyped does
139             # not have general support for this. The attributes are hardcoded, so
140             # I have to keep track of the attributes for the things in the cache
141             # in another data structure.
142 7         1397 $self{A} = Class::Prototyped->new;
143              
144             # we store the key of expired cache items so that we can reconstruct
145             # them later
146 7         634 $self{E} = [];
147              
148 7         22 $self{factory_config} = \%factory_config;
149              
150 7         69 bless \%self, $class;
151             }
152              
153              
154             =head2 set
155              
156             Usage : ->set($key => $factory_config)
157             Purpose : Creates a FactoryCache item accessible by $key whose value will
158             be the object created in fashion specified by $factory_config.
159             $factory_config can be either a hashref or a scalar.
160             If it is a scalar, then it uses the defaults for object
161             creation
162             If it is a hashref, then each of the given parameters in
163             this hashref overwrite the default ones. Of particular
164             important is the new parameter. If this is a scalar, then
165             it is taken as the name of the constructor. If it is a
166             coderef, then the only other factory_config parameter
167             that matters is lazy. If lazy is set, then the
168             constructor execution is delayed until the cache item is
169             requested. Otherwise, the constructor runs immediately.
170             Returns : nothing
171             Throws : Exception if class cannot be created
172              
173             =cut
174              
175             # _merge():
176             # internal routine supporting ->set()
177             sub _merge {
178 3     3   8 my ($current_config, $default_config) = @_;
179              
180 3         7 my %p = %{$default_config};
  3         25  
181              
182 3         10 $p{$_} = $current_config->{$_} for (keys %{$current_config}) ;
  3         21  
183              
184 3         12 \%p;
185             }
186              
187             sub set {
188 11     11 1 519 my $self = shift;
189 11         22 my $key = shift;
190 11         23 my $input_config = shift;
191              
192              
193 11         19 my $parms = do {
194 11 100       40 if (ref $input_config eq 'HASH') {
195 3         37 _merge $input_config, $self->{factory_config};
196             } else {
197 8         36 $self->{factory_config}
198             }
199             };
200              
201 11         46 $self->add_cache_item($key,$parms);
202              
203             }
204              
205             sub is_lazy {
206 19     19 0 42 my ($self, $current_config) = @_;
207              
208 19 100       147 $current_config->{lazy} || $self->{factory_config}->{lazy};
209             }
210              
211             # internal routine which makes it easy to declare a
212             # field as create-on-demand using Class::Prototyped conventions
213 1     1   3 sub _autoload_field { [ $_[0], 'FIELD', 'autoload' ] }
214              
215             # internal routine:
216             # all the logic associated with adding a cache item executes here
217             sub add_cache_item {
218 13     13 0 35 my $self = shift;
219 13         34 my $key = shift;
220 13         33 my $current_config = shift;
221 13         28 my $default_parms = $self->{factory_config};
222              
223 13         20 my ($object, $K, $V);
224              
225             # When something is about to be added to the cache by a custom
226             # coderef, then it only matters whether the object is created now or
227             # on-demand: all other factory configuration parms are ignored.
228 13 100       82 if (ref $current_config->{new} eq 'CODE') {
    50          
229 1 50       6 if ($self->is_lazy($current_config)) {
230 1         3 $K = _autoload_field $key;
231 1         3 $V = $current_config->{new};
232             } else {
233 0         0 $K = $key;
234 0         0 $V = $current_config->{new}->();
235             }
236             # Same logic, but this time, the factory configuration at
237             # Class::Cache instance time requires us to run the same logic. This
238             # happens when a class instance is added to the cache and no "new"
239             # parameter was supplied
240             } elsif (ref $default_parms->{new} eq 'CODE') {
241 0 0       0 if ($self->is_lazy($current_config)) {
242 0         0 $K = _autoload_field $key;
243 0         0 $V = $default_parms->{new};
244             } else {
245 0         0 $K = $key;
246 0         0 $V = $default_parms->{new}->();
247             }
248             # Otherwise build a constructor based on the
249             } else {
250              
251 12         19 my $pkg = do {
252 12 100       58 if (exists $current_config->{pkg}) { $current_config->{pkg} }
  1 50       2  
253 0         0 elsif (exists $default_parms->{pkg}) { $default_parms->{pkg} }
254 11         27 else { $key }
255             };
256 12         80 my $new = do {
257 12 50       42 if (exists $current_config->{new}) { $current_config->{new} }
  12 0       40  
258 0         0 elsif (exists $default_parms->{new}) { $default_parms->{new} }
259 0         0 else { $key }
260             };
261              
262 12         34 my @arg = exists $current_config->{args}
263 0         0 ? @{$current_config->{args}}
264 12 50       99 : @{$default_parms->{args}};
265              
266             my $constructor = sub {
267 8     8   703 eval "require $pkg";
268 8 50       49725 confess $@ if $@;
269 8         62 $pkg->$new(@arg)
270 12         66 } ;
271              
272 12 100       112 if ($self->is_lazy($current_config)) {
273 7         16 $K = [$key, 'FIELD', 'autoload'];
274 7         16 $V = $constructor;
275             } else {
276 5         9 $K = $key;
277 5         12 $V = $constructor->();
278             }
279              
280             }
281              
282             # put the key-value pair in the Class::Cache cache
283 13         6741 $self->{C}->reflect->addSlot($K => $V);
284              
285             # we keep the config parameters around for the manufactured object so that
286             # we can check them later. Specifically, when a user requests an object
287             # from the cache, we check that items's attributes to see if this object
288             # expires on_get, if so, we expire it.
289 13         2344 $self->{A}->reflect->addSlot($key => $current_config);
290             }
291              
292              
293              
294             =head2 get
295              
296             Usage : ->get($cache_item_key)
297             Purpose : returns the cache item with name $cache_item_key. If the
298             cache item was stored with the "lazy" parameter, then
299             the cache item value is constructed now. If the cache
300             item was stored with the "expires" parameter set to
301             "on_get" then we expire this item.
302             Returns : the cache item value or undef
303              
304             =cut
305              
306              
307             sub get {
308              
309 6     6 1 1875 my $self = shift;
310              
311 6         1528 validate_pos(@_, 1);
312              
313 6         32 my $key = shift;
314              
315 6         53 my $retval = $self->{C}->reflect->getSlot($key);
316              
317             # get the user-supplied attributes for this cache item
318 6         315 my $a = $self->{A}->reflect->getSlot($key);
319              
320 6 50       270 if ($a->{expires} eq 'on_get') {
321             # add $key to list of expired keys so it can be re-vivified later
322 6         13 push @{$self->{E}}, $key;
  6         22  
323              
324             # remove $key from the object cache
325 6         28 $self->{C}->reflect->deleteSlot($key);
326             }
327              
328             # interestingly, calling getSlot on a Class::Prototyped object does
329             # not eval the coderef in the value slot. If you get the slot by
330             # calling the key as a method, then it does. So, I have to eval the
331             # coderef in the slot myself.
332 6 100       705 if ($self->is_lazy($a)) {
333 4 50       25 if (ref $retval eq 'CODE') {
334 4         14 $retval = $retval->() ;
335             }
336             }
337 6         52 $retval;
338              
339             }
340              
341              
342             =head2 refill
343              
344             Usage : ->refill
345             Purpose : recreates the objects which were expired from cache
346             Returns : nothing
347             Argument : none
348             Throws : nothing
349              
350             =cut
351              
352              
353             sub refill {
354              
355 2     2 1 1843 my $self = shift;
356              
357 2         11 do {
358 2         15 my $factory_config = $self->{A}->reflect->getSlot($_);
359 2         91 $self->add_cache_item($_ => $factory_config);
360 2         7 } for @{$self->{E}} ;
361              
362 2         456 $self->{E} = [];
363              
364             }
365              
366              
367             =head2 classes
368              
369             Usage : ->classes
370             Purpose : returns a list of the classes in the cache available for
371             retrieval
372             Returns : a list
373             Argument : none
374             Throws : nothing
375              
376             =cut
377              
378             sub classes {
379 7     7 1 658 my $self = shift;
380              
381 7         37 $self->{C}->reflect->slotNames;
382             }
383              
384             =head2 expired
385              
386             Usage : ->expired
387             Purpose : returns a list of the expired classes in the cache
388             Returns : a list
389             Argument : none
390             Throws : nothing
391              
392             =cut
393              
394             sub expired {
395 0     0 1   my $self = shift;
396              
397 0           @{$self->{E}};
  0            
398             }
399              
400             =head1 BUGS
401              
402             None known.
403              
404              
405              
406             =head1 SUPPORT
407              
408             Email the author.
409              
410             =head1 CVS SOURCES
411              
412             cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/seamstress login
413             cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/seamstress co -P classcache
414              
415             Or browse the repository here:
416             L
417              
418             =head1 AUTHOR
419              
420             Terrence Brannon
421             CPAN ID: TBONE
422             metaperl.com
423             metaperl@gmail.com
424             http://www.metaperl.com
425              
426             Original implementation had substantial help from mauke on
427              
428             irc://irc.efnet.org
429              
430             Current version is completely new. I am indebted to Randal Schwartz
431             for generating my interest in Class::Prototyped.
432              
433             =head1 COPYRIGHT
434              
435             This program is free software; you can redistribute
436             it and/or modify it under the same terms as Perl itself.
437              
438             The full text of the license can be found in the
439             LICENSE file included with this module.
440              
441              
442             =head1 SEE ALSO
443              
444             perl(1).
445              
446             =cut
447              
448             #################### main pod documentation end ###################
449              
450              
451             1;
452             # The preceding line will help the module return a true value
453