File Coverage

blib/lib/File/Meta/Cache.pm
Criterion Covered Total %
statement 83 95 87.3
branch 15 26 57.6
condition 15 22 68.1
subroutine 21 23 91.3
pod 10 10 100.0
total 144 176 81.8


line stmt bran cond sub pod time code
1 1     1   127104 use strict;
  1         3  
  1         28  
2 1     1   5 use warnings;
  1         2  
  1         49  
3             package File::Meta::Cache;
4             our $VERSION="v0.1.0";
5             # Default Opening Mode
6 1     1   5 use Fcntl qw(O_RDONLY);
  1         2  
  1         56  
7 1     1   524 use enum qw;
  1         1175  
  1         9  
8              
9 1     1   1183 use Object::Pad;
  1         11323  
  1         4  
10              
11             class File::Meta::Cache;
12 1     1   404 use feature qw;
  1         2  
  1         52  
13              
14 1     1   1839 use Log::ger; # Logger
  1         53  
  1         9  
15 1     1   731 use Log::OK; # Logger enabler
  1         3862  
  1         5  
16              
17 1     1   344 use POSIX();
  1         3  
  1         2566  
18              
19              
20              
21             field $_sweep_size;
22              
23             field $_no_fh :param =undef;
24             field $_enabled;
25             field $_sweeper;
26             field %_cache;
27             field $_opener;
28             field $_closer;
29             field $_updater;
30             field $_http_headers;
31              
32             BUILD{
33             $_sweep_size//=100;
34             $_enabled=1;
35             }
36              
37 1     1 1 4 method sweeper {
38             $_sweeper//= sub {
39 1     1   4 my $i=0;
40 1         2 my $entry;
41 1         2 my $closer=$self->closer;
42 1         4 for(keys %_cache){
43 1         3 $entry=$_cache{$_};
44              
45             # If the cached_ field reaches 1, this is the last code to use it. so close it
46             #
47 1 50       6 $closer->($entry) if($entry->[valid_]==1);
48 1 50       11 last if ++$i >= $_sweep_size;
49             }
50             }
51 1   50     24 }
52              
53             # returns a sub to execute. Object::Pad method lookup is slow. so bypass it
54             # when we don't need it
55             #
56 5     5 1 22 method opener{
57             $_opener//=
58             sub {
59 5     5   14 my ( $key_path, $mode, $force)=@_;
60 5         9 my $in_fd;
61              
62             # Entry is identified by the path, however, the actual data can come from another file
63             #
64 5         8 my $existing_entry=$_cache{$key_path};
65 5   100     21 $mode//=O_RDONLY;
66 5 100 100     25 if(!$existing_entry or $force){
67 4         6 Log::OK::TRACE and log_trace __PACKAGE__.": Searching for: $key_path";
68              
69 4         70 my @stat=stat $key_path;
70            
71             # If the stat fail or is not a file return undef.
72             # If this is a reopen(force), the force close the file to invalidate the cache
73             #
74 4 50 33     28 unless(@stat and -f _){
75 0 0       0 $_closer->($existing_entry, 1) if $existing_entry;
76 0         0 return undef;
77             };
78              
79 4         9 my @entry;
80 4         79 $in_fd=POSIX::open($key_path, $mode);
81              
82              
83              
84 4 50       14 if(defined $in_fd){
85            
86 4 100       10 if($existing_entry){
87             # Duplicate and Close unused fd
88 2         23 POSIX::dup2 $in_fd, $existing_entry->[fd_];
89 2         15 POSIX::close $in_fd;
90              
91             # Copy stat into existing array
92 2         12 $existing_entry->[stat_]->@*=@stat;
93             }
94             else {
95             # Only create a file handle if its enabled
96 2 50       74 open($entry[fh_], "+<&=$in_fd") unless($_no_fh);
97              
98 2         7 $entry[stat_]=\@stat;
99 2         5 $entry[key_]=$key_path;
100 2         8 $entry[fd_]=$in_fd;
101 2         5 $entry[valid_]=1;#$count;
102              
103 2         3 $existing_entry =\@entry;
104 2 50       18 $_cache{$key_path}=$existing_entry if($_enabled);
105             }
106             }
107             else {
108 0         0 Log::OK::ERROR and log_error __PACKAGE__." Error opening file $key_path: $!";
109             }
110             }
111              
112             # Increment the counter
113             #
114 5 50       18 $existing_entry->[valid_]++ if $existing_entry;
115 5         25 $existing_entry;
116             }
117 5   100     48 }
118              
119              
120             # Mark the cache as disabled. Dumps all values and closes
121             # all fds
122             #
123 0     0 1 0 method disable{
124 0         0 $_enabled=undef;
125 0         0 for(values %_cache){
126 0         0 POSIX::close($_cache{$_}[0]);
127             }
128 0         0 %_cache=();
129             }
130              
131             # Generates a sub to close a cached fd
132             # removes meta data from the cache also
133             #
134 3     3 1 14 method closer {
135             $_closer//=sub {
136 3     3   6 my $entry=$_[0];
137 3 100 66     14 if(--$entry->[valid_] <=0 or $_[1]){
138 1         3 my $actual=delete $_cache{$entry->[key_]};
139 1 50       4 if($actual){
140             # Attempt to close only if the entry exists
141 1         2 $actual->[valid_]=0; #Mark as invalid
142 1         34 POSIX::close($actual->[fd_]);
143 1         34 $actual->[fh_]=undef;
144             }
145             else {
146 0         0 die "Entry does not exist";
147             }
148             }
149             }
150 3   100     27 }
151              
152 1     1 1 10 method updater{
153             $_updater//=sub {
154             # To a stat on the entry
155 1     1   20 $_[0][stat_]->@*=stat $_[0][key_];
156 1 50 33     10 unless($_[0][stat_]->@* and -f _){
157             # This is an error force close the file
158 0         0 $_closer->($_[0], 1 );
159             }
160             }
161 1   50     23 }
162              
163             # OO Interface
164             #
165              
166 5     5 1 2514 method open {
167 5         19 $self->opener->&*;
168             }
169              
170 2     2 1 610 method close {
171 2         8 $self->closer->&*;
172             }
173 1     1 1 705 method update{
174 1         7 $self->updater->&*;
175             }
176              
177 1     1 1 333 method sweep {
178 1         5 $self->sweeper->&*;
179             }
180              
181 0     0 1   method enable{ $_enabled=1; }
  0            
182              
183             1;
184