File Coverage

blib/lib/File/Meta/Cache.pm
Criterion Covered Total %
statement 71 75 94.6
branch 15 24 62.5
condition 9 14 64.2
subroutine 13 13 100.0
pod n/a
total 108 126 85.7


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