File Coverage

blib/lib/Gantry/Conf.pm
Criterion Covered Total %
statement 115 164 70.1
branch 30 44 68.1
condition 13 25 52.0
subroutine 12 16 75.0
pod 2 2 100.0
total 172 251 68.5


line stmt bran cond sub pod time code
1             package Gantry::Conf;
2              
3             #####################################################################
4             #
5             # Name : Gantry::Conf
6             # Author : Frank Wiles
7             #
8             # Description : This is the module used by the Gantry programmer
9             # to setup the Gantry auto-configuration and to
10             # retrieve the configuration for a particular
11             # instance of the application
12             #
13             # While this configuration system is packaged with
14             # Gantry, it strives to be entirely independant.
15             #
16             #####################################################################
17              
18 2     2   174922 use strict;
  2         6  
  2         78  
19 2     2   13 use warnings;
  2         4  
  2         72  
20              
21 2     2   11 use Carp qw( croak );
  2         5  
  2         109  
22 2     2   14 use Config::General;
  2         3  
  2         90  
23 2     2   10 use Hash::Merge qw( merge );
  2         4  
  2         4501  
24              
25             # Dispatch table
26             my %dispatch = (
27             PerlSetVar => '_configure_set_var',
28             ParamBuilder => '_configure_parambuilder',
29             FlatFile => '_configure_flat_file',
30             SQL => '_configure_sql',
31             HTTP => '_configure_http',
32             Default => '_configure_main_conf',
33             );
34             our %main_config;
35              
36             sub import {
37 1     1   12 my ( $class, @options ) = @_;
38 1         1 my $cfg;
39              
40 1         13 foreach (@options) {
41 0 0       0 if ( /^-Config=(.*?)$/ ) {
42 0         0 my $file = $1;
43              
44 0         0 _check_file( $class, $file );
45              
46 0         0 $cfg = Config::General->new(
47             -ConfigFile => $file,
48             -UseApacheInclude => 1,
49             -IncludeGlob => 1,
50             -IncludeDirectories => 1,
51             -IncludeRelative => 1,
52             );
53              
54 0         0 %main_config = $cfg->getall;
55             }
56             }
57             }
58              
59             #------------------------------------------------
60             # new
61             #------------------------------------------------
62             # This is used to build our configuration object
63             # and to allow sub-classing of Gantry::Conf
64             #------------------------------------------------
65             sub new {
66 15     15 1 19 my $class = shift;
67 15         23 my $self = {};
68              
69 15         29 bless( $self, $class );
70              
71 15         34 return( $self );
72              
73             } # END new
74              
75             #------------------------------------------------
76             # retrieve
77             #------------------------------------------------
78             # This retrieves the configuration for a
79             # particular "instance" of an application and
80             # returns a hash reference with all of the
81             # configuration options
82             #------------------------------------------------
83             sub retrieve {
84 17     17 1 16308 my $class = shift;
85 17         26 my $params = shift;
86              
87 17 100 66     131 croak "Gantry::Conf ERROR: No parameter hash given to retrieve"
88             unless ( defined $params and ref( $params ) eq 'HASH' );
89              
90             # Die if we aren't given an instance
91             croak "Gantry::Conf ERROR: No instance given to retrieve()"
92 16 100       49 unless ( $params->{ instance } );
93              
94 15         43 my $self = Gantry::Conf->new;
95              
96             # Use /etc/gantry.conf if no other file is given
97 15   50     41 my $config_file = ( $params->{ config_file } ) || '/etc/gantry.conf';
98              
99             # Retrieve the actual configuration
100             $self->_load_configuration(
101             $params->{ instance },
102             $config_file,
103             $params->{ location },
104             $params->{ reload_config },
105 15         62 );
106              
107             # Return our configuration
108 11         226 return( $$self{__config__} );
109              
110             } # END retrieve
111              
112             #------------------------------------------------
113             # _load_configuration
114             #------------------------------------------------
115             # This retrieves our instance information from
116             # /etc/gantry.conf
117             #------------------------------------------------
118             sub _load_configuration {
119 15     15   22 my $self = shift;
120 15         23 my $instance = shift;
121 15         18 my $file = shift;
122 15         29 my $location = shift;
123 15         21 my $reload_config = shift;
124              
125             # Make sure our file is there and readable
126 15         39 $self->_check_file( $file, 'readonly' );
127              
128             # Get a Config::General object and have it read our configuration
129             # filename.
130             #
131             # We set these options:
132             # -UseApacheInclude to allow "include /etc/foo.conf" from within
133             # a config file
134             #
135             # -IncludeGlob to allow a user to do this in their main conf
136             # include /etc/gantry.d/*.conf
137             #
138             # -IncludeDirectories to allow a user to include a directory of
139             # files without a glob, it loads them in ASCII
140             # order
141             #
142             # -IncludeRelative to allow including relative files
143             #
144            
145             # Retrieve the config if it has not already been loaded
146             # or if a config reload is being forced.
147 14 50 66     78 if ( (! %main_config) or $reload_config ) {
148 14         128 my $cfg = Config::General->new(
149             -ConfigFile => $file,
150             -UseApacheInclude => 1,
151             -IncludeGlob => 1,
152             -IncludeDirectories => 1,
153             -IncludeRelative => 1,
154             );
155              
156 14         19806 %main_config = $cfg->getall;
157             }
158              
159             # Look for the instance
160 14 100       359 if( !$main_config{'instance'}{$instance} ) {
161 1         14 croak "Gatry::Conf ERROR: Unable to find '$instance'";
162             }
163              
164             # Store this to reduce hash lookups
165 13         28 my $instance_ref = $main_config{'instance'}{$instance};
166              
167             # Handle all ConfigVia statements
168 13         24 my $configure_via = $$instance_ref{ConfigureVia};
169              
170 13         17 my @config_statements;
171              
172 13 100       48 if ( ref( $configure_via ) =~ /ARRAY/ ) {
    100          
173 1         2 @config_statements = @{ $configure_via };
  1         2  
174             }
175             elsif ( not defined $configure_via ) {
176 5         8 push @config_statements, 'Default';
177             }
178             else {
179 7         13 push @config_statements, $configure_via;
180             }
181              
182 13         22 foreach my $config ( @config_statements ) {
183 14         58 my ( $method_name, @params ) = split /\s+/, $config;
184 14         33 my $method = $dispatch{ $method_name };
185              
186 14 100       45 croak "Gantry::Conf ERROR: No such ConfigureVia method: $method_name\n"
187             unless $method;
188              
189 13         56 $self->$method( $instance, $instance_ref, @params );
190             }
191              
192 11         36 Hash::Merge::set_behavior( 'LEFT_PRECEDENT' );
193 11         148 Hash::Merge::set_clone_behavior(0);
194              
195             # Merge in our global configs if we have any
196 11 100       98 if( $main_config{'global'} ) {
197 3         8 $$self{__global__} = $main_config{'global'};
198              
199 3         11 $$self{__config__} = merge( $$self{__config__}, $$self{__global__} );
200              
201             }
202              
203             # Merge in any shared configs if any
204 11         193 my $shares = $$instance_ref{'use'};
205 11 100 66     62 if( $shares and !ref($shares) ) {
    50 33        
206              
207 3         14 $$self{__config__}
208             = merge( $$self{__config__}, $main_config{'shared'}{$shares} );
209              
210             }
211             elsif( $shares and ref($shares) eq 'ARRAY' ) {
212 0         0 foreach my $s ( @{ $shares } ) {
  0         0  
213              
214 0         0 $$self{__config__} = merge( $$self{__config__},
215             $main_config{'shared'}{$s} );
216              
217             }
218             }
219              
220             # deal with location promotion
221 11 100       192 if ( defined $location ) {
222 3         10 my $locations = delete $$self{__config__}{GantryLocation};
223 3         12 my @path = split( '/', $location );
224            
225 3         6 my @check_paths;
226            
227 3         9 while ( @path ) {
228 5         11 my $path = join( '/', @path );
229 5         7 push( @check_paths, $path );
230 5         13 pop( @path );
231             }
232              
233 3         10 foreach my $path ( reverse( @check_paths ) ) {
234            
235 5         83 my $location_hash = $$locations{$path};
236            
237 5 100       12 if ( defined $location_hash ) {
238            
239 3         10 $$self{__config__} = merge(
240             $location_hash,
241             $$self{__config__}
242             );
243             }
244            
245            
246             }
247              
248             #my $location_hash = $$locations{$location};
249             #if ( defined $location_hash ) {
250             # warn( "defined!2 $location" );
251             # $$self{__config__} = merge(
252             # $location_hash,
253             # $$self{__config__}
254             # );
255             #}
256            
257             }
258              
259             } # END _load_configuration
260              
261             #------------------------------------------------
262             # _check_file( $file, readonly )
263             #------------------------------------------------
264             # This makes sure we can find, read, and write
265             # a particular file. If readonly is passed to
266             # it then we only check to ensure we can read it
267             #------------------------------------------------
268             sub _check_file {
269 15     15   20 my $self = shift;
270 15         17 my $file = shift;
271 15   50     34 my $ro = shift || 0;
272              
273             # Check for existance
274 15 100       370 if( ! -e $file ) {
275 1         18 croak "Gantry::Conf ERROR - Configuration file '$file' does not exist";
276             }
277              
278             # Check for readability
279 14 50       241 if( ! -r $file ) {
280 0         0 croak "Gantry::Conf ERROR - Unable to read configuration file '$file' ".
281             "check the file permissions";
282             }
283              
284             # Check for write access if we are supposed to
285 14 0 33     33 if( not $ro and ( !-w $file ) ) {
286 0         0 croak "Gantry::Conf ERROR - Unable to write file '$file'. Check the ".
287             " file permissions";
288             }
289              
290             # Return true
291 14         26 return( 1 );
292              
293             } # END _check_file
294              
295             #------------------------------------------------
296             # _configure_set_var
297             #------------------------------------------------
298             # Load the configuration from the setvar
299             # provider
300             #------------------------------------------------
301             sub _configure_set_var {
302 0     0   0 my $self = shift;
303 0         0 my $instance = shift;
304 0         0 my $instance_ref = shift;
305              
306             # Populate our configuration via the provider
307 0         0 my $backend = 'Gantry::Conf::Provider::PerlSetVar';
308 0         0 eval "require $backend";
309              
310 0 0       0 if( $@ ) {
311 0         0 croak "Unable to load '$backend': $!";
312             }
313              
314             # Populate the configuration
315 0         0 $$self{__config__} =
316             Gantry::Conf::Provider::PerlSetVar->config( $instance,
317             $instance_ref );
318              
319             # Return true
320 0         0 return( 1 );
321              
322             } # END _configure_set_var
323              
324             #------------------------------------------------
325             # _configure_parambuilder
326             #------------------------------------------------
327             sub _configure_parambuilder {
328 0     0   0 my $self = shift;
329 0         0 my $instance = shift;
330 0         0 my $instance_ref = shift;
331 0         0 my $provider = shift;
332 0         0 my @files = @_;
333              
334             } # END _configure_parambuilder
335              
336             #------------------------------------------------
337             # _configure_flat_file
338             #------------------------------------------------
339             # Use the indicated provider to load the
340             # configuration from a flat file
341             #------------------------------------------------
342             sub _configure_flat_file {
343 8     8   11 my $self = shift;
344 8         68 my $instance = shift;
345 8         13 my $instance_ref = shift;
346 8         27 my $provider = shift;
347 8         17 my @files = @_;
348              
349             # Populate our configuration via the provider
350 8         16 my $backend = 'Gantry::Conf::Provider::FlatFile::' . $provider;
351 8         604 eval "require $backend";
352              
353 8 50       37 if( $@ ) {
354 0         0 croak "Unable to require '$backend': $!";
355             }
356              
357 8         39 Hash::Merge::set_behavior( 'LEFT_PRECEDENT' );
358 8         143 Hash::Merge::set_clone_behavior(0);
359              
360 8   100     199 $$self{__config__} ||= {};
361 8         14 foreach my $file_path ( @files ) {
362              
363 9         13 eval {
364 9         59 my $config_from_file = $backend->config( $file_path );
365 8         109 $$self{__config__} = merge( $$self{__config__}, $config_from_file );
366             };
367              
368 9 100       1488 if( $@ ) {
369 1         19 croak 'Unable to load configuration via '
370             . "Gantry::Conf::Provider::FlatFile::$provider: $@ $!";
371             }
372             }
373              
374             # Return true
375 7         30 return( 1 );
376              
377             } # END _configure_flat_file
378              
379             #------------------------------------------------
380             # _configure_sql
381             #------------------------------------------------
382             sub _configure_sql {
383 0     0   0 my $self = shift;
384 0         0 my $instance = shift;
385 0         0 my $instance_ref = shift;
386 0         0 my $provider = shift;
387 0         0 my @parameters = @_;
388              
389             } # END _configure_sql
390              
391             #------------------------------------------------
392             # _configure_http
393             #------------------------------------------------
394             sub _configure_http {
395 0     0   0 my $self = shift;
396 0         0 my $instance = shift;
397 0         0 my $instance_ref = shift;
398 0         0 my $provider = shift;
399 0         0 my @urls = @_;
400              
401             # Populate our configuration via the provider
402 0         0 my $backend = 'Gantry::Conf::Provider::HTTP::' . $provider;
403 0         0 eval "require $backend";
404              
405 0 0       0 if( $@ ) {
406 0         0 croak "Unable to require '$backend': $@ $!";
407             }
408              
409 0         0 Hash::Merge::set_behavior( 'LEFT_PRECEDENT' );
410 0         0 Hash::Merge::set_clone_behavior(0);
411              
412 0   0     0 $$self{__config__} ||= {};
413              
414 0         0 foreach my $url ( @urls ) {
415              
416 0         0 eval {
417 0         0 my $config_from_web = $backend->config( $url );
418 0         0 $$self{__config__} = merge( $$self{__config__}, $config_from_web );
419             };
420              
421 0 0       0 if( $@ ) {
422 0         0 croak 'Unable to load configuration via '
423             . "Gantry::Conf::Provider::HTTP::$provider: $@ $!";
424             }
425             }
426              
427 0         0 return 1;
428              
429             } # END _configure_http
430              
431             #------------------------------------------------
432             # _configure_main_conf
433             #------------------------------------------------
434             # If the user didn't specify a ConfigureViaXXXX
435             # option then assume they want to configure in
436             # the main /etc/gantry.conf
437             #------------------------------------------------
438             sub _configure_main_conf {
439 5     5   8 my $self = shift;
440 5         8 my $instance = shift;
441 5         6 my $instance_ref = shift;
442 5         8 my $provider = shift;
443 5         10 my @files = @_;
444              
445             # Set hash merging precedence
446 5         19 Hash::Merge::set_behavior( 'LEFT_PRECEDENT' );
447 5         79 Hash::Merge::set_clone_behavior(0);
448              
449             # Make sure we have a configuration already
450 5   50     63 $$self{__config__} ||= {};
451              
452             # Make a copy of our instance ref, skipping any 'use' methods
453 5         6 my %temp_instance;
454 5         7 foreach my $key ( keys( %{ $instance_ref } ) ) {
  5         17  
455 13 100       26 next if $key eq 'use';
456 12         32 $temp_instance{$key} = $$instance_ref{$key};
457             }
458              
459 5         23 $$self{__config__} = merge( $$self{__config__}, \%temp_instance );
460              
461             # Return true
462 5         300 return( 1 );
463              
464             } # END _configure_main_conf
465              
466             1;
467             __END__