collapsium-config 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +10 -10
- data/README.md +73 -2
- data/collapsium-config.gemspec +5 -3
- data/lib/collapsium-config/configuration.rb +67 -53
- data/lib/collapsium-config/version.rb +1 -1
- data/spec/configuration_spec.rb +24 -5
- data/spec/data/driverconfig.yml +21 -0
- data/spec/data/extend.yml +2 -0
- data/spec/data/include-extend.yml +2 -0
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f59285701d071aae2421be7c500cf4b814c346d
|
4
|
+
data.tar.gz: efe7bc99ba90a5b7a0508047a7e61ff5618eede9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 051281284616335b1776dc9001f07d5da473f3ce6873c3d477eb2b8c74c62396d6b57f30943c288123fbe76e149a5f50b63b83957bc30946b700ed1f584fce67
|
7
|
+
data.tar.gz: 26a6b17ffcb4af8aed8368adaf8f9686800f61dded3e3d1566b121ccf3b7e3618af8fbc94ecedcfb5fff22d2e05f6e0bd2730975cbf246913dcfc261c8820649
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
collapsium-config (0.
|
5
|
-
collapsium (~> 0.
|
4
|
+
collapsium-config (0.3.0)
|
5
|
+
collapsium (~> 0.5)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
@@ -10,20 +10,20 @@ GEM
|
|
10
10
|
ast (2.3.0)
|
11
11
|
codeclimate-test-reporter (0.6.0)
|
12
12
|
simplecov (>= 0.7.1, < 1.0.0)
|
13
|
-
collapsium (0.
|
13
|
+
collapsium (0.5.0)
|
14
14
|
diff-lcs (1.2.5)
|
15
15
|
docile (1.1.5)
|
16
16
|
json (2.0.2)
|
17
|
-
parser (2.3.1.
|
17
|
+
parser (2.3.1.4)
|
18
18
|
ast (~> 2.2)
|
19
19
|
powerpack (0.1.1)
|
20
20
|
rainbow (2.1.0)
|
21
|
-
rake (11.
|
21
|
+
rake (11.3.0)
|
22
22
|
rspec (3.5.0)
|
23
23
|
rspec-core (~> 3.5.0)
|
24
24
|
rspec-expectations (~> 3.5.0)
|
25
25
|
rspec-mocks (~> 3.5.0)
|
26
|
-
rspec-core (3.5.
|
26
|
+
rspec-core (3.5.4)
|
27
27
|
rspec-support (~> 3.5.0)
|
28
28
|
rspec-expectations (3.5.0)
|
29
29
|
diff-lcs (>= 1.2.0, < 2.0)
|
@@ -32,7 +32,7 @@ GEM
|
|
32
32
|
diff-lcs (>= 1.2.0, < 2.0)
|
33
33
|
rspec-support (~> 3.5.0)
|
34
34
|
rspec-support (3.5.0)
|
35
|
-
rubocop (0.
|
35
|
+
rubocop (0.44.1)
|
36
36
|
parser (>= 2.3.1.1, < 3.0)
|
37
37
|
powerpack (~> 0.1)
|
38
38
|
rainbow (>= 1.99.1, < 3.0)
|
@@ -44,7 +44,7 @@ GEM
|
|
44
44
|
json (>= 1.8, < 3)
|
45
45
|
simplecov-html (~> 0.10.0)
|
46
46
|
simplecov-html (0.10.0)
|
47
|
-
unicode-display_width (1.1.
|
47
|
+
unicode-display_width (1.1.1)
|
48
48
|
yard (0.9.5)
|
49
49
|
|
50
50
|
PLATFORMS
|
@@ -54,9 +54,9 @@ DEPENDENCIES
|
|
54
54
|
bundler (~> 1.12)
|
55
55
|
codeclimate-test-reporter
|
56
56
|
collapsium-config!
|
57
|
-
rake (~> 11.
|
57
|
+
rake (~> 11.3)
|
58
58
|
rspec (~> 3.5)
|
59
|
-
rubocop (~> 0.
|
59
|
+
rubocop (~> 0.44)
|
60
60
|
simplecov (~> 0.12)
|
61
61
|
yard (~> 0.9)
|
62
62
|
|
data/README.md
CHANGED
@@ -18,11 +18,13 @@ various configuration sources into one configuration object.
|
|
18
18
|
if that exists, and merges it's contents recursively into the main
|
19
19
|
configuration.
|
20
20
|
- Using the special `extends` configuration key, allows a configuration Hash
|
21
|
-
to include all values from
|
21
|
+
to include all values from other configuration Hash(es).
|
22
22
|
- Using the special, top-level `include` configuration key, allows a
|
23
23
|
configuration file to be split into multiple included files.
|
24
|
+
- As of `v0.2`, configuration files are [ERB templates](http://ruby-doc.org/stdlib-2.3.1/libdoc/erb/rdoc/ERB.html).
|
25
|
+
Do your templating stuff as you'd usually do it.
|
24
26
|
|
25
|
-
# Usage
|
27
|
+
# Basic Usage
|
26
28
|
|
27
29
|
While you can use the `Configuration` class yourself, the simplest usage is to
|
28
30
|
access a global configuration object:
|
@@ -33,3 +35,72 @@ include Collapsium::Config
|
|
33
35
|
|
34
36
|
puts config["foo"] # loaded automatically from config.yml
|
35
37
|
```
|
38
|
+
|
39
|
+
# Advanced Usage
|
40
|
+
|
41
|
+
## Configuration File Location
|
42
|
+
|
43
|
+
The friendly neighbour to the `#config` function introduced in the basic
|
44
|
+
usage section above is the `#config_file` accessor. Its value will default
|
45
|
+
to `config.yml`, but you can set it to something different, too:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
config_file = 'foo.yaml'
|
49
|
+
puts config["foo"] # loaded automatically from foo.yaml
|
50
|
+
```
|
51
|
+
|
52
|
+
## Loading Configuration Files
|
53
|
+
|
54
|
+
All that `#config` and `#config_file` do is wrap `#load_config` such that
|
55
|
+
configuration is loaded only once. You can load configuration files manually,
|
56
|
+
too:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
my_config = Collapsium::Config::Configuration.load_config('filename.yaml')
|
60
|
+
```
|
61
|
+
|
62
|
+
## Extension
|
63
|
+
|
64
|
+
Given the following configuration file:
|
65
|
+
|
66
|
+
```yaml
|
67
|
+
base:
|
68
|
+
foo: 42
|
69
|
+
|
70
|
+
derived:
|
71
|
+
bar: value
|
72
|
+
extends: .base
|
73
|
+
```
|
74
|
+
|
75
|
+
Then the special `extends` keyword is interpreted to merge all values from
|
76
|
+
the value at path `.base` into the value at path `.derived`. Additionally,
|
77
|
+
`.derived` will gain a new key `base` which is an Array containing all the
|
78
|
+
bases merged into the value.
|
79
|
+
|
80
|
+
- Absolute paths are preferred for values of `extends`.
|
81
|
+
- Relative paths for values of `extends` are looked up in the parent of the
|
82
|
+
value that contains the `extends` keyword, i.e. the root in the example
|
83
|
+
above. So in this minimal example, specifying `.base` and `base` is
|
84
|
+
equivalent.
|
85
|
+
- You can specify a comma-separated list of bases in the `extends` keyword.
|
86
|
+
Latter paths overwrite values in earlier paths.
|
87
|
+
|
88
|
+
## Templating
|
89
|
+
|
90
|
+
ERB templating in configuration files works out-of-the-box, but one of the
|
91
|
+
more powerful features is of course to substitute some values in the template
|
92
|
+
which your loading code already knows. If you're using `#load_config`, you
|
93
|
+
can do that with the `data` keyword parameter:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
my_data_hash = {}
|
97
|
+
my_config = Configuration.load_config('foo.yaml', data: my_data_hash)
|
98
|
+
```
|
99
|
+
|
100
|
+
Note that the template has access to the entire hash under the `data` name,
|
101
|
+
not to its individual keys:
|
102
|
+
|
103
|
+
```erb
|
104
|
+
<%= data[:some_key] %> # correct usage
|
105
|
+
<%= some_key %> # incorrect usage
|
106
|
+
```
|
data/collapsium-config.gemspec
CHANGED
@@ -13,6 +13,7 @@ require 'collapsium-config/version'
|
|
13
13
|
|
14
14
|
# rubocop:disable Style/UnneededPercentQ, Style/ExtraSpacing
|
15
15
|
# rubocop:disable Style/SpaceAroundOperators
|
16
|
+
# rubocop:disable Metrics/BlockLength
|
16
17
|
Gem::Specification.new do |spec|
|
17
18
|
spec.name = "collapsium-config"
|
18
19
|
spec.version = Collapsium::Config::VERSION
|
@@ -37,13 +38,14 @@ Gem::Specification.new do |spec|
|
|
37
38
|
spec.required_ruby_version = '>= 2.0'
|
38
39
|
|
39
40
|
spec.add_development_dependency "bundler", "~> 1.12"
|
40
|
-
spec.add_development_dependency "rubocop", "~> 0.
|
41
|
-
spec.add_development_dependency "rake", "~> 11.
|
41
|
+
spec.add_development_dependency "rubocop", "~> 0.44"
|
42
|
+
spec.add_development_dependency "rake", "~> 11.3"
|
42
43
|
spec.add_development_dependency "rspec", "~> 3.5"
|
43
44
|
spec.add_development_dependency "simplecov", "~> 0.12"
|
44
45
|
spec.add_development_dependency "yard", "~> 0.9"
|
45
46
|
|
46
|
-
spec.add_dependency 'collapsium', '~> 0.
|
47
|
+
spec.add_dependency 'collapsium', '~> 0.5'
|
47
48
|
end
|
49
|
+
# rubocop:enable Metrics/BlockLength
|
48
50
|
# rubocop:enable Style/SpaceAroundOperators
|
49
51
|
# rubocop:enable Style/UnneededPercentQ, Style/ExtraSpacing
|
@@ -65,6 +65,10 @@ module Collapsium
|
|
65
65
|
end
|
66
66
|
private_constant :JSONParser
|
67
67
|
|
68
|
+
def initialize(*args)
|
69
|
+
super(*args)
|
70
|
+
end
|
71
|
+
|
68
72
|
class << self
|
69
73
|
# @api private
|
70
74
|
# Mapping of file name extensions to parser types.
|
@@ -267,80 +271,90 @@ module Collapsium
|
|
267
271
|
# keywords that cannot be used in configuration files other than for this
|
268
272
|
# purpose!
|
269
273
|
def resolve_extensions!
|
270
|
-
|
274
|
+
# The root object is always a Hash, so has keys, which can be processed
|
275
|
+
# recursively.
|
276
|
+
recursive_resolve(self)
|
271
277
|
end
|
272
278
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
279
|
+
def recursive_resolve(root, prefix = "")
|
280
|
+
# The self object is a Hash or an Array. Let's iterate over its children
|
281
|
+
# one by one. Defaulting to a Hash here is just convenience, it could
|
282
|
+
# equally be an Array.
|
283
|
+
children = root.fetch(prefix, {})
|
278
284
|
|
279
|
-
|
280
|
-
base = full_key
|
281
|
-
derived = nil
|
282
|
-
loop do
|
283
|
-
new_base, new_derived = resolve_extension(parent, base)
|
285
|
+
merge_base(root, prefix, children)
|
284
286
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
base = new_base
|
290
|
-
derived = new_derived
|
287
|
+
if children.is_a? Hash
|
288
|
+
children.each do |key, _|
|
289
|
+
full_key = normalize_path("#{prefix}#{separator}#{key}")
|
290
|
+
recursive_resolve(root, full_key)
|
291
291
|
end
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
292
|
+
elsif children.is_a? Array
|
293
|
+
children.each_with_index do |_, idx|
|
294
|
+
key = idx.to_s
|
295
|
+
full_key = normalize_path("#{prefix}#{separator}#{key}")
|
296
|
+
recursive_resolve(root, full_key)
|
296
297
|
end
|
297
|
-
|
298
|
-
# Otherwise, merge what needs merging and continue
|
299
|
-
merge_extension(base, derived)
|
300
298
|
end
|
301
299
|
end
|
302
300
|
|
303
|
-
def
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
end
|
301
|
+
def merge_base(root, path, value)
|
302
|
+
# If the value is not a Hash, we can't do anything here.
|
303
|
+
if not value.is_a? Hash
|
304
|
+
return
|
305
|
+
end
|
309
306
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
307
|
+
# If the value contains an "extends" keyword, we can find the value's
|
308
|
+
# base. Otherwise there's nothing to do.
|
309
|
+
if not value.include? "extends"
|
310
|
+
return
|
311
|
+
end
|
314
312
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
313
|
+
# Now to resolve the path to the base and remove the "extends" keyword.
|
314
|
+
base_paths = value["extends"]
|
315
|
+
base_paths = base_paths.split(/,/).map(&:strip)
|
316
|
+
bases = {}
|
317
|
+
base_paths.each do |base_path|
|
318
|
+
if not base_path.start_with?(separator)
|
319
|
+
parent = parent_path(path)
|
320
|
+
base_path = "#{parent}#{separator}#{base_path}"
|
319
321
|
end
|
322
|
+
base_path = normalize_path(base_path)
|
320
323
|
|
321
|
-
|
324
|
+
# Fetch the base value from the root. This makes full use of
|
325
|
+
# PathedAccess.
|
326
|
+
# We default to nil. Only Hash base values can be processed.
|
327
|
+
base_value = root.fetch(base_path, nil)
|
328
|
+
if not base_value.is_a? Hash
|
322
329
|
next
|
323
330
|
end
|
324
|
-
|
331
|
+
|
332
|
+
bases[base_path] = base_value
|
325
333
|
end
|
326
334
|
|
327
|
-
|
328
|
-
|
335
|
+
# Only delete the "extends" keyword if we found all base.
|
336
|
+
if bases.length == base_paths.length
|
337
|
+
value.delete("extends")
|
338
|
+
end
|
329
339
|
|
330
|
-
|
331
|
-
#
|
332
|
-
|
333
|
-
|
340
|
+
# We need to recursively resolve the base values before merging them into
|
341
|
+
# value. To preserve the override order, we need to overwrite values when
|
342
|
+
# merging bases...
|
343
|
+
merged_base = Configuration.new
|
344
|
+
bases.each do |base_path, base_value|
|
345
|
+
base_value.recursive_resolve(root, base_path)
|
346
|
+
merged_base.recursive_merge!(base_value, true)
|
347
|
+
end
|
334
348
|
|
335
|
-
#
|
336
|
-
|
337
|
-
self[derived].recursive_merge!(self[base], false)
|
349
|
+
# ... but value needs to stay authoritative.
|
350
|
+
value.recursive_merge!(merged_base, false)
|
338
351
|
|
339
|
-
#
|
340
|
-
if
|
341
|
-
|
352
|
+
# Set the base if all is well.
|
353
|
+
if value["base"].nil? and not bases.keys.empty?
|
354
|
+
value["base"] = bases.keys
|
342
355
|
end
|
343
|
-
|
356
|
+
|
357
|
+
root[path] = value
|
344
358
|
end
|
345
359
|
end # class Configuration
|
346
360
|
end # module Config
|
data/spec/configuration_spec.rb
CHANGED
@@ -117,9 +117,25 @@ describe Collapsium::Config::Configuration do
|
|
117
117
|
expect(cfg["drivers.leaf.branch2option"]).to eql "bar"
|
118
118
|
|
119
119
|
# Also test that all levels go back to base == mock
|
120
|
-
expect(cfg["drivers.branch1.base"]).to eql
|
121
|
-
expect(cfg["drivers.branch2.base"]).to eql
|
122
|
-
expect(cfg["drivers.leaf.base"]).to eql
|
120
|
+
expect(cfg["drivers.branch1.base"]).to eql %w(.drivers.mock)
|
121
|
+
expect(cfg["drivers.branch2.base"]).to eql %w(.drivers.mock)
|
122
|
+
expect(cfg["drivers.leaf.base"]).to eql %w(.drivers.mock)
|
123
|
+
|
124
|
+
# We expect that 'derived' is extended with '.other.base', too
|
125
|
+
expect(cfg["derived.test.foo"]).to eql 'bar'
|
126
|
+
expect(cfg["derived.test.some"]).to eql 'option'
|
127
|
+
|
128
|
+
# Expect this to also work with list items
|
129
|
+
expect(cfg["derived.test2.0.foo"]).to eql 'bar'
|
130
|
+
expect(cfg["derived.test2.0.some"]).to eql 'option'
|
131
|
+
|
132
|
+
expect(cfg["derived.test3.foo"]).to eql 'bar'
|
133
|
+
expect(cfg["derived.test3.some2"]).to eql 'option2'
|
134
|
+
|
135
|
+
# Expect this to work with multiple inheritance
|
136
|
+
expect(cfg["derived.test4.foo"]).to eql 'bar'
|
137
|
+
expect(cfg["derived.test4.some"]).to eql 'option_override'
|
138
|
+
expect(cfg["derived.test4.some2"]).to eql 'option2'
|
123
139
|
end
|
124
140
|
|
125
141
|
it "extends configuration hashes when the base does not exist" do
|
@@ -129,8 +145,11 @@ describe Collapsium::Config::Configuration do
|
|
129
145
|
# Ensure the hash contains its own value
|
130
146
|
expect(cfg["drivers.base_does_not_exist.some"]).to eql "value"
|
131
147
|
|
132
|
-
# Also ensure the "base" is set properly
|
133
|
-
expect(cfg["drivers.base_does_not_exist.base"]).to
|
148
|
+
# Also ensure the "base" is _not_ set properly
|
149
|
+
expect(cfg["drivers.base_does_not_exist.base"]).to be_nil
|
150
|
+
|
151
|
+
# On the other hand, "extends" should stay.
|
152
|
+
expect(cfg["drivers.base_does_not_exist.extends"]).to eql "nonexistent_base"
|
134
153
|
end
|
135
154
|
|
136
155
|
it "does nothing when a hash extends itself" do
|
data/spec/data/driverconfig.yml
CHANGED
@@ -24,3 +24,24 @@ drivers:
|
|
24
24
|
extends: nonexistent_base
|
25
25
|
some: value
|
26
26
|
driver: leaf
|
27
|
+
|
28
|
+
other:
|
29
|
+
testbase:
|
30
|
+
some: option
|
31
|
+
testbase2:
|
32
|
+
- some2: option2
|
33
|
+
some: option_override
|
34
|
+
|
35
|
+
derived:
|
36
|
+
test:
|
37
|
+
foo: bar
|
38
|
+
extends: .other.testbase
|
39
|
+
test2:
|
40
|
+
- foo: bar
|
41
|
+
extends: .other.testbase
|
42
|
+
test3:
|
43
|
+
foo: bar
|
44
|
+
extends: .other.testbase2.0
|
45
|
+
test4:
|
46
|
+
foo: bar
|
47
|
+
extends: .other.testbase, .other.testbase2.0
|
data/spec/data/extend.yml
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: collapsium-config
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jens Finkhaeuser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
33
|
+
version: '0.44'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0.
|
40
|
+
version: '0.44'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '11.
|
47
|
+
version: '11.3'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '11.
|
54
|
+
version: '11.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0.
|
103
|
+
version: '0.5'
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0.
|
110
|
+
version: '0.5'
|
111
111
|
description: "\n Using collapsium's UberHash class for easy access to configuration
|
112
112
|
values,\n this gem reads and merges various configuration sources into one\n
|
113
113
|
\ configuration object.\n "
|