File Coverage

blib/lib/Data/Fallback.pm
Criterion Covered Total %
statement 215 295 72.8
branch 66 146 45.2
condition 46 93 49.4
subroutine 26 35 74.2
pod 0 21 0.0
total 353 590 59.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             # use whole path on item packages
3             # if :: swap for slashes, use Data::Fallback for not
4              
5             package CacheHash;
6              
7 1     1   15956 use strict;
  1         3  
  1         58  
8 1     1   7 use vars qw(@ISA @EXPORT_OK $VERSION);
  1         2  
  1         83  
9 1     1   7 use Exporter;
  1         7  
  1         54  
10 1     1   7 use Carp qw(confess);
  1         2  
  1         1796  
11              
12             @ISA = ('Exporter');
13             @EXPORT_OK = qw( cache_hash );
14             $VERSION = "0.16";
15              
16             sub new {
17 1     1   2 my $type = shift;
18 1         2 my $hash_ref = $_[0];
19 1 50       5 my @PASSED_ARGS = (ref $hash_ref eq 'HASH') ? %{$_[0]} : @_;
  1         4  
20 1         2 my $cache_object;
21 1         5 my @DEFAULT_ARGS = (
22             ttl => "1 day",
23             periods_to_keep => 2,
24             );
25 1         4 my %ARGS = (@DEFAULT_ARGS, @PASSED_ARGS);
26 1         4 $cache_object = bless \%ARGS, $type;
27              
28 1         6 return $cache_object;
29             }
30              
31             sub expired_check {
32 0     0   0 my $self = shift;
33 0         0 my $_sub_hash = shift;
34 0         0 my $diff = $self->{int_time} - $_sub_hash;
35 0 0       0 if($diff >= $self->{periods_to_keep}) {
36 0         0 return 1;
37             } else {
38 0         0 return 0;
39             }
40             }
41              
42             sub cleanup {
43 0     0   0 my $self = shift;
44 0         0 my ($hash, $key) = @_;
45 0         0 delete $hash->{$key};
46             }
47              
48             sub handle_ttl {
49 9     9   11 my $self = shift;
50              
51 9 100       50 if($self->{ttl} =~ /^\d+$/) {
    50          
52             # do nothing
53             } elsif($self->{ttl} =~ s/^(\d+)\s*(\D+)$/$1/) {
54 1 50       4 $self->{ttl} = $1 if defined $1;
55 1 50       3 my $units = (defined $2) ? $2 : '';
56 1 50 33     5 if(($units =~ /^s/i) || (!$units)) {
    0          
    0          
    0          
    0          
57 1         2 $self->{ttl} = $self->{ttl};
58             } elsif ($units =~ /^m/i) {
59 0         0 $self->{ttl} *= 60;
60             } elsif ($units =~ /^h/i) {
61 0         0 $self->{ttl} *= 3600;
62             } elsif ($units =~ /^d/i) {
63 0         0 $self->{ttl} *= 86400;
64             } elsif ($units =~ /^w/i) {
65 0         0 $self->{ttl} *= 604800;
66             } else {
67 0         0 die "invalid ttl '$self->{ttl}', bad units '$units'";
68             }
69             } else {
70 0         0 die "invalid ttl '$self->{ttl}', not just number and couldn't find units";
71             }
72             }
73              
74             sub cache_hash {
75 9     9   12 my $self = $_[0];
76              
77 9 50 33     68 unless($self->{base_hash} && ref $self->{base_hash} && ref $self->{base_hash} eq 'HASH') {
      33        
78 0         0 confess "need a hash ref for base_hash";
79             }
80              
81 9         21 $self->handle_ttl;
82              
83 9 100       26 unless(exists $self->{base_hash}{$self->{ttl}}) {
84 1         2 $self->{base_hash}{$self->{ttl}} = {};
85             }
86              
87            
88 9         27 $self->{int_time} = (int(time/$self->{ttl}));
89              
90 9 100       25 if(exists $self->{base_hash}{$self->{ttl}}{$self->{int_time}}) {
91              
92             } else {
93 1         1 foreach my $key (keys %{$self->{base_hash}{$self->{ttl}}}) {
  1         4  
94              
95 0 0       0 if($self->expired_check($key)) {
96 0         0 $self->cleanup($self->{base_hash}{$self->{ttl}}, $key);
97             }
98             }
99 1         4 $self->{base_hash}{$self->{ttl}}{$self->{int_time}} = {};
100             }
101 9         22 return $self->{base_hash}{$self->{ttl}}{$self->{int_time}};
102             }
103              
104             package Data::Fallback;
105              
106 1     1   7 use strict;
  1         2  
  1         42  
