layeredyamlconfig 1.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/History.txt +36 -0
- data/LICENSE.txt +21 -0
- data/Manifest.txt +46 -0
- data/README.txt +247 -0
- data/Rakefile +18 -0
- data/layeredyamlconfig.gemspec +17 -0
- data/lib/layeredyamlconfig.rb +2 -0
- data/lib/layeredyamlconfig/array_traverse.rb +19 -0
- data/lib/layeredyamlconfig/core.rb +103 -0
- data/lib/layeredyamlconfig/hash_traverse.rb +20 -0
- data/lib/layeredyamlconfig/templates.rb +64 -0
- data/test/ex1.yaml +2 -0
- data/test/ex10.yaml +3 -0
- data/test/ex11.yaml +4 -0
- data/test/ex12.yaml +4 -0
- data/test/ex13.yaml +5 -0
- data/test/ex14.yaml +4 -0
- data/test/ex15.yaml +4 -0
- data/test/ex16.yaml +8 -0
- data/test/ex17.yaml +5 -0
- data/test/ex18.yaml +23 -0
- data/test/ex2.yaml +2 -0
- data/test/ex3.yaml +2 -0
- data/test/ex4.yaml +2 -0
- data/test/ex5.yaml +6 -0
- data/test/ex6.yaml +9 -0
- data/test/ex7.yaml +3 -0
- data/test/ex8.yaml +3 -0
- data/test/ex9.yaml +3 -0
- data/test/exbad1.yaml +1 -0
- data/test/exbad2.yaml +2 -0
- data/test/minitest_helper.rb +12 -0
- data/test/test_addlayer.rb +22 -0
- data/test/test_clear.rb +48 -0
- data/test/test_comments.rb +26 -0
- data/test/test_constructor.rb +50 -0
- data/test/test_deepmerge.rb +19 -0
- data/test/test_erb.rb +24 -0
- data/test/test_erb_array.rb +40 -0
- data/test/test_erb_empty.rb +23 -0
- data/test/test_erb_hash.rb +21 -0
- data/test/test_erb_multi.rb +34 -0
- data/test/test_files.rb +22 -0
- data/test/test_invalid.rb +38 -0
- data/test/test_multi.rb +54 -0
- data/test/test_tohash.rb +18 -0
- metadata +156 -0
data/.gemtest
ADDED
File without changes
|
data/History.txt
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# LayeredYAMLConfig Change History
|
2
|
+
|
3
|
+
## 1.4.3
|
4
|
+
|
5
|
+
* first release on rubygems.org
|
6
|
+
* bring test suite to 100% coverage
|
7
|
+
|
8
|
+
## 1.4.2
|
9
|
+
|
10
|
+
* remove soft breakpoints. I make this mistake so often I wrote a Perl test module to catch them: https://metacpan.org/release/Test-NoBreakpoints. Time for a Ruby version methinks.
|
11
|
+
|
12
|
+
## 1.4.1
|
13
|
+
|
14
|
+
* replace dependency on 'erbuis' (ERB's sophisticated European cousin) with 'erubis'
|
15
|
+
|
16
|
+
## 1.4.0
|
17
|
+
|
18
|
+
* added ::reset and ::reset_all methods to reset per-class options to their default
|
19
|
+
* added ERB Template evaluation of leaf nodes
|
20
|
+
|
21
|
+
## 1.3.0
|
22
|
+
|
23
|
+
* Added #to_hash
|
24
|
+
|
25
|
+
## 1.2.0
|
26
|
+
|
27
|
+
* added #files read accessor to get a list of files the config was constructed from
|
28
|
+
|
29
|
+
## 1.1.0
|
30
|
+
|
31
|
+
* Changes to symbolized keys
|
32
|
+
* We now return an object of our class and delegate key lookup to the underlying hash
|
33
|
+
|
34
|
+
## 1.0.0
|
35
|
+
|
36
|
+
* Initial Release
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2012, 2013 James FitzGibbon <james@nadt.net>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to
|
7
|
+
deal in the Software without restriction, including without limitation the
|
8
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
9
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
LICENSE.txt
|
5
|
+
Rakefile
|
6
|
+
layeredyamlconfig.gemspec
|
7
|
+
lib/layeredyamlconfig.rb
|
8
|
+
lib/layeredyamlconfig/array_traverse.rb
|
9
|
+
lib/layeredyamlconfig/core.rb
|
10
|
+
lib/layeredyamlconfig/hash_traverse.rb
|
11
|
+
lib/layeredyamlconfig/templates.rb
|
12
|
+
test/minitest_helper.rb
|
13
|
+
test/ex1.yaml
|
14
|
+
test/ex2.yaml
|
15
|
+
test/ex3.yaml
|
16
|
+
test/ex4.yaml
|
17
|
+
test/ex5.yaml
|
18
|
+
test/ex6.yaml
|
19
|
+
test/ex7.yaml
|
20
|
+
test/ex8.yaml
|
21
|
+
test/ex9.yaml
|
22
|
+
test/ex10.yaml
|
23
|
+
test/ex11.yaml
|
24
|
+
test/ex12.yaml
|
25
|
+
test/ex13.yaml
|
26
|
+
test/ex14.yaml
|
27
|
+
test/ex15.yaml
|
28
|
+
test/ex16.yaml
|
29
|
+
test/ex17.yaml
|
30
|
+
test/ex18.yaml
|
31
|
+
test/exbad1.yaml
|
32
|
+
test/exbad2.yaml
|
33
|
+
test/test_addlayer.rb
|
34
|
+
test/test_clear.rb
|
35
|
+
test/test_comments.rb
|
36
|
+
test/test_constructor.rb
|
37
|
+
test/test_deepmerge.rb
|
38
|
+
test/test_erb_array.rb
|
39
|
+
test/test_erb_hash.rb
|
40
|
+
test/test_erb_empty.rb
|
41
|
+
test/test_erb_multi.rb
|
42
|
+
test/test_erb.rb
|
43
|
+
test/test_files.rb
|
44
|
+
test/test_invalid.rb
|
45
|
+
test/test_multi.rb
|
46
|
+
test/test_tohash.rb
|
data/README.txt
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
# LayeredYAMLConfig
|
2
|
+
|
3
|
+
home :: https://github.com/jf647/LayeredYAMLConfig
|
4
|
+
|
5
|
+
## SUMMARY:
|
6
|
+
|
7
|
+
Ruby configuration library that layers multiple YAML files on top of each
|
8
|
+
other with ERB evalution.
|
9
|
+
|
10
|
+
## DESCRIPTION:
|
11
|
+
|
12
|
+
LayeredYAMLConfig provides a simple config file that supports multiple
|
13
|
+
layers. Values in the right or uppermost layers override values in lower
|
14
|
+
layers. This makes it easy to share configuration without duplication while
|
15
|
+
still allowing what needs to be different to vary.
|
16
|
+
|
17
|
+
For example:
|
18
|
+
|
19
|
+
program.default.conf
|
20
|
+
program.server_foo.conf
|
21
|
+
program.site_bar.conf
|
22
|
+
program.conf
|
23
|
+
|
24
|
+
Optionally, leaf nodes can be evaluated using as ERB templates, feeding the
|
25
|
+
configuration into itself.
|
26
|
+
|
27
|
+
## Synopsis
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class MyConfig < LayeredYAMLConfig
|
31
|
+
end
|
32
|
+
|
33
|
+
MyConfig.skipbad = true
|
34
|
+
cfg = MyConfig.instance( 'ex7.yaml', 'ex8.yaml', 'ex9.yaml', 'ex10.yaml' )
|
35
|
+
puts cfg[:foo]['bar']
|
36
|
+
puts cfg['foo'][:gzonk]
|
37
|
+
```
|
38
|
+
|
39
|
+
ex7.yaml:
|
40
|
+
```yaml
|
41
|
+
---
|
42
|
+
foo:
|
43
|
+
bar: baz
|
44
|
+
```
|
45
|
+
|
46
|
+
ex8.yaml:
|
47
|
+
```yaml
|
48
|
+
---
|
49
|
+
foo:
|
50
|
+
gzonk: quux
|
51
|
+
```
|
52
|
+
|
53
|
+
ex10.yaml:
|
54
|
+
```text
|
55
|
+
This is not a YAML file
|
56
|
+
```
|
57
|
+
|
58
|
+
ex16.yaml:
|
59
|
+
```text
|
60
|
+
---
|
61
|
+
a: d
|
62
|
+
b: e
|
63
|
+
c: f
|
64
|
+
g:
|
65
|
+
- <%= @cfg[:a] %>
|
66
|
+
- <%= @cfg[:b] %>
|
67
|
+
- <%= @cfg[:c] %>
|
68
|
+
```
|
69
|
+
|
70
|
+
To use LayeredYAMLConfig, create a new class that inherits from it. The new
|
71
|
+
class is a singleton that can only be constructed the first time ::instance
|
72
|
+
is called. Pass one or more YAML filenames which will be read and deep
|
73
|
+
merged on top of each other in left-to-right order.
|
74
|
+
|
75
|
+
Files that are missing are skipped by default. Files that are bad (i.e. do
|
76
|
+
not parse as valid YAML) cause an exception to be thrown. This behaviour
|
77
|
+
can be overridden by calling ::skipbad = true or ::skipmissing = true before
|
78
|
+
constructing the configuration.
|
79
|
+
|
80
|
+
The type of the returned object is your subordinate class, but #[] and #[]=
|
81
|
+
are delegated to the contained hash, which is an
|
82
|
+
ActiveSupport::HashWithIndifferentAccess. This means you can use strings or
|
83
|
+
symbols interchangeably to access elements of the hash.
|
84
|
+
|
85
|
+
## Adding Layers after Instance Construction
|
86
|
+
|
87
|
+
Using #add, you can add one or more layers that are deep merged into the
|
88
|
+
existing config.
|
89
|
+
|
90
|
+
## Converting to a Hash
|
91
|
+
|
92
|
+
Call #to_hash to return a symbolized hash representation of the
|
93
|
+
configuration object
|
94
|
+
|
95
|
+
## ERB Templates
|
96
|
+
|
97
|
+
To enable template evaluation, call ::templates = true on the class:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
class OurConfig < LayeredYAMLConfig
|
101
|
+
end
|
102
|
+
OurConfig.templates = true
|
103
|
+
OurConfig.instance('file_containing_erb.yaml')
|
104
|
+
```
|
105
|
+
|
106
|
+
Every time #add is called (either directly or implicitly during creation),
|
107
|
+
the configuration tree is traversed. Any String leaf nodes are evaluated
|
108
|
+
using Erubis. The only context variable available is @cfg, which
|
109
|
+
representes the configuration at the start of the template pass.
|
110
|
+
|
111
|
+
The process of template evaluation is as follows:
|
112
|
+
|
113
|
+
1. Walk the tree, keeping track of how successful and failed template evaluations we performed
|
114
|
+
2. If there were no failures, the pass is complete
|
115
|
+
3. If there were no successes and the previous pass (if any) had at least one success, keep going
|
116
|
+
4. If there were no successes and the previous pass also had no successes, raise an exception
|
117
|
+
5. Keep going
|
118
|
+
|
119
|
+
Requiring two passes with no successful template evaluations allows files in
|
120
|
+
upper layers to depend on values defined in lower layers. For example,
|
121
|
+
given these files:
|
122
|
+
|
123
|
+
```text
|
124
|
+
---
|
125
|
+
a:
|
126
|
+
b:
|
127
|
+
c: <%= @cfg[:d][:e][:f] %>
|
128
|
+
|
129
|
+
---
|
130
|
+
d:
|
131
|
+
e:
|
132
|
+
f: <%= @cfg[:g][:h][:i] %>
|
133
|
+
|
134
|
+
---
|
135
|
+
g:
|
136
|
+
h:
|
137
|
+
i: j
|
138
|
+
```
|
139
|
+
|
140
|
+
It would take two passes to fully resolve all templates. In the first pass,
|
141
|
+
cfg[:d][:e][:f] would resolve to 'j', and in the second pass,
|
142
|
+
cfg[:a][:b][:c] would also resolve to 'j'.
|
143
|
+
|
144
|
+
### Empty Strings
|
145
|
+
|
146
|
+
If a template evaluates to the empty string, this is by default considered a
|
147
|
+
failure. To treat empty strings as success, call ::emptyok before
|
148
|
+
constructing the configuration:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
class OurConfig < LayeredYAMLConfig
|
152
|
+
end
|
153
|
+
OurConfig.templates = true
|
154
|
+
OurConfig.emptyok = true
|
155
|
+
OurConfig.instance('file_containing_erb.yaml')
|
156
|
+
```
|
157
|
+
|
158
|
+
Beware of hidden gotchas though: if an intermediate node is undefined, ERB
|
159
|
+
will throw an exception trying to deference nil. But if only the last node
|
160
|
+
is defined, then ERB will generate an empty string:
|
161
|
+
|
162
|
+
```text
|
163
|
+
---
|
164
|
+
a:
|
165
|
+
b:
|
166
|
+
c: d
|
167
|
+
e: <%= @cfg[:a][:b][:f] %>
|
168
|
+
g: <%= @cfg[:a][:h][:i] %>
|
169
|
+
```
|
170
|
+
|
171
|
+
In the default mode, neither template resolves successfully. With ::emptyok
|
172
|
+
enabled, cfg[:a][:b][:e] becomes the empty string.
|
173
|
+
|
174
|
+
### What can be expanded
|
175
|
+
|
176
|
+
The result of an Erubis expansion is always a string. Therefore, you can't
|
177
|
+
expand a Hash or Array and then expect it to dereference properly. This,
|
178
|
+
for example, won't work:
|
179
|
+
|
180
|
+
```text
|
181
|
+
a:
|
182
|
+
one: 1
|
183
|
+
two: 2
|
184
|
+
three: 3
|
185
|
+
b: <%= @cfg[:a] %>
|
186
|
+
```
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
puts cfg[:b][:one]
|
190
|
+
```
|
191
|
+
|
192
|
+
The expansion of cfg[:b] isn't the same as cfg[:a], it's the same as
|
193
|
+
cfg[:a].to_s.
|
194
|
+
|
195
|
+
You can however dereference through a Hash or Array to a scalar leaf:
|
196
|
+
|
197
|
+
```text
|
198
|
+
a:
|
199
|
+
one: 1
|
200
|
+
two: 2
|
201
|
+
three: 3
|
202
|
+
b: <%= @cfg[:a][:one] %>
|
203
|
+
```
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
puts cfg[:b]
|
207
|
+
```
|
208
|
+
|
209
|
+
## Resetting Per-Class Options
|
210
|
+
|
211
|
+
call ::reset on your class to reset the ::skipbad, ::skipmissing,
|
212
|
+
::templates and ::emptyok settings to their defaults. Call ::reset_all on
|
213
|
+
the base class to reset these options to default for all derived classes.
|
214
|
+
|
215
|
+
## LICENSE:
|
216
|
+
|
217
|
+
The MIT License (MIT)
|
218
|
+
|
219
|
+
Copyright (c) 2012, 2013 James FitzGibbon <james@nadt.net>
|
220
|
+
|
221
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
222
|
+
of this software and associated documentation files (the "Software"), to
|
223
|
+
deal in the Software without restriction, including without limitation the
|
224
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
225
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
226
|
+
furnished to do so, subject to the following conditions:
|
227
|
+
|
228
|
+
The above copyright notice and this permission notice shall be included in
|
229
|
+
all copies or substantial portions of the Software.
|
230
|
+
|
231
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
232
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
233
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
234
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
235
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
236
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
237
|
+
IN THE SOFTWARE.
|
238
|
+
|
239
|
+
## Contributing to LayeredYAMLConfig
|
240
|
+
|
241
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
242
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
243
|
+
* Fork the project
|
244
|
+
* Start a feature/bugfix branch
|
245
|
+
* Commit and push until you are happy with your contribution
|
246
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
247
|
+
* Please try not to mess with the version or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'hoe'
|
3
|
+
|
4
|
+
Hoe.spec 'layeredyamlconfig' do
|
5
|
+
developer("James FitzGibbon", "james@nadt.net")
|
6
|
+
license "MIT"
|
7
|
+
end
|
8
|
+
|
9
|
+
task :default => [:unit_tests]
|
10
|
+
|
11
|
+
desc "Run basic tests"
|
12
|
+
Rake::TestTask.new("unit_tests") { |t|
|
13
|
+
t.libs.push 'lib'
|
14
|
+
t.libs.push 'test'
|
15
|
+
t.pattern = 'test/test_*.rb'
|
16
|
+
t.verbose = true
|
17
|
+
t.warning = true
|
18
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require 'layeredyamlconfig'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'layeredyamlconfig'
|
6
|
+
s.version = ThreadedLogger::VERSION
|
7
|
+
s.summary = 'YAML Configs with multiple layers and ERB evaluation'
|
8
|
+
s.add_dependency( 'psych', '>= 1.3.4' )
|
9
|
+
s.add_dependency( 'activesupport', '>= 3.2.12' )
|
10
|
+
s.add_dependency( 'hash-deep-merge' )
|
11
|
+
s.add_dependency( 'erubis' )
|
12
|
+
s.authors = ['James FitzGibbon']
|
13
|
+
s.email = ['james@nadt.net']
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f)
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# based on Hash traverse from Ruby Facets (https://github.com/rubyworks/facets)
|
2
|
+
class Array
|
3
|
+
|
4
|
+
def traverse(&blk)
|
5
|
+
na = []
|
6
|
+
inject(na) do |a, e|
|
7
|
+
case e
|
8
|
+
when Hash
|
9
|
+
e = e.traverse(&blk)
|
10
|
+
when Array
|
11
|
+
e = e.traverse(&blk)
|
12
|
+
end
|
13
|
+
ne = blk.call(e)
|
14
|
+
a.push ne
|
15
|
+
a
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'hash_deep_merge'
|
4
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
5
|
+
|
6
|
+
class LayeredYAMLConfig
|
7
|
+
|
8
|
+
VERSION = '1.4.3'
|
9
|
+
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
private_class_method :new
|
13
|
+
attr_reader :files
|
14
|
+
def_delegators :@cfg, :[], :[]=, :to_hash
|
15
|
+
|
16
|
+
def self.instance(*files)
|
17
|
+
if @@instances[self].nil?
|
18
|
+
if ! files.empty?
|
19
|
+
@@instances[self] = new(files)
|
20
|
+
else
|
21
|
+
raise ArgumentError, "no files in initial construction of #{self}"
|
22
|
+
end
|
23
|
+
else
|
24
|
+
if ! files.empty?
|
25
|
+
raise ArgumentError, "instance for #{self} already constructed"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
return @@instances[self]
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.clear
|
32
|
+
@@instances[self] = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.clear_all
|
36
|
+
@@instances = Hash.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.reset
|
40
|
+
%w(skipbad skipmissing templates emptyok).each do |opt|
|
41
|
+
@@opts[opt.to_sym].delete(self)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.reset_all
|
46
|
+
@@opts = {
|
47
|
+
:skipbad => Hash.new(false),
|
48
|
+
:skipmissing => Hash.new(true),
|
49
|
+
:templates => Hash.new(false),
|
50
|
+
:emptyok => Hash.new(false),
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# create class-level option accessors
|
55
|
+
%w(skipbad skipmissing templates emptyok).each do |opt|
|
56
|
+
self.define_singleton_method(opt.to_sym) do
|
57
|
+
@@opts[opt.to_sym][self]
|
58
|
+
end
|
59
|
+
self.define_singleton_method("#{opt}=".to_sym) do |newval|
|
60
|
+
@@opts[opt.to_sym][self] = !!newval
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# add files to an instance
|
65
|
+
def add(*files)
|
66
|
+
# due to the multi passing of splat args, we can get Array-in-Array situations here
|
67
|
+
files.flatten.each do |fn|
|
68
|
+
@files.push(fn)
|
69
|
+
if ! File.exists?(fn)
|
70
|
+
next if self.class.skipmissing
|
71
|
+
raise ArgumentError, "file #{fn} does not exist"
|
72
|
+
end
|
73
|
+
begin
|
74
|
+
data = YAML.load(File.open(fn))
|
75
|
+
if ! data.instance_of?(Hash)
|
76
|
+
raise ArgumentError, "file #{fn} does not contain a Hash"
|
77
|
+
end
|
78
|
+
@cfg.deep_merge!(data.deep_symbolize_keys).deep_symbolize_keys
|
79
|
+
rescue
|
80
|
+
if ! self.class.skipbad
|
81
|
+
raise
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# resolve templates
|
87
|
+
if self.class.templates
|
88
|
+
resolve_templates
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# constructor
|
93
|
+
def initialize(*files)
|
94
|
+
@cfg = Hash.new.with_indifferent_access
|
95
|
+
@files = []
|
96
|
+
add(files)
|
97
|
+
end
|
98
|
+
|
99
|
+
# create catalog of per-subclass instances and options
|
100
|
+
self.clear_all
|
101
|
+
self.reset_all
|
102
|
+
|
103
|
+
end
|