File Coverage

blib/lib/File/Meta/Cache.pm
Criterion Covered Total %
statement 89 94 94.6
branch 15 26 57.6
condition 15 22 68.1
subroutine 22 22 100.0
pod 10 10 100.0
total 151 174 86.7


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