File Coverage

lib/File/DataClass/Cache.pm
Criterion Covered Total %
statement 63 63 100.0
branch 15 16 93.7
condition 2 2 100.0
subroutine 19 19 100.0
pod 7 7 100.0
total 106 107 99.0


line stmt bran cond sub pod time code
1             package File::DataClass::Cache;
2              
3 3     3   68 use 5.01;
  3         8  
4 3     3   11 use namespace::autoclean;
  3         1  
  3         21  
5              
6 3     3   140 use File::DataClass::Constants qw( FALSE NUL SPC TRUE );
  3         4  
  3         183  
7 3     3   12 use File::DataClass::Functions qw( merge_attributes throw );
  3         3  
  3         140  
8 3         32 use File::DataClass::Types qw( Bool Cache ClassName HashRef
9 3     3   999 LoadableClass Object Str );
  3         8  
10 3     3   5534 use Storable qw( freeze );
  3         7480  
  3         183  
11 3     3   18 use Try::Tiny;
  3         5  
  3         135  
12 3     3   11 use Moo;
  3         5  
  3         24  
13              
14             # Public attributes
15             has 'cache' => is => 'lazy', isa => Object, builder => sub {
16 3     3   74 $_[ 0 ]->cache_class->new( %{ $_[ 0 ]->cache_attributes } ) };
  3         8762  
17              
18             has 'cache_attributes' => is => 'ro', isa => HashRef, required => TRUE;
19              
20             has 'cache_class' => is => 'lazy', isa => LoadableClass,
21             default => 'Cache::FastMmap';
22              
23             has 'log' => is => 'ro', isa => Object, required => TRUE;
24              
25             # Private attributes
26             has '_mtimes_key' => is => 'ro', isa => Str, default => '_mtimes';
27              
28             # Construction
29             around 'BUILDARGS' => sub {
30             my ($orig, $class, @args) = @_; my $attr = $orig->( $class, @args );
31              
32             $attr->{cache_attributes} //= {}; my $cache_class;
33              
34             $cache_class = delete $attr->{cache_attributes}->{cache_class}
35             and $attr->{cache_class} = $cache_class;
36              
37             my $builder = delete $attr->{builder} or return $attr;
38              
39             merge_attributes $attr, $builder, [ 'log' ];
40              
41             return $attr;
42             };
43              
44             # Private methods
45             my $_get_key_and_newest = sub {
46             my ($self, $paths) = @_; my $newest = 0; my $is_valid = TRUE; my $key;
47              
48             for my $path (grep { defined && length "${_}" } @{ $paths }) {
49             my $mtime = $self->get_mtime( "${path}" ) or $is_valid = FALSE;
50              
51             ($mtime and $path->exists and $mtime == $path->stat->{mtime})
52             or $is_valid = FALSE;
53             $mtime and $mtime > $newest and $newest = $mtime;
54             $key .= $key ? "~${path}" : "${path}";
55             }
56              
57             return ($key, $is_valid ? $newest : undef);
58             };
59              
60             # Public methods
61             sub get {
62 75     75 1 2159 my ($self, $key) = @_; $key .= NUL;
  75         221  
63              
64 75 100       2236 my $cached = $key ? $self->cache->get( $key ) : FALSE;
65              
66 75 100       56327 $cached and return ($cached->{data}, $cached->{meta});
67              
68 29         725 return (undef, { mtime => undef });
69             }
70              
71             sub get_by_paths {
72 2     2 1 80 my ($self, $paths) = @_;
73 2         8 my ($key, $newest) = $self->$_get_key_and_newest( $paths );
74              
75 2         8 return ($self->get( $key ), $newest);
76             }
77              
78             sub get_mtime {
79 10 100   10 1 1229 my ($self, $k) = @_; $k or return;
  10         24  
80              
81 9 100       113 my $mtimes = $self->cache->get( $self->_mtimes_key ) or return;
82              
83 8         444 return $mtimes->{ $k };
84             }
85              
86             sub remove {
87 24 100   24 1 716 my ($self, $key) = @_; defined $key or return;
  24         67  
88              
89 23         292 $self->cache->remove( $key ); $self->set_mtime( $key, undef );
  23         829  
90              
91 23         2090 return;
92             }
93              
94             sub set {
95 28   100 28 1 1172 my ($self, $key, $data, $meta) = @_; $meta //= { mtime => undef };
  28         74  
96              
97 28         63 my $val = { data => $data, meta => $meta };
98              
99             try {
100 28 100   28   876 $key eq $self->_mtimes_key and throw 'key not allowed';
101 27 50       861 $self->cache->set( $key, $val ) or throw 'set operation returned false';
102 27         1286 $self->set_mtime( $key, $meta->{mtime} );
103             }
104             catch {
105 1     1   1364 my $len = length( $key ) + length( freeze $val );
106              
107 1         36 $self->log->error( "Cache key ${key}(${len}) set failed: ${_}" );
108 28         246 };
109              
110 28         2070 return ($data, $meta);
111             }
112              
113             sub set_by_paths {
114 2     2 1 6 my ($self, $paths, $data, $meta) = @_;
115              
116 2         5 my ($key, $newest) = $self->$_get_key_and_newest( $paths );
117              
118 2         4 $meta->{mtime} = $newest;
119              
120 2         7 return $self->set( $key, $data, $meta );
121             }
122              
123             sub set_mtime {
124 50     50 1 77 my ($self, $k, $v) = @_;
125              
126             return $self->cache->get_and_set( $self->_mtimes_key, sub {
127 50     50   2677 my (undef, $mtimes) = @_;
128              
129 50 100       91 if (defined $v) { $mtimes->{ $k } = $v } else { delete $mtimes->{ $k } }
  25         68  
  25         71  
130              
131 50         910 return $mtimes;
132 50         694 } );
133             }
134              
135             1;
136              
137             __END__