collapsium-config 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 "
|