File Coverage

blib/lib/Cache/File/Simple.pm
Criterion Covered Total %
statement 43 62 69.3
branch 6 22 27.2
condition 6 16 37.5
subroutine 10 12 83.3
pod 2 2 100.0
total 67 114 58.7


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3             package Cache::File::Simple;
4              
5 3     3   384136 use Exporter 'import';
  3         6  
  3         201  
6             our @EXPORT = qw(cache);
7              
8 3     3   18 use strict;
  3         8  
  3         124  
9 3     3   18 use warnings;
  3         4  
  3         154  
10 3     3   62 use v5.16;
  3         14  
11              
12 3     3   2580 use JSON::PP;
  3         72324  
  3         290  
13 3     3   2687 use Tie::File;
  3         86859  
  3         225  
14 3     3   32 use File::Path;
  3         7  
  3         268  
15 3     3   2096 use Digest::SHA qw(sha256_hex);
  3         11971  
  3         381  
16 3     3   24 use File::Basename;
  3         7  
  3         2730  
17              
18             our $CACHE_ROOT = "/tmp/cacheroot/";
19             our $DEFAULT_EXPIRE = 3600;
20              
21             # https://pause.perl.org/pause/query?ACTION=pause_operating_model#3_5_factors_considering_in_the_indexing_phase
22             our $VERSION = '0.1';
23              
24             ###############################################################################
25             ###############################################################################
26              
27             # Cache get: cache($key);
28             # Cache set: cache($key, $val, $expires = 3600);
29             sub cache {
30 15     15 1 2629911 my ($key, $val, $expire, $ret, @data) = @_;
31              
32 15   50     191 my $hash = sha256_hex($key || "");
33 15         59 my $dir = "$CACHE_ROOT/perl-cache/" . substr($hash, 0, 3);
34 15         35 my $file = "$dir/$hash.json";
35 15         43812 mkpath($dir);
36              
37 15 50       143 tie @data, 'Tie::File', $file or die("Unable to write $file"); # to r/w file
38              
39 15 100 33     3316 if (@_ > 1) { # Set
    50          
40 7   66     56 my $expires = int($expire || 0) || time() + $DEFAULT_EXPIRE;
41 7         65 $data[0] = encode_json({ expires => $expires, data => $val, key => $key });
42 7         5880 $ret = 1;
43             } elsif ($key && -r $file) { # Get
44 8         21 eval { $ret = decode_json($data[0]); };
  8         63  
45 8 100 66     7384 if ($ret->{expires} && $ret->{expires} > time()) {
46 5         22 $ret = $ret->{data};
47             } else {
48 3         228 unlink($file);
49 3         14 $ret = undef;
50             }
51             }
52              
53 15         141 return $ret;
54             }
55              
56             # $num = cache_clean()
57             sub cache_clean {
58 0     0 1   my ($verbose) = @_;
59 0           my $ret = 0;
60              
61             # https://www.perturb.org/display/1306_Perl_Nested_subroutines.html
62             local *dir_is_empty = sub {
63 0 0   0     opendir(my $dh, $_[0]) or return undef;
64 0 0         return scalar(grep { $_ ne "." && $_ ne ".." } readdir($dh)) == 0;
  0            
65 0           };
66              
67 0           foreach my $file (glob("$CACHE_ROOT/*/*.json")) {
68 0 0         tie my @data, 'Tie::File', $file or die("Unable to write $file");
69 0   0       my $x = decode_json($data[0] // {});
70              
71 0 0         if ($x->{expires} < time()) { # File is expired
72 0 0         if ($verbose) { print "$file is expired\n"; }
  0            
73 0           $ret += int(unlink($file));
74             }
75             }
76              
77 0           foreach my $dir (glob("$CACHE_ROOT/*")) { # Directory is empty
78 0 0 0       if (-d $dir && dir_is_empty($dir)) {
79 0 0         if ($verbose) { print "$dir is empty\n"; }
  0            
80 0           $ret += int(rmdir($dir));
81             }
82             }
83              
84 0           return int($ret);
85             }
86              
87             =pod
88              
89             =head1 NAME
90              
91             Cache::File::Simple - Dead simple file based caching meachanism
92              
93             =head1 SYNOPSIS
94              
95             use Cache::File::Simple;
96              
97             my $ckey = "cust:1234";
98              
99             # Get data from the cache
100             my $data = cache($ckey);
101              
102             # Store a scalar
103             cache($ckey, "Jason Doolis");
104             cache($ckey, "Jason Doolis", time() + 7200);
105              
106             # Store an arrayref
107             cache($ckey, [1, 2, 3]);
108              
109             # Store a hashref
110             cache($ckey, {'one' => 1, 'two' => 2});
111              
112             # Delete an item from the cache
113             cache($ckey, undef);
114              
115             =head1 DESCRIPTION
116              
117             C exports a single C function automatically.
118              
119             Store Perl data structures in an on-disk file cache. Cache entries can be given
120             an expiration time to allow for easy clean up.
121              
122             =head1 METHODS
123              
124             =over 4
125              
126             =item B
127              
128             Get cache data for C<$key> from the cache
129              
130             =item B
131              
132             Store data in the cache for C<$key>. C<$obj> can be a scalar, listref, or hashref.
133              
134             =item B
135              
136             Store data in the cache for C<$key> with an expiration time. C<$expires> is a
137             unixtime after which the cache entry will be removed.
138              
139             =item B
140              
141             Delete an entry from the cache.
142              
143             =item B
144              
145             Manually remove expired entries from the cache. Returns the number of items
146             expired from the cache;
147              
148             =item B<$Cache::File::Simple::CACHE_ROOT>
149              
150             Change where the cache files are stored. Default C
151              
152             =item B<$Cache::File::Simple::DEFAULT_EXPIRES>
153              
154             Change the default time entries are cached for. Default 3600 seconds
155              
156             =back
157              
158             =cut
159              
160             1;
161              
162             # vim: tabstop=4 shiftwidth=4 autoindent softtabstop=4