File Coverage

blib/lib/Rose/Conf/FileBased.pm
Criterion Covered Total %
statement 135 158 85.4
branch 45 72 62.5
condition 21 31 67.7
subroutine 17 18 94.4
pod 3 5 60.0
total 221 284 77.8


line stmt bran cond sub pod time code
1             package Rose::Conf::FileBased;
2              
3 1     1   4432 use strict;
  1         3  
  1         38  
4              
5 1     1   6 use Carp();
  1         2  
  1         15  
6 1     1   5 use File::Spec;
  1         2  
  1         20  
7              
8 1     1   5 use Rose::Conf;
  1         2  
  1         5  
9             require Tie::Hash;
10             our @ISA = qw(Rose::Conf Tie::StdHash);
11              
12             our($CONF_ROOT, %Refresh_Time, $APACHE_CONF_PATH);
13              
14             our $REFRESH_TIMEOUT = 60 * 15; # in seconds
15              
16             our $CONF_SUFFIX = '.conf';
17             our $LOCAL_CONF_FILE = 'local' . $CONF_SUFFIX;
18              
19 1     1   6 use constant PRIVATE_PREFIX => '__' . __PACKAGE__ . '::';
  1         2  
  1         780  
20              
21             our $VERSION = '0.02';
22              
23             our $Debug = 0;
24              
25             sub import
26             {
27 2     2   35 my($class) = shift;
28              
29 2         4 local $Rose::Conf::ExportLevel;
30 2         5 $Rose::Conf::ExportLevel++;
31              
32 2 100       24 $class->Rose::Conf::import(@_) if(@_);
33              
34 2         19 my $conf = $class->conf_hash;
35              
36 2         17 my %save = %$conf;
37              
38 2         22 tie(%$conf, $class);
39              
40 2         33 %$conf = %save;
41             }
42              
43             sub refresh
44             {
45 2     2 1 12 my($class) = shift;
46              
47             #return if(time < $Refresh_Time{$class} + $class->refresh_timeout);
48              
49 2         13 $class->_get_conf_root();
50              
51 2 100 66     31 if($CONF_ROOT && -d $CONF_ROOT)
52             {
53             #$Refresh_Time{$class} ||= 0;
54              
55             # Local conf file
56 1         27 my $local_conf = File::Spec->catfile($CONF_ROOT, $LOCAL_CONF_FILE);
57              
58 1 50       24 if(-s $local_conf)
59             {
60 1         11 $class->_read_combined_conf($local_conf);
61             }
62              
63             # Package-specific conf file
64 1         18 my $class_conf = File::Spec->catfile($CONF_ROOT, $class . $CONF_SUFFIX);
65              
66 1 50       26 if(-s $class_conf)
67             {
68 0         0 $class->_read_class_conf($class_conf);
69             }
70             else
71             {
72 1         3 my $mod_class = $class;
73 1         5 $mod_class =~ s/::/-/g;
74              
75 1         4 $class_conf = $CONF_ROOT . '/' . $mod_class . $CONF_SUFFIX;
76              
77 1 50       27 if(-s $class_conf)
78             {
79 1         11 $class->_read_class_conf($class_conf);
80             }
81             }
82             }
83             }
84              
85             sub FETCH
86             {
87 101     101   3984 my($hash, $key) = @_;
88              
89 101 100 100     322 unless(exists $hash->{PRIVATE_PREFIX . 'IMPORTED'} ||
90             exists $hash->{PRIVATE_PREFIX . 'IMPORTING'})
91             {
92 1 50       6 $Debug && warn "FETCH $hash { $key }\n";
93              
94 1         3 $hash->{PRIVATE_PREFIX . 'IMPORTING'} = 1;
95              
96 1         3 my $class = ref($hash);
97              
98 1         4 $class->refresh();
99              
100 1         6 delete $hash->{PRIVATE_PREFIX . 'IMPORTING'};
101              
102 1         3 $hash->{PRIVATE_PREFIX . 'IMPORTED'}++;
103             }
104              
105             ##
106             ## This is broken for now...
107             ##
108              
109             # Do not try refresh when looking up private keys
110             # unless(index($key, PRIVATE_PREFIX) == 0)
111             # {
112             # my $class = ref $hash;
113             #
114             # if(my $timeout = $class->refresh_timeout)
115             # {
116             # $Refresh_Time{$class} ||= 0;
117             #
118             # if(time > $Refresh_Time{$class} + $timeout)
119             # {
120             # $class->refresh;
121             # }
122             # }
123             # }
124              
125 101 100 100     865 Carp::croak "No such conf parameter: '$key'\n"
126             unless(index($key, PRIVATE_PREFIX) == 0 || exists $hash->{$key});
127              
128 99         576 return $hash->{$key};
129             }
130              
131             sub _get_conf_root
132             {
133 2     2   6 $CONF_ROOT = $ENV{'ROSE_CONF_FILE_ROOT'};
134              
135 2 0 66     16 if(!$CONF_ROOT && exists $ENV{'MOD_PERL'} && require mod_perl && $mod_perl::VERSION < 1.99)
      33        
      33        
136             {
137 0         0 $CONF_ROOT = Apache->server_root_relative($APACHE_CONF_PATH);
138 0 0       0 $CONF_ROOT = undef unless(-d $CONF_ROOT);
139             }
140             }
141              
142             sub refresh_timeout
143             {
144 0     0 0 0 my($class) = shift;
145              
146 1     1   5 no strict 'refs';
  1         2  
  1         1862  
147              
148 0 0       0 if(@_)
149             {
150 0         0 return ${$class . '::REFRESH_TIMEOUT'} = shift;
  0         0  
151             }
152              
153 0         0 my $timeout = ${$class . '::REFRESH_TIMEOUT'};
  0         0  
154              
155 0 0       0 $timeout = $REFRESH_TIMEOUT unless(defined $timeout);
156              
157 0         0 return $timeout;
158             }
159              
160             sub _parse_line
161             {
162 21     21   46 my($class, $conf, $line, $file, $line_num) = @_;
163              
164 21 100       89 return unless($line =~ /\S/);
165              
166 16         20 my($key, $val);
167              
168 16 100       87 if($line =~ /^((?:[^\\ \t]+|\\.)+)\s*=\s*(\S.*|$)/)
    50          
169             {
170 14         28 $key = $1;
171 14         25 $val = $2;
172             }
173             elsif($line !~ /^(#|$)/)
174             {
175 0         0 die "Syntax error in $file on line $line_num: $line\n";
176             }
177 2         8 else { return }
178              
179 14 50 33     75 if(length($key) && length($val))
180             {
181 14 100       60 if($val =~ s/(['"])(.*)\1$/$2/)
182             {
183 8 100 100     82 if($1 eq '"' && index($val, '\\') >= 0)
184             {
185 1         99 $val = eval qq("$val");
186              
187 1 50       6 if($@)
188             {
189 0         0 die qq(Invalid value "$val" in $file on line $line_num: $@\n);
190             }
191             }
192             }
193              
194             # Hash sub-key access
195 14 100       46 if($key =~ m/^(?:[^\\: \t]+|\\.)+:/)
196             {
197 4         5 my $original_key = $key;
198              
199 4 50       21 if($key =~ /^(?:[^\\: \t]+|\\.)+:$/)
200             {
201 0         0 Carp::croak qq($class - Invalid hash sub-key access: "$key", ),
202             qq(missing key name after final ':' in $file line $line_num);
203             }
204              
205 4         5 my @parts;
206 4         4 my $param = $conf;
207 4         6 my $prev_param;
208              
209 4         22 while($key =~ m/\G((?:[^\\: \t]+|\\.)+)(?::|$)/g)
210             {
211 13         16 $prev_param = $param;
212 13   100     50 $param = $param->{$1} ||= {};
213 13         30 push(@parts, $1);
214 13         70 $parts[-1] =~ s{\\(.)}{$1}g;
215             }
216            
217 4 50       23 $Debug && warn "\$${class}::CONF{", join('}{', @parts), "} = $val\n";
218              
219 4         12 $prev_param->{$parts[-1]} = $val;
220 4         12 $key = $original_key;
221             }
222             else
223             {
224 10         20 $key =~ s{\\(.)}{$1}g;
225 10 50       24 $Debug && warn "\$${class}::CONF{$key} = $val\n";
226 10         53 $conf->{$key} = $val;
227 10         68 $key =~ s{:}{\\:}g;
228             }
229             }
230             else
231             {
232 0 0       0 $Debug && warn "\$${class}::CONF{$key} = undef\n";
233 0         0 $conf->{$key} = undef;
234 0         0 $key =~ s{:}{\\:}g;
235             }
236              
237 14         78 $conf->{PRIVATE_PREFIX . 'MODIFIED'}{$key} =
238             {
239             file => $file,
240             line_number => $line_num,
241             #time => time(),
242             };
243             }
244              
245             sub _read_class_conf
246             {
247 1     1   2 my($class, $file) = @_;
248              
249 1 50       44 unless(open(CONF, $file))
250             {
251 0         0 warn "Could not open $file: $!";
252 0         0 return;
253             }
254              
255             #$Refresh_Time{$class} = time;
256              
257 1         6 my $conf = $class->conf_hash;
258              
259 1         25 while()
260             {
261 9         24 s/^\s+//;
262 9         33 s/\s+$//;
263              
264 9         26 $class->_parse_line($conf, $_, $file, $.);
265             }
266              
267 1         14 close(CONF);
268             }
269              
270             sub _read_combined_conf
271             {
272 1     1   2 my($class, $file) = @_;
273              
274 1         2 my $conf_fh;
275              
276 1 50       55 unless(open($conf_fh, $file))
277             {
278 0         0 warn "Could not open $file: $!";
279 0         0 return;
280             }
281              
282             #$Refresh_Time{$class} = time;
283              
284 1         7 my $conf = $class->conf_hash;
285              
286 1         3 my $in_domain = 0;
287              
288 1         43 my $in_domain_re = qr(^CLASS\s+$class$);
289 1         7 my $out_domain_re = qr(^CLASS\s*(?!=));
290              
291 1         37 while(<$conf_fh>)
292             {
293 17         43 s/^\s+//;
294 17         71 s/\s+$//;
295              
296 17 100 66     135 if(/$in_domain_re/)
    50          
297             {
298 1         2 $in_domain = 1;
299 1         4 next;
300             }
301             elsif($in_domain && /$out_domain_re/)
302             {
303 0         0 $in_domain = 0;
304             }
305              
306 16 100       41 next unless($in_domain);
307              
308 12         42 $class->_parse_line($conf, $_, $file, $.);
309             }
310              
311 1         21 close($conf_fh);
312             }
313              
314             sub local_conf_keys
315             {
316 1     1 1 534 my($class) = shift;
317              
318 1         5 my $conf = $class->conf_hash;
319              
320 1         2 return keys(%{$conf->{PRIVATE_PREFIX . 'MODIFIED'}});
  1         5  
321             }
322              
323             sub local_conf_setting
324             {
325 6     6 0 9 my($class, $key) = @_;
326              
327 6 50       13 Carp::croak "Cannot get setting without $key" unless(defined($key));
328              
329 6         21 my $conf = $class->conf_hash;
330              
331 6 100       27 return unless($conf->{PRIVATE_PREFIX . 'MODIFIED'}{$key});
332              
333 5         21 return bless($conf->{PRIVATE_PREFIX . 'MODIFIED'}{$key}, 'Rose::Conf::File::Setting');
334             }
335              
336             sub local_conf_value
337             {
338 6     6 1 70 my($class, $key) = @_;
339              
340 6 50       16 Carp::croak "Cannot get setting without $key" unless(defined($key));
341              
342 6 100       21 $class->local_conf_setting($key) || return undef;
343              
344 5 100       26 if($key =~ m/^(?:[^\\: \t]+|\\.)+:/)
345             {
346 2 50       69 if($key =~ /^(?:[^\\: \t]+|\\.)+:$/)
347             {
348 0         0 Carp::croak qq($class - Invalid hash sub-key access: "$key" - missing key name after final ':');
349             }
350              
351 2         4 my @parts;
352 2         6 my $param = $class->conf_hash;
353 2         3 my $prev_param;
354              
355 2         12 while($key =~ m/\G((?:[^\\: \t]+|\\.)+)(?::|$)/g)
356             {
357 7         10 $prev_param = $param;
358 7   50     24 $param = $param->{$1} ||= {};
359 7         15 push(@parts, $1);
360 7         32 $parts[-1] =~ s{\\(.)}{$1}g;
361             }
362            
363 2 50       7 $Debug && warn "Get local conf value for \$${class}::CONF{", join('}{', @parts), "}\n";
364              
365 2         14 return $prev_param->{$parts[-1]};
366             }
367              
368 3         8 $key =~ s{\\:}{:}g;
369 3         11 return $class->param($key);
370             }
371              
372             BEGIN
373             {
374 1     1   4 $APACHE_CONF_PATH = 'conf/perl';
375              
376             #_get_conf_root();
377              
378 1         1083 require Rose::Object;
379 1         244 @Rose::Conf::File::Setting::ISA = qw(Rose::Object);
380             }
381              
382             1;
383              
384             # =item B
385             #
386             # If an argument is provided, sets the class's refresh timeout to SECS
387             # seconds. Returns the class's current refresh timeout in seconds.
388             #
389             # The refresh timeout is used to determine when (if ever) to refresh the
390             # configuration hash by re-reading the (possibly changed) configuration
391             # file(s). The configuration file(s) will only be re-read if they have
392             # been modified since they were last read (as determined by the files'
393             # "mtime").
394             #
395             # If the refresh timeout is zero, the files are never re-read. The
396             # default refresh timeout is 15 minutes.
397              
398             __END__