File Coverage

blib/lib/Config/Constants.pm
Criterion Covered Total %
statement 59 63 93.6
branch 19 22 86.3
condition n/a
subroutine 9 12 75.0
pod n/a
total 87 97 89.6


line stmt bran cond sub pod time code
1              
2             package Config::Constants;
3              
4 13     13   296047 use strict;
  13         30  
  13         496  
5 13     13   67 use warnings;
  13         26  
  13         5122  
6              
7             our $VERSION = '0.03';
8              
9             my %CONFIG_LOADER_CLASSES = (
10             perl => 'Config::Constants::Perl',
11             xml => 'Config::Constants::XML'
12             );
13              
14             # our config object
15             my $CONFIG;
16              
17             # these two hashes are used
18             # for checking to make sure
19             # no invalid constants are used
20             my %UNCHECKED_CONSTANTS;
21             my %CHECKED_CONSTANTS;
22              
23             sub import {
24 33     33   20409 shift;
25 33 100       137 return unless @_;
26 32         83 my @args = @_;
27 32 100       210 if ($args[0] =~ /xml|perl/) {
28 12         26 my ($type, $file) = @args;
29 12         29 my $config_loader_class = $CONFIG_LOADER_CLASSES{$type};
30             # try to load the config loader ...
31 12     12   681 eval "use $config_loader_class";
  12         6509  
  12         34  
  12         250  
32 12 50       127 die "Failed to load config loader class ($config_loader_class) : $@" if $@;
33             # try to load the config ...
34 12         28 $CONFIG = eval { $config_loader_class->new($file) };
  12         95  
35 12 100       72 die "Failed to load config (type => $type, file => $file) : $@" if $@;
36 11         41 _load_all_modules();
37             }
38             else {
39 20         56 my $calling_pkg = caller();
40 20 100       70 if (exists $UNCHECKED_CONSTANTS{$calling_pkg}) {
41             # this means that the conf was loaded first, so we
42             # need to check the constants which were created
43             # against the ones which are allowed for this
44             # module, and if we find we have created extra
45             # ones we throw an exception
46 13         34 foreach my $arg (@args) {
47 20 100       104 delete $UNCHECKED_CONSTANTS{$calling_pkg}->{$arg}
48             if exists $UNCHECKED_CONSTANTS{$calling_pkg}->{$arg}
49             }
50 0         0 die "Unchecked constants found in config for '$calling_pkg' -> (" .
51 13         62 join(", " => keys(%{$UNCHECKED_CONSTANTS{$calling_pkg}})) .
52 13 50       26 ")" if keys %{$UNCHECKED_CONSTANTS{$calling_pkg}};
53             }
54             else {
55             # this means the conf has not been loaded yet,
56             # so we need to build a list of acceptable
57             # constants to check against.
58 7         13 $CHECKED_CONSTANTS{$calling_pkg} = { map { $_ => undef } @args };
  10         40  
59             }
60 13     13   93 no strict 'refs';
  13         37  
  13         3099  
61 20         78 foreach my $arg (@args) {
62             # skip it if it has already been defined
63             # NOTE: this means that the config was
64             # loaded before the module itself was
65             # loaded, so we don't want to overwrite
66 30 100       81 next if defined &{"${calling_pkg}::$arg"};
  30         983  
67             # However, if it hasn't been defined, then
68             # we want to do so. This will create a
69             # stub sub which will die if it is not
70             # configured.
71             # NOTE: the sub does not have the () prototype
72             # here so that we can prevent constant folding
73             # from happening. When the proper sub gets
74             # installed, it will have that prototype (and
75             # thus be folded in)
76 11     0   41 *{"${calling_pkg}::$arg"} = sub { die "undefined Config::Constant in ${calling_pkg}::$arg" };
  11         507  
  0         0  
77             }
78             }
79             }
80              
81             ## For DEBUGGING
82             #INIT {
83             # use Data::Dumper;
84             # print "UNCHECKED: " . Dumper \%UNCHECKED_CONSTANTS;
85             # print "CHECKED: " . Dumper \%CHECKED_CONSTANTS;
86             #}
87              
88             ## Private Utility Functions
89              
90             sub _load_all_modules {
91 11     11   71 foreach my $module ($CONFIG->modules()) {
92 18         52 _load_module($module);
93             }
94             }
95              
96             sub _load_module {
97 18     18   46 my $module = shift;
98 13     13   75 no strict 'refs';
  13         32  
  13         974  
99 18         101 foreach my $constant ($CONFIG->constants($module)) {
100 27         39 my ($name, $value) = each %{$constant};
  27         73  
101 27 100       41 if (defined &{"${module}::$name"}) {
  27         144  
102             # since this already exists, we
103             # assume that it is a valid constant
104             # and that our module was already
105             # loaded
106 13     13   68 no warnings;
  13         22  
  13         2730  
107 9     0   63 *{"${module}::$name"} = sub () { $value };
  9         65  
  0         0  
108 9         11177 delete $CHECKED_CONSTANTS{$module}->{$name};
109             }
110             else {
111             # since this does not exist, then our
112             # module may not have been loaded yet.
113             # but in order to determine this,
114             # we have to see if the constants have
115             # been registered yet ...
116 18 100       83 if (exists $CHECKED_CONSTANTS{$module}) {
117             # this means our module was loaded first, and
118             # the constants were registered. Now, if
119             # we do not see this particular constant
120             # in here, then we need to throw an exception
121 1 50       27 die "Unknown constant for '$module' -> ($name)"
122             unless exists $CHECKED_CONSTANTS{$module}->{$name};
123             }
124             else {
125             # being in this block means that is is unlikely
126             # the conf has been loaded yet, so we will
127             # just create our constants, and make a note
128             # of each of them so that they can be
129             # checked later on.
130 17         45 $UNCHECKED_CONSTANTS{$module} = { $name => undef };
131 17     0   145 *{"${module}::$name"} = sub () { $value };
  17         2291  
  0         0  
132             }
133             }
134             }
135             }
136              
137              
138             1;
139              
140             __END__