File Coverage

blib/lib/File/Meta/Cache.pm
Criterion Covered Total %
statement 67 72 93.0
branch 16 26 61.5
condition 9 14 64.2
subroutine 11 11 100.0
pod n/a
total 103 123 83.7


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