File Coverage

blib/lib/Config/Onion.pm
Criterion Covered Total %
statement 108 111 97.3
branch 36 48 75.0
condition 5 6 83.3
subroutine 14 14 100.0
pod 5 5 100.0
total 168 184 91.3


line stmt bran cond sub pod time code
1             package Config::Onion;
2              
3 6     6   188432 use strict;
  6         6  
  6         134  
4 6     6   19 use warnings;
  6         7  
  6         182  
5              
6             our $VERSION = 1.007;
7              
8 6     6   2363 use Config::Any;
  6         48976  
  6         147  
9 6     6   2362 use Hash::Merge::Simple 'merge';
  6         1878  
  6         277  
10 6     6   2488 use Moo;
  6         38739  
  6         26  
11              
12             has cfg => ( is => 'lazy', clearer => '_reset_cfg' );
13             has prefix_key => ( is => 'rw' );
14 31     31 1 2061 sub get { goto &cfg }
15              
16             has [qw( default main local override )]
17             => ( is => 'rwp', default => sub { {} } );
18              
19             sub set_default {
20 9     9 1 2222 my $self = shift;
21 9 100       103 $self = $self->new unless ref $self;
22              
23 9         31 my $default = $self->default;
24 9         28 $default = merge $default, shift while ref $_[0] eq 'HASH';
25 9 100       86 $default = merge $default, { @_ } if @_;
26              
27 9         140 $self->_set_default($default);
28 9         104 $self->_reset_cfg;
29 9         888 return $self;
30             }
31              
32             sub set_override {
33 2     2 1 7 my $self = shift;
34 2 50       9 $self = $self->new unless ref $self;
35              
36 2         6 my $override = $self->override;
37 2         7 $override = merge $override, shift while ref $_[0] eq 'HASH';
38 2 50       12 $override = merge $override, { @_ } if @_;
39              
40 2         35 $self->_set_override($override);
41 2         37 $self->_reset_cfg;
42 2         7 return $self;
43             }
44              
45             sub load {
46 8     8 1 1916 my $self = shift;
47 8 100       100 $self = $self->new unless ref $self;
48              
49 8         28 my $ca_opts = $self->_ca_opts;
50             # user passed in a hash ref as the last argument.
51 8 50       25 if (ref $_[$#_] eq "HASH") {
52             # allow user to override the default use_ext => 1
53 0 0       0 delete $ca_opts->{use_ext} unless exists $_[$#_]{use_ext};
54             # merge in any other options passed in
55 0         0 $ca_opts = merge $ca_opts, pop @_;
56             # ensure that flatten_to_hash feature that Config::Any supports is turned
57             # off
58 0 0       0 delete $ca_opts->{flatten_to_hash} if exists $ca_opts->{flatten_to_hash};
59             }
60              
61 8         56 my $main = Config::Any->load_stems({ stems => \@_ , %$ca_opts });
62 8         99313 my $local = Config::Any->load_stems({ stems => [ map { "$_.local" } @_ ],
  9         76  
63             %$ca_opts });
64              
65 8         45644 $self->_add_loaded($main, $local);
66 8         383 return $self;
67             }
68              
69             sub load_glob {
70 3     3 1 1033 my $self = shift;
71 3 50       64 $self = $self->new unless ref $self;
72              
73 3         17 my $ca_opts = $self->_ca_opts;
74             # user passed in a hash ref as the last argument.
75 3 100       12 if (ref $_[$#_] eq "HASH") {
76             # allow user to override the default use_ext => 1
77 1 50       4 delete $ca_opts->{use_ext} unless exists $_[$#_]{use_ext};
78             # merge in any other options passed in
79 1         4 $ca_opts = merge $ca_opts, pop @_;
80             # ensure that flatten_to_hash feature that Config::Any supports is turned
81             # off
82 1 50       19 delete $ca_opts->{flatten_to_hash} if exists $ca_opts->{flatten_to_hash};
83             }
84              
85             # if use_ext is on, we need to query Config::Any to see what extensions are
86             # allowed
87 3         5 my $ext_re = '';
88 3 100       23 if ($ca_opts->{use_ext}) {
89 2         9 my @exts = Config::Any->extensions();
90 2         4878 $ext_re = '\.' . (shift @exts) . '$';
91 2         12 $ext_re .= "|\\.$_\$" foreach @exts;
92             }
93              
94 3         3 my (@main_files, @local_files);
95 3         5 for my $globspec (@_) {
96 3         197 for (glob $globspec) {
97 8 100 100     79 next if $ca_opts->{use_ext} && !/$ext_re/;
98 6 100       13 if (/\.local\./) { push @local_files, $_ }
  1         2  
99 5         10 else { push @main_files, $_ }
100             }
101             }
102              
103 3         22 my $main = Config::Any->load_files({ files => \@main_files, %$ca_opts });
104 3         15119 my $local = Config::Any->load_files({ files => \@local_files, %$ca_opts });
105              
106 3         6265 $self->_add_loaded($main, $local);
107              
108 3         23 return $self;
109             }
110              
111             sub _add_loaded {
112 11     11   18 my $self = shift;
113 11         15 my ($main, $local) = @_;
114              
115 11 100       11 my @main; @main = map { values %$_ } @$main if @$main;
  11         39  
  14         39  
116 11 100       10 my @local; @local = map { values %$_ } @$local if @$local;
  11         24  
  4         11  
117              
118 11 100       55 if ($self->prefix_key) {
119 1         3 for my $cfg (@main, @local) {
120 1 50       6 $self->_replace_prefix_key($cfg) if exists $cfg->{$self->prefix_key};
121             }
122             }
123              
124 11         50 $self->_set_main( merge $self->main, @main);
125 11         520 $self->_set_local(merge $self->local, @local);
126              
127 11         268 $self->_reset_cfg;
128             }
129              
130             sub _build_cfg {
131 21     21   1631 my $self = shift;
132 21         105 my $cfg = merge $self->default, $self->main, $self->local, $self->override;
133              
134             # remove any hash keys with a (merged) value of '!DELETE!'
135 21         828 my @nodes = $cfg;
136 21         52 while (my $curr = shift @nodes) {
137 34         66 for (keys %$curr) {
138 69 100       107 next unless defined $curr->{$_};
139 67 100       175 if ($curr->{$_} eq '!DELETE!') {
    100          
140 1         4 delete $curr->{$_};
141             } elsif (ref $curr->{$_} eq 'HASH') {
142 13         28 push @nodes, $curr->{$_};
143             }
144             }
145             }
146              
147 21         118 return $cfg;
148             }
149              
150 11     11   24 sub _ca_opts { { use_ext => 1 } }
151              
152             sub _replace_prefix_key {
153 1     1   2 my $self = shift;
154 1         1 my $cfg = shift;
155              
156 1         2 my $top_key;
157 1         3 my $root = $cfg->{$self->prefix_key};
158 1         2 while (1) {
159 3 50       5 die "Config::Onion prefix key structure may not branch" if keys %$root > 1;
160 3   66     9 $top_key ||= (keys %$root)[0];
161 3         3 my $child = (values %$root)[0];
162 3 100       7 unless ($child) {
163 1         1 my $key = (keys %$root)[0];
164 1         3 $root = $root->{$key} = {};
165 1         1 last;
166             }
167 2         2 $root = $child;
168             }
169              
170 1         4 my $new = $cfg->{$self->prefix_key}{$top_key};
171 1         3 delete $cfg->{$self->prefix_key};
172              
173 1         4 for (keys %$cfg) {
174 2         3 $root->{$_} = $cfg->{$_};
175 2         4 delete $cfg->{$_};
176             }
177 1         2 $cfg->{$top_key} = $new;
178             }
179              
180             1;
181              
182             =pod
183              
184             =encoding UTF-8
185              
186             =head1 NAME
187              
188             Config::Onion - Layered configuration, because configs are like ogres
189              
190             =head1 VERSION
191              
192             version 1.007
193              
194             =head1 SYNOPSIS
195              
196             my $cfg = Config::Onion->new;
197             my $cfg = Config::Onion->set_default(db => {name => 'foo', password => 'bar'});
198             my $cfg = Config::Onion->load('/etc/myapp', './myapp');
199             my $cfg = Config::Onion->load('/etc/myapp', './myapp', {use_ext => 1, filter => \&filter});
200             my $cfg = Config::Onion->load_glob('./plugins/*');
201             my $cfg = Config::Onion->load_glob('./plugins/*', {force_plugins => ['Config::Any::YAML']});
202              
203             $cfg->set_default(font => 'Comic Sans');
204             $cfg->load('config');
205             $cfg->load_glob('conf.d/myapp*');
206             $cfg->set_override(font => 'Arial');
207              
208             my $dbname = $cfg->get->{db}{name};
209             my $plain_hashref_conf = $cfg->get;
210             my $dbpassword = $plain_hashref_conf->{db}{password};
211              
212             =head1 DESCRIPTION
213              
214             All too often, configuration is not a universal or one-time thing, yet most
215             configuration-handling treats it as such. Perhaps you can only load one config
216             file. If you can load more than one, you often have to load all of them at the
217             same time or each is stored completely independently, preventing one from being
218             able to override another. Config::Onion changes that.
219              
220             Config::Onion stores all configuration settings in four layers: Defaults,
221             Main, Local, and Override. Each layer can be added to as many times as you
222             like. Within each layer, settings which are given multiple times will take the
223             last specified value, while those which are not repeated will remain untouched.
224              
225             $cfg->set_default(name => 'Arthur Dent', location => 'Earth');
226             $cfg->set_default(location => 'Magrathea');
227             # In the Default layer, 'name' is still 'Arthur Dent', but 'location' has
228             # been changed to 'Magrathea'.
229              
230             Regardless of the order in which they are set, values in Main will always
231             override values in the Default layer, the Local layer always overrides both
232             Default and Main, and the Override layer overrides all the others.
233              
234             The design intent for each layer is:
235              
236             =over 4
237              
238             =item * Default
239              
240             Hardcoded default values to be used when no further configuration is present
241              
242             =item * Main
243              
244             Values loaded from standard configuration files shipped with the application
245              
246             =item * Local
247              
248             Values loaded from local configuration files which are kept separate to prevent
249             them from being overwritten by application upgrades, etc.
250              
251             =item * Override
252              
253             Settings provided at run-time which take precendence over all configuration
254             files, such as settings provided via command line switches
255              
256             =back
257              
258             If a higher-priority layer wishes to completely remove a hash entry made by a
259             lower-priority layer (i.e., delete the hash key, not just set it to an empty
260             value), it can do so by setting the value to "!DELETE!". This only applies to
261             hash entries, not array values, as the entire array already needs to be
262             overwritten to make any changes to it. Also, if, for some reason, the
263             configuration contains objects, the contents of those objects will be ignored
264             for the sake of encapsulation. Only unblessed hashes are cleaned in this
265             manner.
266              
267             =head1 METHODS
268              
269             =head2 new
270              
271             Returns a new, empty configuration object.
272              
273             =head2 load(@file_stems)
274             =head2 load(@file\_stems, {...})
275              
276             Loads files matching the given stems using C<< Config::Any->load_stems >> into
277             the Main layer. Also concatenates ".local" to each stem and loads matching
278             files into the Local layer. e.g., C<< $cfg->load('myapp') >> would load
279             C into Main and C into Local. All filename
280             extensions supported by C are recognized along with their
281             corresponding formats.
282              
283             An optional hash ref final argument can be provided to override the default
284             option C<< use_ext => 1 >> passed to C. All options supported by C
285             are supported except flatten_to_hash. See C<< Config::Any->load_files >>
286             documentation for available options.
287              
288             =head2 load_glob(@globs)
289             =head2 load_glob(@globs, {...})
290              
291             Uses the Perl C function to expand each parameter into a list of
292             filenames and loads each file using C. Files whose names contain
293             the string ".local." are loaded into the Local layer. All other files are
294             loaded into the Main layer.
295              
296             An optional hash ref final argument can be provided to override the default
297             option C<< use_ext => 1 >> passed to C. All options supported by C
298             are supported except flatten_to_hash. See C<< Config::Any->load_files >>
299             documentation for available options.
300              
301             =head2 set_default([\%settings,...,] %settings)
302              
303             =head2 set_override([\%settings,...,] %settings)
304              
305             Imports C<%settings> into the Default or Override layer. Accepts settings both
306             as a plain hash and as hash references, but, if the two are mixed, all hash
307             references must appear at the beginning of the parameter list, before any
308             non-hashref settings.
309              
310             =head1 PROPERTIES
311              
312             =head2 cfg
313              
314             =head2 get
315              
316             Returns the complete configuration as a hash reference.
317              
318             =head2 default
319              
320             =head2 main
321              
322             =head2 local
323              
324             =head2 override
325              
326             These properties each return a single layer of the configuration. This is
327             not likely to be useful other than for debugging. For most other purposes,
328             you probably want to use C instead.
329              
330             =head2 prefix_key
331              
332             If set, enables the Prefix Structures functionality described below when using
333             the C or C methods. The value of C specifies the
334             name of the key under which the prefix structure may be found.
335              
336             Default value is C.
337              
338             =head1 Prefix Structures
339              
340             If you find that your configuration structure is becoming unwieldy due to
341             deeply-nested structures, you can define a file-specific "prefix structure"
342             and all other settings within that file will be loaded as children of the
343             prefix structure. For example, if your main program uses
344              
345             $cfg = Config::Onion->new(prefix_key => '_prefix');
346             $cfg->load("myapp/config");
347              
348             and C contains
349              
350             _prefix:
351             foo:
352             bar:
353              
354             baz: 1
355              
356             then C<$cfg> will contain the configuration
357              
358             foo:
359             bar:
360             baz: 1
361              
362             Note that the top-level C is removed.
363              
364             There are some limitations on the prefix structure, in order to keep it sane
365             and deterministic. First, the prefix structure may only contain hashes.
366             Second, each hash must contain exactly one key. Finally, the value associated
367             with the final key must be left undefined.
368              
369             =head1 BUGS AND LIMITATIONS
370              
371             No bugs have been reported.
372              
373             Please report any bugs or feature requests at
374             L
375              
376             =head1 AUTHOR
377              
378             Dave Sherohman
379              
380             =head1 COPYRIGHT AND LICENSE
381              
382             This software is copyright (c) 2012 by Lund University Library.
383              
384             This is free software; you can redistribute it and/or modify it under
385             the same terms as the Perl 5 programming language system itself.
386              
387             =cut
388              
389             __END__