File Coverage

blib/lib/Config/Onion.pm
Criterion Covered Total %
statement 89 89 100.0
branch 21 26 80.7
condition 2 3 66.6
subroutine 14 14 100.0
pod 5 5 100.0
total 131 137 95.6


line stmt bran cond sub pod time code
1             package Config::Onion;
2              
3 5     5   77267 use strict;
  5         8  
  5         243  
4 5     5   26 use warnings;
  5         9  
  5         235  
5              
6             our $VERSION = 1.004;
7              
8 5     5   6760 use Config::Any;
  5         61148  
  5         207  
9 5     5   4976 use Hash::Merge::Simple 'merge';
  5         2442  
  5         333  
10 5     5   5946 use Moo;
  5         119971  
  5         36  
11              
12             has cfg => ( is => 'lazy', clearer => '_reset_cfg' );
13             has prefix_key => ( is => 'rw' );
14 30     30 1 9656 sub get { goto &cfg }
15              
16             has [qw( default main local override )]
17             => ( is => 'rwp', default => sub { {} } );
18              
19             sub set_default {
20 8     8 1 3912 my $self = shift;
21 8 100       131 $self = $self->new unless ref $self;
22              
23 8         42 my $default = $self->default;
24 8         106 $default = merge $default, shift while ref $_[0] eq 'HASH';
25 8 100       120 $default = merge $default, { @_ } if @_;
26              
27 8         205 $self->_set_default($default);
28 8         171 $self->_reset_cfg;
29 8         873 return $self;
30             }
31              
32             sub set_override {
33 1     1 1 927 my $self = shift;
34 1 50       5 $self = $self->new unless ref $self;
35              
36 1         3 my $override = $self->override;
37 1         24 $override = merge $override, shift while ref $_[0] eq 'HASH';
38 1 50       8 $override = merge $override, { @_ } if @_;
39              
40 1         23 $self->_set_override($override);
41 1         24 $self->_reset_cfg;
42 1         6 return $self;
43             }
44              
45             sub load {
46 8     8 1 4591 my $self = shift;
47 8 100       156 $self = $self->new unless ref $self;
48              
49 8         52 my %ca_opts = $self->_ca_opts;
50 8         95 my $main = Config::Any->load_stems({ stems => \@_ , %ca_opts });
51 8         218182 my $local = Config::Any->load_stems({ stems => [ map { "$_.local" } @_ ],
  9         142  
52             %ca_opts });
53              
54 8         93088 $self->_add_loaded($main, $local);
55 8         640 return $self;
56             }
57              
58             sub load_glob {
59 2     2 1 1756 my $self = shift;
60 2 50       70 $self = $self->new unless ref $self;
61              
62 2         14 my (@main_files, @local_files);
63 2         6 for my $globspec (@_) {
64 2         268 for (glob $globspec) {
65 5 100       23 if (/\.local\./) { push @local_files, $_ }
  1         3  
66 4         13 else { push @main_files, $_ }
67             }
68             }
69              
70 2         11 my %ca_opts = $self->_ca_opts;
71 2         25 my $main = Config::Any->load_files({ files => \@main_files, %ca_opts });
72 2         23282 my $local = Config::Any->load_files({ files => \@local_files, %ca_opts });
73              
74 2         13270 $self->_add_loaded($main, $local);
75              
76 2         35 return $self;
77             }
78              
79             sub _add_loaded {
80 10     10   28 my $self = shift;
81 10         23 my ($main, $local) = @_;
82              
83 10 100       21 my @main; @main = map { values %$_ } @$main if @$main;
  10         52  
  12         52  
84 10 100       21 my @local; @local = map { values %$_ } @$local if @$local;
  10         88  
  4         15  
85              
86 10 100       94 if ($self->prefix_key) {
87 1         4 for my $cfg (@main, @local) {
88 1 50       12 $self->_replace_prefix_key($cfg) if exists $cfg->{$self->prefix_key};
89             }
90             }
91              
92 10         137 $self->_set_main( merge $self->main, @main);
93 10         688 $self->_set_local(merge $self->local, @local);
94              
95 10         481 $self->_reset_cfg;
96             }
97              
98             sub _build_cfg {
99 19     19   2119 my $self = shift;
100 19         152 merge $self->default, $self->main, $self->local, $self->override;
101             }
102              
103 10     10   48 sub _ca_opts { ( use_ext => 1 ) }
104              
105             sub _replace_prefix_key {
106 1     1   4 my $self = shift;
107 1         4 my $cfg = shift;
108              
109 1         2 my $top_key;
110 1         9 my $root = $cfg->{$self->prefix_key};
111 1         2 while (1) {
112 3 50       11 die "Config::Onion prefix key structure may not branch" if keys %$root > 1;
113 3   66     15 $top_key ||= (keys %$root)[0];
114 3         8 my $child = (values %$root)[0];
115 3 100       7 unless ($child) {
116 1         3 my $key = (keys %$root)[0];
117 1         3 $root = $root->{$key} = {};
118 1         3 last;
119             }
120 2         5 $root = $child;
121             }
122              
123 1         6 my $new = $cfg->{$self->prefix_key}{$top_key};
124 1         6 delete $cfg->{$self->prefix_key};
125              
126 1         4 for (keys %$cfg) {
127 2         6 $root->{$_} = $cfg->{$_};
128 2         5 delete $cfg->{$_};
129             }
130 1         6 $cfg->{$top_key} = $new;
131             }
132              
133             1;
134              
135             =pod
136              
137             =head1 NAME
138              
139             Config::Onion - Layered configuration, because configs are like ogres
140              
141             =head1 VERSION
142              
143             version 1.004
144              
145             =head1 SYNOPSIS
146              
147             my $cfg = Config::Onion->new;
148             my $cfg = Config::Onion->set_default(db => {name => 'foo', password => 'bar'});
149             my $cfg = Config::Onion->load('/etc/myapp', './myapp');
150             my $cfg = Config::Onion->load_glob('./plugins/*');
151              
152             $cfg->set_default(font => 'Comic Sans');
153             $cfg->load('config');
154             $cfg->load_glob('conf.d/myapp*');
155             $cfg->set_override(font => 'Arial');
156              
157             my $dbname = $cfg->get->{db}{name};
158             my $plain_hashref_conf = $cfg->get;
159             my $dbpassword = $plain_hashref_conf->{db}{password};
160              
161             =head1 DESCRIPTION
162              
163             All too often, configuration is not a universal or one-time thing, yet most
164             configuration-handling treats it as such. Perhaps you can only load one config
165             file. If you can load more than one, you often have to load all of them at the
166             same time or each is stored completely independently, preventing one from being
167             able to override another. Config::Onion changes that.
168              
169             Config::Onion stores all configuration settings in four layers: Defaults,
170             Main, Local, and Override. Each layer can be added to as many times as you
171             like. Within each layer, settings which are given multiple times will take the
172             last specified value, while those which are not repeated will remain untouched.
173              
174             $cfg->set_default(name => 'Arthur Dent', location => 'Earth');
175             $cfg->set_default(location => 'Magrathea');
176             # In the Default layer, 'name' is still 'Arthur Dent', but 'location' has
177             # been changed to 'Magrathea'.
178              
179             Regardless of the order in which they are set, values in Main will always
180             override values in the Default layer, the Local layer always overrides both
181             Default and Main, and the Override layer overrides all the others.
182              
183             The design intent for each layer is:
184              
185             =over 4
186              
187             =item * Default
188              
189             Hardcoded default values to be used when no further configuration is present
190              
191             =item * Main
192              
193             Values loaded from standard configuration files shipped with the application
194              
195             =item * Local
196              
197             Values loaded from local configuration files which are kept separate to prevent
198             them from being overwritten by application upgrades, etc.
199              
200             =item * Override
201              
202             Settings provided at run-time which take precendence over all configuration
203             files, such as settings provided via command line switches
204              
205             =back
206              
207             =head1 METHODS
208              
209             =head2 new
210              
211             Returns a new, empty configuration object.
212              
213             =head2 load(@file_stems)
214              
215             Loads files matching the given stems using C<< Config::Any->load_stems >> into
216             the Main layer. Also concatenates ".local" to each stem and loads matching
217             files into the Local layer. e.g., C<< $cfg->load('myapp') >> would load
218             C into Main and C into Local. All filename
219             extensions supported by C are recognized along with their
220             corresponding formats.
221              
222             =head2 load_glob(@globs)
223              
224             Uses the Perl C function to expand each parameter into a list of
225             filenames and loads each file using C. Files whose names contain
226             the string ".local." are loaded into the Local layer. All other files are
227             loaded into the Main layer.
228              
229             =head2 set_default([\%settings,...,] %settings)
230              
231             =head2 set_override([\%settings,...,] %settings)
232              
233             Imports C<%settings> into the Default or Override layer. Accepts settings both
234             as a plain hash and as hash references, but, if the two are mixed, all hash
235             references must appear at the beginning of the parameter list, before any
236             non-hashref settings.
237              
238             =head1 PROPERTIES
239              
240             =head2 cfg
241              
242             =head2 get
243              
244             Returns the complete configuration as a hash reference.
245              
246             =head2 default
247              
248             =head2 main
249              
250             =head2 local
251              
252             =head2 override
253              
254             These properties each return a single layer of the configuration. This is
255             not likely to be useful other than for debugging. For most other purposes,
256             you probably want to use C instead.
257              
258             =head2 prefix_key
259              
260             If set, enables the Prefix Structures functionality described below when using
261             the C or C methods. The value of C specifies the
262             name of the key under which the prefix structure may be found.
263              
264             Default value is C.
265              
266             =head1 Prefix Structures
267              
268             If you find that your configuration structure is becoming unwieldy due to
269             deeply-nested structures, you can define a file-specific "prefix structure"
270             and all other settings within that file will be loaded as children of the
271             prefix structure. For example, if your main program uses
272              
273             $cfg = Config::Onion->new(prefix_key => '_prefix');
274             $cfg->load("myapp/config");
275              
276             and C contains
277              
278             _prefix:
279             foo:
280             bar:
281              
282             baz: 1
283              
284             then C<$cfg> will contain the configuration
285              
286             foo:
287             bar:
288             baz: 1
289              
290             Note that the top-level C is removed.
291              
292             There are some limitations on the prefix structure, in order to keep it sane
293             and deterministic. First, the prefix structure may only contain hashes.
294             Second, each hash must contain exactly one key. Finally, the value associated
295             with the final key must be left undefined.
296              
297             =head1 BUGS AND LIMITATIONS
298              
299             No bugs have been reported.
300              
301             Please report any bugs or feature requests at
302             L
303              
304             =head1 AUTHOR
305              
306             Dave Sherohman
307              
308             =head1 COPYRIGHT AND LICENSE
309              
310             This software is copyright (c) 2014 by Lund University Library.
311              
312             This is free software; you can redistribute it and/or modify it under
313             the same terms as the Perl 5 programming language system itself.
314              
315             =cut
316              
317             __END__