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   47 use 5.01;
  3         9  
4 3     3   15 use namespace::autoclean;
  3         5  
  3         31  
5              
6 3     3   182 use File::DataClass::Constants qw( FALSE NUL SPC TRUE );
  3         6  
  3         189  
7 3     3   17 use File::DataClass::Functions qw( merge_attributes throw );
  3         9  
  3         179  
8 3         39 use File::DataClass::Types qw( Bool Cache ClassName HashRef
9 3     3   1029 LoadableClass Object Str );
  3         10  
10 3     3   7277 use Storable qw( freeze );
  3         7610  
  3         205  
11 3     3   27 use Try::Tiny;
  3         7  
  3         141  
12 3     3   19 use Moo;
  3         6  
  3         27  
13              
14             # Public attributes
15             has 'cache' => is => 'lazy', isa => Object, builder => sub {
16 3     3   72 $_[ 0 ]->cache_class->new( %{ $_[ 0 ]->cache_attributes } ) };
  3         8609  
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 2850 my ($self, $key) = @_; $key .= NUL;
  75         301  
63              
64 75 100       2793 my $cached = $key ? $self->cache->get( $key ) : FALSE;
65              
66 75 100       63712 $cached and return ($cached->{data}, $cached->{meta});
67              
68 29         140 return (undef, { mtime => undef });
69             }
70              
71             sub get_by_paths {
72 2     2 1 94 my ($self, $paths) = @_;
73 2         9 my ($key, $newest) = $self->$_get_key_and_newest( $paths );
74              
75 2         9 return ($self->get( $key ), $newest);
76             }
77              
78             sub get_mtime {
79 10 100   10 1 2326 my ($self, $k) = @_; $k or return;
  10         32  
80              
81 9 100       126 my $mtimes = $self->cache->get( $self->_mtimes_key ) or return;
82              
83 8         664 return $mtimes->{ $k };
84             }
85              
86             sub remove {
87 24 100   24 1 939 my ($self, $key) = @_; defined $key or return;
  24         84  
88              
89 23         334 $self->cache->remove( $key ); $self->set_mtime( $key, undef );
  23         1182  
90              
91 23         2796 return;
92             }
93              
94             sub set {
95 28   100 28 1 1961 my ($self, $key, $data, $meta) = @_; $meta //= { mtime => undef };
  28         94  
96              
97 28         97 my $val = { data => $data, meta => $meta };
98              
99             try {
100 28 100   28   1365 $key eq $self->_mtimes_key and throw 'key not allowed';
101 27 50       957 $self->cache->set( $key, $val ) or throw 'set operation returned false';
102 27         1904 $self->set_mtime( $key, $meta->{mtime} );
103             }
104             catch {
105 1     1   1771 my $len = length( $key ) + length( freeze $val );
106              
107 1         45 $self->log->error( "Cache key ${key}(${len}) set failed: ${_}" );
108 28         214 };
109              
110 28         3297 return ($data, $meta);
111             }
112              
113             sub set_by_paths {
114 2     2 1 6 my ($self, $paths, $data, $meta) = @_;
115              
116 2         9 my ($key, $newest) = $self->$_get_key_and_newest( $paths );
117              
118 2         8 $meta->{mtime} = $newest;
119              
120 2         9 return $self->set( $key, $data, $meta );
121             }
122              
123             sub set_mtime {
124 50     50 1 127 my ($self, $k, $v) = @_;
125              
126             return $self->cache->get_and_set( $self->_mtimes_key, sub {
127 50     50   3768 my (undef, $mtimes) = @_;
128              
129 50 100       132 if (defined $v) { $mtimes->{ $k } = $v } else { delete $mtimes->{ $k } }
  25         91  
  25         91  
130              
131 50         1223 return $mtimes;
132 50         819 } );
133             }
134              
135             1;
136              
137             __END__