107 1     1   5 use vars qw($VERSION);
  1         2  
  1         38  
108 1     1   11 use Exporter;
  1         2  
  1         41  
109              
110             $VERSION = "0.01";
111              
112 1     1   4 use Carp qw(confess);
  1         2  
  1         4768  
113              
114             sub new {
115 1     1 0 436 my $type = shift;
116 1 50       6 my @PASSED_ARGS = (ref $_[0] eq 'HASH') ? %{$_[0]} : @_;
  1         7  
117 1         10 my @DEFAULT_ARGS = (
118             cache => {},
119              
120             # cache_level looks like
121             # session.group
122             # session.item
123             # all.group
124             # all.item
125             cache_level => 'session',
126             cache_order => ['session', 'all', 0],
127             cache_type => ['item', 'group'],
128              
129             list => [],
130             list_name => '',
131              
132             use_zeroth_hash => 1,
133             );
134 1         8 my %ARGS = (@DEFAULT_ARGS, @PASSED_ARGS);
135 1         3 my $self = bless \%ARGS, $type;
136 1 50       16 if($self->{use_zeroth_hash}) {
137 1         6 $self->set_zeroth_hash($self->{zeroth_hash})
138             } else {
139 0         0 delete $self->{use_zeroth_hash};
140             }
141            
142 1         7 return $self;
143             }
144              
145             sub set_zeroth_hash {
146 1     1 0 2 my $self = shift;
147 1         2 my $passed_zeroth_hash = shift;
148 1   50     3 $passed_zeroth_hash ||= {};
149 1         6 my %zeroth_hash = ( (
150             zeroth_hash => 1,
151             accept_update => 'item',
152             package => 'Memory',
153 1         2 ), %{$passed_zeroth_hash}
154             );
155 1         3 $self->{zeroth_hash} = \%zeroth_hash;
156 1 50       7 if($self->{zeroth_hash}{ttl}) {
157 1         10 $self->{zeroth_hash}{cache_hash} = CacheHash->new({
158             ttl => $self->{zeroth_hash}{ttl},
159             });
160 1         6 delete $self->{zeroth_hash}{ttl};
161             }
162             }
163              
164             sub get {
165 5     5 0 3788 my $self = shift;
166 5         10 my $chunk = shift;
167              
168 5         7 my ($primary_key, $items);
169 5 50       14 if($chunk =~ m@^/(.+?)/(.+)$@) {
170 0         0 ($primary_key, $items) = ($1, $2);
171             } else {
172 5         22 $items = $chunk;
173             }
174 5 50 33     30 die "need a \$self->{list_name}" unless( (defined $self->{list_name}) && length $self->{list_name});
175 5 50       11 die "\$self->{list} is required" unless $self->{list};
176 5 50 33     30 die "\$self->{list} needs to be an array ref" unless(ref $self->{list} && ref $self->{list} eq 'ARRAY');
177 5 50 33     29 die "\$self->{list} needs to be an array ref of hash refs" unless(ref $self->{list}[0] && ref $self->{list}[0] eq 'HASH');
178 5 50 33     22 die "usage: \$self->get('item1,item2') ($chunk)" unless( (defined $items) && length $items);
179              
180 5 100 66     36 if($self->{use_zeroth_hash} && !$self->{list}[0]{zeroth_hash}) {
181 1         3 unshift @{$self->{list}}, $self->{zeroth_hash};
  1         4  
182             }
183              
184 5         12 $self->{update} = {};
185 5         29 my $return = [];
186 5         9 $self->{history} = [];
187 5         24 foreach my $item (split /\s*,\s*/, $items) {
188              
189 5         9 $self->{item} = $item;
190              
191 5         7 $self->{got_item_from} = "";
192 5         11 $self->{from_cache} = "";
193              
194 5         9 for($self->{i}=0;$self->{i}<@{$self->{list}};$self->{i}++) {
  12         31  
195 12         24 $self->{hash} = $self->{list}[$self->{i}];
196 12         21 $self->{hash}{item} = $item;
197              
198 12   50     111 $self->{this_primary_key} = $primary_key || $self->{hash}{primary_key} || $self->{primary_key} || '';
199              
200              
201 12         15 my $clean_hash_content = 0;
202 12         17 my $orig_hash_content = '';
203 12 50 66     64 if($self->{hash}{content} && $self->{hash}{content} =~ /\$primary_key\b/) {
204            
205 0         0 $orig_hash_content = $self->{hash}{content};
206 0         0 $self->{hash}{content} =~ s/\$primary_key\b/$self->{this_primary_key}/;
207              
208 0         0 $clean_hash_content = 1;
209              
210             }
211              
212 12 50 66     45 die "need a content" if(!$self->{hash}{content} && !$self->{hash}{zeroth_hash});
213              
214 12   66     32 $self->{hash}{package} ||= $self->{package};
215 12 50       25 die "need a package" if(!$self->{hash}{package});
216              
217 12         35 $self->morph($self->{hash}{package});
218 12   100     48 $self->{hash}{content} ||= '';
219 12 100       35 if($self->_GET) {
220 5         10 $self->{got_item_from} = $self->{hash}{package};
221 5 100       16 $self->{got_item_from} .= " ($self->{from_cache})" if($self->{from_cache});
222 5         4 push @{$return}, $self->{update}{item};
  5         14  
223 5         5 push @{$self->{history}}, {
  5         31  
224             content => $self->{hash}{content},
225             got_item_from => $self->{got_item_from},
226             item => $self->{item},
227             value => $self->{update}{item},
228             };
229 5 50       13 if($clean_hash_content) {
230 0         0 $self->{hash}{content} = $orig_hash_content;
231 0         0 $self->{true_content} = $orig_hash_content;
232             }
233 5         15 $self->list_update($self->{i});
234 5         9 last;
235             } else {
236 7 50       24 if($clean_hash_content) {
237 0         0 $self->{hash}{content} = $orig_hash_content;
238 0         0 $self->{true_content} = $orig_hash_content;
239             }
240             }
241             }
242 5 50       11 unless($self->{got_item_from}) {
243             # I didn't find a value, so add on undef
244 0         0 push @{$return}, '';
  0         0  
245 0         0 push @{$self->{history}}, {
  0         0  
246             content => $self->{true_content},
247             got_item_from => 'nowhere',
248             item => $self->{item},
249             value => 'NULL',
250             };
251             }
252 5         9 foreach(qw(i item update)) {
253 15         36 delete $self->{$_};
254             }
255             }
256 5 50       9 if(scalar @{$return} == 0) {
  5 50       11  
  5         9  
257             # gonna return undef
258             } elsif( scalar @{$return} == 1) {
259 5         9 $return = $return->[0];
260             } else {
261 0 0       0 $return = wantarray ? @{$return} : $return;
  0         0  
262             }
263 5         16 return $return;
264             }
265              
266             sub get_accept_update {
267 7     7 0 9 my $self = shift;
268 7         7 my $accept_update = 0;
269 7 50 33     32 if( (defined $self->{hash}{accept_update}) && length $self->{hash}{accept_update}) {
    0 0        
270 7         13 $accept_update = $self->{hash}{accept_update};
271             } elsif( (defined $self->{accept_update}) && length $self->{accept_update}) {
272 0         0 $accept_update = $self->{accept_update};
273             }
274 7         12 return $accept_update;
275             }
276              
277             sub list_update {
278 5     5 0 6 my $self = shift;
279 5         6 my $i = shift;
280 5         14 for(my $j=($i - 1);$j>=0;$j--) {
281 7         13 $self->{hash} = $self->{list}[$j];
282 7         17 my $accept_update = $self->get_accept_update;
283 7 50       25 next unless($accept_update);
284              
285 7         8 my $delete_hash_primary_key;
286 7 50 33     41 if(!exists $self->{hash}{primary_key} && exists $self->{set_primary_key}) {
287 0         0 $self->{hash}{primary_key} = $self->{set_primary_key};
288             }
289              
290 7 50       14 if(!ref $accept_update) {
291 7         17 $self->_list_update_helper($accept_update);
292             } else {
293 0 0       0 if(ref $accept_update eq 'ARRAY') {
294 0         0 for(my $i=0;$i<@{$accept_update};$i++) {
  0         0  
295 0         0 my $this_update = $accept_update->[$i];
296 0 0       0 if(ref $this_update) {
297 0 0       0 if(ref $this_update eq 'Regexp') {
    0          
298 0 0 0     0 if($self->{got_item_from} && $self->{got_item_from} =~ /$this_update/) {
299 0 0       0 if($accept_update->[($i + 1)]) {
300 0         0 $self->_list_update_helper($accept_update->[($i + 1)]);
301             }
302             }
303             } elsif(ref $this_update eq 'CODE') {
304 0         0 my $return = &{$this_update}($self);
  0         0  
305 0         0 $self->_list_update_helper($return);
306             }
307             }
308             }
309             }
310             }
311 7 50       31 delete $self->{hash}{primary_key} if($delete_hash_primary_key);
312             }
313             }
314              
315             sub _list_update_helper {
316 7     7   43 my $self = shift;
317 7         9 my $update_type = shift;
318 7 100       19 if($update_type eq 'item') {
    50          
    0          
319 5         14 $self->update_item;
320             } elsif($update_type eq 'group') {
321 2         12 $self->update_group;
322             } elsif($update_type eq 'all') {
323 0         0 $self->update_item;
324 0         0 $self->update_group;
325             } else {
326 0         0 confess "unknown \$update_type: $update_type";
327             }
328             }
329              
330             sub update_item {
331 5     5 0 6 my $self = shift;
332 5 50       14 if(exists $self->{update}{item}) {
333 5         14 $self->morph($self->{hash}{package});
334 5         14 $self->SET_ITEM;
335             }
336             }
337              
338             sub update_group {
339 2     2 0 3 my $self = shift;
340 2 50 33     15 if( (defined $self->{update}{item}) && length $self->{update}{item}) {
341 2         10 $self->morph($self->{hash}{package});
342 2         5 $self->SET_GROUP;
343             }
344             }
345              
346             sub set_list_name {
347 0     0 0 0 my $self = shift;
348 0   0     0 my $list_name = shift || die "need a list_name";
349 0         0 $self->{list_name} = $list_name;
350             }
351              
352             sub set_list {
353 0     0 0 0 my $self = shift;
354 0         0 my $list = shift;
355 0 0       0 die "need a list" unless($list);
356 0 0 0     0 die "list needs to be an ARRAY ref" unless(ref $list && ref $list eq 'ARRAY');
357 0         0 $self->{list} = $list;
358             }
359              
360             sub INITIALIZE_PACKAGE {
361 19     19 0 26 return;
362             }
363              
364             ### this turns the object into the correct type
365             ### thanks to Paul Seamons for the start of this code
366             sub morph {
367 19     19 0 20 my $self = shift;
368 19         25 my $package = shift;
369              
370 19         21 my $tmp_package = $package;
371 19         33 $tmp_package =~ s@::@/@g;
372             ### polymorph
373              
374 19         23 my $at = '';
375             # this little trick should allow users to write their own overriding
376             # fallback methods, thanks to Rob Brown for the idea
377 19 50       39 if($tmp_package =~ m@/@) {
378 0         0 eval {
379 0         0 require "$tmp_package.pm";
380             };
381 0 0       0 if($@) {
382 0         0 $at .= $@;
383             } else {
384 0         0 bless $self, $package;
385             }
386             } else {
387              
388             # likely a Data::Fallback package
389 19         21 eval {
390 19         1461 require "Data/Fallback/$tmp_package.pm";
391             };
392 19 50       37 if( $@ ){
393 0         0 $at .= $@;
394             # this is just for sort of top level stuff like CGI
395 0         0 eval {
396 0         0 require "$tmp_package.pm";
397             };
398              
399 0 0       0 if($@) {
400 0         0 $at .= $@;
401             } else {
402 0         0 $at = '';
403 0         0 bless $self, $package;
404             }
405              
406             } else {
407 19         51 bless $self, "Data::Fallback::$package";
408             }
409             }
410 19 50       35 die "bad stuff on require of $tmp_package: $at" if($at);
411 19         44 $self->INITIALIZE_PACKAGE($package);
412 19         29 return $self;
413             }
414              
415             sub SET_SESSION_ITEM {
416 0     0 0 0 die "need to write a SET_SESSION_ITEM method";
417             }
418              
419             sub SET_SESSION_CONTENT {
420 0     0 0 0 die "need to write a SET_SESSION_CONTENT method";
421             }
422              
423             sub SET_ITEM {
424 0     0 0 0 die "need to write a SET_ITEM method";
425             }
426              
427             sub SET_GROUP {
428 0     0 0 0 die "need to write a SET_GROUP method";
429             }
430              
431             sub delete_list {
432 0     0 0 0 my $self = shift;
433 0         0 delete $self->{list};
434             }
435              
436             sub get_cache_level {
437 9     9 0 11 my $self = shift;
438 9         13 my $return = $self->{cache_level};
439             # just going to look in two places, the hash, then the object
440 9 100 66     51 if( (defined $self->{hash}{cache_level}) && length $self->{hash}{cache_level}) {
    50 33        
441 4         7 $return = $self->{hash}{cache_level};
442             } elsif( (defined $self->{cache_level}) && length $self->{cache_level}) {
443 5         8 $return = $self->{cache_level};
444             }
445 9 50       12 unless(grep {$return eq $_} @{$self->{cache_order}}) {
  27         52  
  9         15  
446 0         0 confess "Unknown cache_level: $return. Known cache_order: " . join(", ", @{$self->{cache_order}});
  0         0  
447             }
448 9         17 return $return;
449             }
450              
451             sub check_cache {
452 16     16 0 24 my $self = shift;
453 16         27 my ($package, $type, $key) = @_;
454 16         23 my ($found_in_cache, $content) = (0, 0, 0);
455 16         17 foreach my $cache_level (@{$self->{cache_order}}) {
  16         37  
456 43 100       75 last unless($cache_level);
457             next unless(
458 32 50 66     287 $self->{cache} &&
      100        
      66        
      66        
459             $self->{cache}{$package} &&
460             $self->{cache}{$package}{$cache_level} &&
461             $self->{cache}{$package}{$cache_level}{$self->{list_name}} &&
462             $self->{cache}{$package}{$cache_level}{$self->{list_name}}{$type});
463              
464 13         15 my $ref;
465 13 100       30 if($self->cache_hashed) {
466 4         15 $self->{hash}{cache_hash}{base_hash} = $self->{cache}{$package}{$cache_level}{$self->{list_name}}{$type};
467 4         9 $ref = $self->{hash}{cache_hash}->cache_hash;
468             } else {
469 9         25 $ref = $self->{cache}{$package}{$cache_level}{$self->{list_name}};
470             }
471 13 100       39 if(defined $ref->{$type}{$key}) {
472 5         5 $found_in_cache = 1;
473 5         10 $content = $ref->{$type}{$key};
474 5         10 $self->{from_cache} = "cache - $type";
475 5 50       11 $self->{from_cache} .= " ttl ($self->{hash}{cache_hash}{int_time})" if($self->cache_hashed);
476 5         8 last;
477             }
478             }
479 16         49 return ($found_in_cache, $content);
480             }
481              
482             sub set_cache {
483 9     9 0 12 my $self = shift;
484 9         16 my ($package, $type, $key, $value) = @_;
485 9 50       10 unless(grep {$type eq $_} @{$self->{cache_type}}) {
  18         48  
  9         20  
486 0         0 confess "Unknown cache_type: $type. Known cache_types: " . join(", ", @{$self->{cache_type}});
  0         0  
487             }
488 9 50       18 confess "need a cache \$key" unless($key);
489 9 50 33     35 confess "need a cache \$value" unless( (defined $value) && length $value);
490 9         22 my $cache_level = $self->get_cache_level;
491 9 50       15 return unless($cache_level);
492 9   100     26 $self->{cache}{$package} ||= {};
493 9   100     24 $self->{cache}{$package}{$cache_level} ||= {};
494 9   100     28 $self->{cache}{$package}{$cache_level}{$self->{list_name}} ||= {};
495 9   100     28 $self->{cache}{$package}{$cache_level}{$self->{list_name}}{$type} ||= {};
496 9         9 my $ref;
497 9 100       22 if($self->cache_hashed) {
498 5         30 $self->{hash}{cache_hash}{base_hash} = $self->{cache}{$package}{$cache_level}{$self->{list_name}}{$type};
499 5         18 $ref = $self->{hash}{cache_hash}->cache_hash;
500             } else {
501 4         9 $ref = $self->{cache}{$package}{$cache_level}{$self->{list_name}}{$type};
502             }
503 9         31 $ref->{$key} = $value;
504             }
505              
506             sub cache_hashed {
507 27     27 0 32 my $self = shift;
508 27   66     128 return $self->{hash}{cache_hash} && $self->{hash}{cache_hash}{ttl};
509             }
510              
511             sub get_cache_key {
512 25     25 0 29 my $self = shift;
513 25         27 my $key = shift;
514 25   50     832 return $self->{"this_$key"} || $self->{hash}{$key} || $self->{$key} || '';
515             }
516              
517              
518             =head1 NAME
519              
520             Data::Fallback - fallback through an array of levels till you find your data, cacheing where desired
521              
522             =head1 DESCRIPTION
523              
524             The simplest, good example for Data::Fallback, is cacheing a database to a conf file, then to memory. In general, the user
525             supplies an array ref of hash refs (an object property named list), where each hash ref explains how to get data for that step. Each
526             hash ref needs a package, which currently can be Memory, ConfFile, DBI, or WholeFile. Update acceptance can be set for each level.
527              
528             Data::Fallback then goes through the array, checking for data, stopping when it finds said data, updates up the array,
529             as requested, and returns the data.
530              
531             A group can be thought of as a row and an item a column.
532              
533             =head1 INFORMAL EXAMPLE
534              
535             Start with a table foo.
536              
537             column data
538              
539             ------ ----
540             id 1
541             name Chopper
542              
543             and a file foo.cache. I offer two sets of hits, in a mod_perl of daemon environment, both trying to
544              
545             SELECT id FROM foo WHERE name = 'Chopper'
546              
547             Set 1
548             Hit 1a
549             Check memory -> data not there
550             Check foo.cache -> data not there
551             Check db -> data is there
552             Update foo.cache
553             Update memory
554             Return id = 1
555              
556             Hit 1b
557             Check memory -> data is there
558             Return id = 1
559              
560             Set 2, after a restart
561             Hit 2a
562             Check memory -> data not there
563             Check foo.cache -> data is there
564             Update memory
565             Return id = 1
566              
567             Hit 2b
568             Check memory -> data is there
569             Return id = 1
570              
571             So, even after the restart, the database only gets hit once.
572              
573             =head1 EXAMPLE
574              
575             #!/usr/bin/perl -w
576              
577             use strict;
578             use Data::Fallback;
579             use Carp qw(confess);
580              
581             # I use dumper just to show some complex structures
582             use Data::Dumper;
583              
584             # here I write out a couple files which I late clean up
585             # the idea is that the over file, overrides the default file
586              
587             my $over_file = "/tmp/data_fallback_over";
588             my $default_file = "/tmp/data_fallback_default";
589              
590             open (FILE, ">$over_file") || confess "couldn't open $over_file: $!";
591             print FILE "key2 over2";
592             close(FILE);
593              
594             open (FILE, ">$default_file") || confess "couldn't open $default_file: $!";
595             print FILE "key1 default1\nkey2 default2";
596             close(FILE);
597              
598             my $self = Data::Fallback->new({
599              
600             # list is an array ref of hash refs to fall through looking for data
601              
602             list => [
603             {
604             # accept_update says to update the conf
605             accept_update => 'group',
606              
607             # this means to cache everything
608             cache_level => 'all',
609              
610             # where to get the content
611             content => $over_file,
612             },
613             {
614             cache_level => 'all',
615             content => $default_file,
616             },
617             ],
618              
619             # need to name list
620             list_name => 'test',
621              
622             # object global for package
623             package => 'ConfFile',
624              
625             zeroth_hash => {
626             ttl => '5 seconds',
627             },
628             });
629             print $self->get('key2') . "\n";
630             print Dumper $self->{history};
631             print $self->get('key2') . "\n";
632             print Dumper $self->{history};
633             print $self->get('key1') . "\n";
634             print Dumper $self->{history};
635             print $self->get('key1') . "\n";
636             print Dumper $self->{history};
637             unlink $over_file, $default_file;
638              
639             =head1 PACKAGES
640              
641             You are able to write your own packages that aren't a part of Data::Fallback. Such packages would look something like this
642              
643             #!/usr/bin/perl -w
644              
645             package Mine;
646              
647             use strict;
648             use Data::Fallback;
649             use vars qw(@ISA);
650              
651             @ISA = qw(Data::Fallback);
652              
653             1;
654              
655             and methods for at least each of the following _GET, SET_ITEM, SET_GROUP, SET_SESSION_ITEM, SET_SESSION_CONTENT. This
656             functionality allows you to build your content however you like, from wherever you like. For example, let's supposing you have
657             your own objects that build entire pages. You could simply wrap around said objects with the above methods. Put a nice
658             WholeFile cache that accepts updates in front of your personal object. On the first hit, your content gets generated, in some
659             potentially very expensive way. On the second hit you cache from either the Memory package, or the WholeFile level you inserted.
660             Currently, there are cacheing issues, but I hope yo clear them up in time.
661              
662             =head1 APOLOGIES
663              
664             This perldoc isn't the best, but I plan on continued development for sometime. In other words, a better perldoc is to come.
665             And a better test suite. If you feel so inclined to use Data::Fallback::Daemon, do so realizing that the protocol is sure to change.
666             The TO_DO shows where the poject is headed.
667              
668             =head1 THANKS
669              
670             Thanks to Rob Brown, Paul Seamons, Allen Bettilyon and Dan Hanks for listening to my babblings and offering feedback. Thanks to Rob
671             Brown for testing my first version. Also, thanks to Paul for Net::Server and helping me set up Data::Fallback::Daemon. Lincoln Stein's
672             AUTHOR INFORMATION was borrowed from heavily.
673              
674             =head1 AUTHOR
675              
676             Copyright 2001-2002, Earl J. Cahill. All rights reserved.
677              
678             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
679              
680             Address bug reports and comments to: cpan@spack.net.
681              
682             When sending bug reports, please provide the version of Data::Fallback, the version of Perl, and the name and version of the operating
683             system you are using.
684              
685             =cut
686              
687             1;