File Coverage

blib/lib/Hub/Data/FileCache.pm
Criterion Covered Total %
statement 48 83 57.8
branch 16 58 27.5
condition 0 12 0.0
subroutine 5 7 71.4
pod 5 5 100.0
total 74 165 44.8


line stmt bran cond sub pod time code
1             package Hub::Data::FileCache;
2 1     1   6 use strict;
  1         2  
  1         48  
3 1     1   7 use Hub qw/:lib/;
  1         3  
  1         6  
4             our $VERSION = '4.00043';
5             our @EXPORT = qw//;
6             our @EXPORT_OK = qw/fattach fhandler finstance frefresh fread/;
7             our $NAMESPACE = Hub::regns('filecache');
8             our $COUNT = 0;
9              
10             # ------------------------------------------------------------------------------
11             # fattach - Attach an instance of a class to a file.
12             # fattach $filename, $class
13             #
14             # C<$class> must implement the method C
15             #
16             # Returns a hash of:
17             #
18             # lastread # mod time last time we read it
19             # filename # name
20             # lines # ARRAY of lines in the file
21             # handlers # HASH of attached classes
22             #
23             # The instance is a singleton.
24             # ------------------------------------------------------------------------------
25              
26             sub fattach {
27 16 50   16 1 55 my $param_filename = shift or croak "Provide a filename";
28 16         17 my $handler = shift;
29 16 50       65 croak "Provide a reloadable object" unless can($handler, 'reload');
30 16         47 my $filename = Hub::abspath($param_filename);
31 16         38 my $instance = $$NAMESPACE{$filename};
32 16 50       37 if( defined $instance ) {
33 0 0       0 if( $instance->{'handlers'}{$handler} ) {
34 0         0 croak "Already attached";
35             } else {
36 0         0 $instance->{'handlers'}{$handler} = $handler;
37 0 0       0 if ($$instance{'lastread'}) {
38 0         0 $handler->reload( $instance );
39             } else {
40 0         0 Hub::fread($instance);
41             }
42             }#if
43             } else {
44 16         94 $instance = {
45             'filename' => $filename,
46             'lastread' => 0,
47             'handlers' => { $handler => $handler, },
48             };
49 16         61 $$NAMESPACE{$filename} = $instance;
50 16         44 Hub::fread($instance);
51             }#unless
52 16         54 return $instance;
53             }#fattach
54              
55             # ------------------------------------------------------------------------------
56             # fhandler - Get the file handler for a given file
57             # fhandler $filename, $classname
58             # fhandler $filename
59             # In its first form, we will return the handler for the given class name.
60             # In its second form, we will return all handlers for the given file.
61             # ------------------------------------------------------------------------------
62              
63             sub fhandler {
64 16 50   16 1 42 my $filename = shift or croak "Provide a filename";
65 16         23 my $classname = shift;
66 16         20 my @handlers = ();
67 16         62 my $filepath = Hub::abspath($filename);
68 16 50       37 return unless $filepath;
69 16         35 my $instance = $$NAMESPACE{$filepath};
70 16 50       35 if( defined $instance ) {
71 0 0       0 if (defined $classname) {
72 0 0       0 map { push @handlers, $_ if ref($_) eq $classname }
  0         0  
73 0         0 values %{$instance->{'handlers'}};
74             } else {
75 0         0 @handlers = values %{$instance->{'handlers'}};
  0         0  
76             }
77             }
78 16 50       27 wantarray and return @handlers;
79 16         45 return pop @handlers;
80             }
81              
82             # ------------------------------------------------------------------------------
83             # finstance - Get the cache instance for a specific file
84             # finstance - $filename
85             # ------------------------------------------------------------------------------
86              
87             sub finstance {
88 0 0   0 1 0 my $filename = shift or croak "Provide a filename";
89 0         0 my $path = Hub::abspath($filename);
90 0 0       0 return defined $path ? $$NAMESPACE{$path} : undef;
91             }
92              
93             # ------------------------------------------------------------------------------
94             # frefresh - Signal handlers to reparse
95             # frefresh [$filename], [options]
96             #
97             # options:
98             #
99             # -force=>1 Force re-reading all
100             # -force_dirs=>1 Force re-reading of directories
101             #
102             # Without a $filename, B file instances are checked for disk modifications.
103             # If the file has been modified, re-read the file and tell all your handlers to
104             # reparse themselves via the C method.
105             #
106             # With a $filename, only handlers for the specific filename are signaled to
107             # reparse.
108             # ------------------------------------------------------------------------------
109              
110             sub frefresh {
111 0     0 1 0 my ($opts, $fn) = Hub::opts(\@_);
112 0 0       0 my $filepath = defined $fn ? Hub::abspath($fn) : undef;
113 0         0 my @instances = defined $fn
114 0 0       0 ? grep { $_->{'filename'} eq $filepath } values %$NAMESPACE
115             : values %$NAMESPACE;
116 0         0 foreach my $instance (@instances) {
117 0 0       0 my $stats = defined $instance->{'filename'}
118             ? stat $instance->{'filename'}
119             : undef;
120 0 0       0 if (defined $stats) {
121             #warn "Refresh ", $instance->{'filename'}, "? ", $stats->mtime(), " -vs- ", $instance->{'lastread'}, "\n";
122             }
123 0 0 0     0 if (!defined $stats || ($stats->mtime() == 0)) {
124             # file no longer exists
125 0         0 delete $$Hub{Hub::getaddr($instance->{'filename'})};
126 0         0 delete $NAMESPACE->{$instance->{'filename'}};
127 0         0 next;
128             }
129 0 0 0     0 if (($$opts{'force'} || ($stats->mtime() > $instance->{'lastread'}))
    0 0        
      0        
130             || ($$opts{'force_dirs'} && -d $instance->{'filename'})) {
131             #warn " Read \n";
132 0         0 Hub::fread($instance);
133             } elsif (-d $instance->{'filename'}) {
134 0         0 my $md_filename = $instance->{'filename'}
135             . Hub::SEPARATOR . Hub::META_FILENAME;
136 0 0       0 if (-e $md_filename) {
137 0         0 my $md_stats = stat $md_filename;
138 0 0       0 if ($md_stats->mtime() > $instance->{'lastread'}) {
139             #warn " -fread b/c of meta\n";
140 0         0 Hub::fread($instance);
141             #warn " -done fread\n";
142             }
143             }
144             }
145             }
146             }
147              
148             # ------------------------------------------------------------------------------
149             # fread - Modify the provided instance to reflect what is on disk.
150             # fread $instance
151             #
152             # C<$instance> must be the special hash returned by L
153             # If all handling classes implement the C function, and they all
154             # return a true value, we will not read file.
155             # ------------------------------------------------------------------------------
156              
157             sub fread {
158 16     16 1 24 my $instance = shift;
159 16         25 my $filename = $instance->{'filename'};
160             # Do not continue if all handlers want to delay reading
161 16         18 my $delay_reading = 1;
162 16 100       96 map {
163 16         126 $delay_reading &= UNIVERSAL::can($_, 'delay_reading') ?
164             $_->delay_reading($instance) : 0;
165 16         17 } values %{$instance->{'handlers'}};
166 16 50       46 return if $delay_reading;
167             # Read file from disk
168 16         56 my $stats = stat $filename;
169 16 50       3404 if (defined $stats) {
170             #warn " -reading: $filename\n";
171 16         364 $instance->{'lastread'} = $stats->mtime();
172             # $instance->{'lastread'} = time;
173 16 100       654 if (-f $filename) {
    50          
174 8         39 my @contents = Hub::readfile($filename, '-asa=1');
175 8         220 $instance->{'lines'} = [ @contents ];
176 8         24 $instance->{'contents'} = '';
177 8         19 map { $instance->{'contents'} .= $_ } @contents;
  1528         9831  
178             } elsif (-d $filename) {
179 8 50       310 if (opendir (DIR, $filename)) {
180 8         128 $instance->{'contents'} = [grep {!/^\.+$/} readdir DIR];
  80         239  
181 8         92 closedir DIR;
182             } else {
183 0         0 warn "$!: $filename (deleting from cache)";
184 0         0 delete $$NAMESPACE{$filename};
185             }
186             }
187             # Signal all handlers to re-parse
188 16         22 for (values %{$instance->{'handlers'}}) {
  16         49  
189             #warn "reload: $$instance{'filename'}: $_\n";
190 16 50       89 $_->reload($instance) if $_;
191             }
192             }
193             }#fread
194              
195             # ------------------------------------------------------------------------------
196             1;