collapsium-config 0.1.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 +7 -0
- data/.codeclimate.yml +34 -0
- data/.gitignore +39 -0
- data/.rspec +2 -0
- data/.rubocop.yml +75 -0
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +64 -0
- data/LICENSE +30 -0
- data/README.md +35 -0
- data/Rakefile +25 -0
- data/collapsium-config.gemspec +49 -0
- data/lib/collapsium-config.rb +51 -0
- data/lib/collapsium-config/configuration.rb +360 -0
- data/lib/collapsium-config/version.rb +14 -0
- data/spec/configuration_spec.rb +183 -0
- data/spec/data/array.yaml +3 -0
- data/spec/data/driverconfig.yml +26 -0
- data/spec/data/empty.yml +1 -0
- data/spec/data/extend.yml +6 -0
- data/spec/data/global.yml +2 -0
- data/spec/data/hash.yml +3 -0
- data/spec/data/include-extend.yml +7 -0
- data/spec/data/include-multiple.yml +5 -0
- data/spec/data/include-recursive.yml +3 -0
- data/spec/data/include-simple.yml +3 -0
- data/spec/data/include1.yml +2 -0
- data/spec/data/include2.json +3 -0
- data/spec/data/merge-array-local.yaml +2 -0
- data/spec/data/merge-array.yaml +3 -0
- data/spec/data/merge-fail-local.yaml +2 -0
- data/spec/data/merge-fail.yaml +5 -0
- data/spec/data/merge-hash-local.yml +4 -0
- data/spec/data/merge-hash.yml +7 -0
- data/spec/data/recurse.yml +4 -0
- data/spec/data/recursive.yml +3 -0
- data/spec/data/test.json +4 -0
- data/spec/module_spec.rb +25 -0
- data/spec/spec_helper.rb +9 -0
- metadata +207 -0
@@ -0,0 +1,360 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
# collapsium-config
|
4
|
+
# https://github.com/jfinkhaeuser/collapsium-config
|
5
|
+
#
|
6
|
+
# Copyright (c) 2016 Jens Finkhaeuser and other collapsium-config contributors.
|
7
|
+
# All rights reserved.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'collapsium'
|
11
|
+
|
12
|
+
module Collapsium
|
13
|
+
module Config
|
14
|
+
##
|
15
|
+
# The Config class extends UberHash by two main pieces of functionality:
|
16
|
+
#
|
17
|
+
# - it loads configuration files and turns them into pathed hashes, and
|
18
|
+
# - it treats environment variables as overriding anything contained in
|
19
|
+
# the configuration file.
|
20
|
+
#
|
21
|
+
# For configuration file loading, a named configuration file will be laoaded
|
22
|
+
# if present. A file with the same name but `-local` appended before the
|
23
|
+
# extension will be loaded as well, overriding any values in the original
|
24
|
+
# configuration file.
|
25
|
+
#
|
26
|
+
# For environment variable support, any environment variable named like a
|
27
|
+
# path into the configuration hash, but with separators transformed to
|
28
|
+
# underscore and all letters capitalized will override values from the
|
29
|
+
# configuration files under that path, i.e. `FOO_BAR` will override
|
30
|
+
# `'foo.bar'`.
|
31
|
+
#
|
32
|
+
# Environment variables can contain JSON *only*; if the value can be parsed
|
33
|
+
# as JSON, it becomes a Hash in the configuration tree. If it cannot be parsed
|
34
|
+
# as JSON, it remains a string.
|
35
|
+
#
|
36
|
+
# **Note:** if your configuration file's top-level structure is an array, it
|
37
|
+
# will be returned as a hash with a 'config' key that maps to your file's
|
38
|
+
# contents.
|
39
|
+
# That means that if you are trying to merge a hash with an array config, the
|
40
|
+
# result may be unexpected.
|
41
|
+
class Configuration < ::Collapsium::UberHash
|
42
|
+
# @api private
|
43
|
+
# Very simple YAML parser
|
44
|
+
class YAMLParser
|
45
|
+
require 'yaml'
|
46
|
+
|
47
|
+
# @return parsed string
|
48
|
+
def self.parse(string)
|
49
|
+
YAML.load(string)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
private_constant :YAMLParser
|
53
|
+
|
54
|
+
# @api private
|
55
|
+
# Very simple JSON parser
|
56
|
+
class JSONParser
|
57
|
+
require 'json'
|
58
|
+
|
59
|
+
# @return parsed string
|
60
|
+
def self.parse(string)
|
61
|
+
JSON.parse(string)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
private_constant :JSONParser
|
65
|
+
|
66
|
+
UberHash::READ_METHODS.each do |method|
|
67
|
+
# Wrap all read functions into something that checks for environment
|
68
|
+
# variables first.
|
69
|
+
define_method(method) do |*args, &block|
|
70
|
+
# If there are no arguments, there's nothing to do with paths. Just
|
71
|
+
# delegate to the hash.
|
72
|
+
if args.empty?
|
73
|
+
return super(*args, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
# We'll make it rather simple: since the first argument is a key, we
|
77
|
+
# will just transform it to the matching environment variable name,
|
78
|
+
# and see if that environment variable is set.
|
79
|
+
env_name = args[0].to_s.upcase.gsub(split_pattern, '_')
|
80
|
+
contents = nil
|
81
|
+
if env_name != '_'
|
82
|
+
contents = ENV[env_name]
|
83
|
+
end
|
84
|
+
|
85
|
+
# No environment variable set? Fine, just do the usual thing.
|
86
|
+
if contents.nil? or contents.empty?
|
87
|
+
return super(*args, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
# With an environment variable, we will try to parse it as JSON first.
|
91
|
+
begin
|
92
|
+
return JSONParser.parse(contents)
|
93
|
+
rescue JSON::ParserError
|
94
|
+
return contents
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class << self
|
100
|
+
# @api private
|
101
|
+
# Mapping of file name extensions to parser types.
|
102
|
+
FILE_TO_PARSER = {
|
103
|
+
'.yml' => YAMLParser,
|
104
|
+
'.yaml' => YAMLParser,
|
105
|
+
'.json' => JSONParser,
|
106
|
+
}.freeze
|
107
|
+
private_constant :FILE_TO_PARSER
|
108
|
+
|
109
|
+
# @api private
|
110
|
+
# If the config file contains an Array, this is what they key of the
|
111
|
+
# returned Hash will be.
|
112
|
+
ARRAY_KEY = 'config'.freeze
|
113
|
+
private_constant :ARRAY_KEY
|
114
|
+
|
115
|
+
##
|
116
|
+
# Loads a configuration file with the given file name. The format is
|
117
|
+
# detected based on one of the extensions in FILE_TO_PARSER.
|
118
|
+
#
|
119
|
+
# @param path [String] the path of the configuration file to load.
|
120
|
+
# @param resolve_extensions [Boolean] flag whether to resolve configuration
|
121
|
+
# hash extensions. (see `#resolve_extensions`)
|
122
|
+
def load_config(path, resolve_extensions = true)
|
123
|
+
# Load base and local configuration files
|
124
|
+
base, config = load_base_config(path)
|
125
|
+
_, local_config = load_local_config(base)
|
126
|
+
|
127
|
+
# Merge local configuration
|
128
|
+
config.recursive_merge!(local_config)
|
129
|
+
|
130
|
+
# Resolve includes
|
131
|
+
config = resolve_includes(base, config)
|
132
|
+
|
133
|
+
# Create config from the result
|
134
|
+
cfg = Configuration.new(config)
|
135
|
+
|
136
|
+
# Now resolve config hashes that extend other hashes.
|
137
|
+
if resolve_extensions
|
138
|
+
cfg.resolve_extensions!
|
139
|
+
end
|
140
|
+
|
141
|
+
return cfg
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def load_base_config(path)
|
147
|
+
# Make sure the format is recognized early on.
|
148
|
+
base = Pathname.new(path)
|
149
|
+
formats = FILE_TO_PARSER.keys
|
150
|
+
if not formats.include?(base.extname)
|
151
|
+
raise ArgumentError, "Files with extension '#{base.extname}' are not"\
|
152
|
+
" recognized; please use one of #{formats}!"
|
153
|
+
end
|
154
|
+
|
155
|
+
# Don't check the path whether it exists - loading a nonexistent
|
156
|
+
# file will throw a nice error for the user to catch.
|
157
|
+
file = base.open
|
158
|
+
contents = file.read
|
159
|
+
|
160
|
+
# Parse the contents.
|
161
|
+
config = FILE_TO_PARSER[base.extname].parse(contents)
|
162
|
+
|
163
|
+
return base, UberHash.new(hashify(config))
|
164
|
+
end
|
165
|
+
|
166
|
+
def load_local_config(base)
|
167
|
+
# Now construct a file name for a local override.
|
168
|
+
local = Pathname.new(base.dirname)
|
169
|
+
local = local.join(base.basename(base.extname).to_s + "-local" +
|
170
|
+
base.extname)
|
171
|
+
if not local.exist?
|
172
|
+
return local, nil
|
173
|
+
end
|
174
|
+
|
175
|
+
# We know the local override file exists, but we do want to let any
|
176
|
+
# errors go through that come with reading or parsing it.
|
177
|
+
file = local.open
|
178
|
+
contents = file.read
|
179
|
+
|
180
|
+
local_config = FILE_TO_PARSER[base.extname].parse(contents)
|
181
|
+
|
182
|
+
return local, UberHash.new(hashify(local_config))
|
183
|
+
end
|
184
|
+
|
185
|
+
def hashify(data)
|
186
|
+
if data.nil?
|
187
|
+
return {}
|
188
|
+
end
|
189
|
+
if data.is_a? Array
|
190
|
+
data = { ARRAY_KEY => data }
|
191
|
+
end
|
192
|
+
return data
|
193
|
+
end
|
194
|
+
|
195
|
+
def resolve_includes(base, config)
|
196
|
+
processed = []
|
197
|
+
includes = []
|
198
|
+
|
199
|
+
loop do
|
200
|
+
# Figure out includes
|
201
|
+
outer_inc = extract_includes(config)
|
202
|
+
if not outer_inc.empty?
|
203
|
+
includes = outer_inc
|
204
|
+
end
|
205
|
+
|
206
|
+
to_process = includes - processed
|
207
|
+
|
208
|
+
# Stop resolving when all includes have been processed
|
209
|
+
if to_process.empty?
|
210
|
+
break
|
211
|
+
end
|
212
|
+
|
213
|
+
# Load and merge the include files
|
214
|
+
to_process.each do |filename|
|
215
|
+
incfile = Pathname.new(base.dirname)
|
216
|
+
incfile = incfile.join(filename)
|
217
|
+
|
218
|
+
# Just try to open it, if that errors out that's ok.
|
219
|
+
file = incfile.open
|
220
|
+
contents = file.read
|
221
|
+
|
222
|
+
parsed = FILE_TO_PARSER[incfile.extname].parse(contents)
|
223
|
+
|
224
|
+
# Extract and merge includes
|
225
|
+
inner_inc = extract_includes(parsed)
|
226
|
+
includes += inner_inc
|
227
|
+
|
228
|
+
# Merge the rest
|
229
|
+
config.recursive_merge!(UberHash.new(hashify(parsed)))
|
230
|
+
|
231
|
+
processed << filename
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
return config
|
236
|
+
end
|
237
|
+
|
238
|
+
def extract_includes(config)
|
239
|
+
# Figure out includes
|
240
|
+
includes = config.fetch("include", [])
|
241
|
+
config.delete("include")
|
242
|
+
includes = config.fetch(:include, includes)
|
243
|
+
config.delete(:include)
|
244
|
+
|
245
|
+
# We might have a simple/string include
|
246
|
+
if not includes.is_a? Array
|
247
|
+
includes = [includes]
|
248
|
+
end
|
249
|
+
|
250
|
+
return includes
|
251
|
+
end
|
252
|
+
end # class << self
|
253
|
+
|
254
|
+
##
|
255
|
+
# Resolve extensions in configuration hashes. If your hash contains e.g.:
|
256
|
+
#
|
257
|
+
# ```yaml
|
258
|
+
# foo:
|
259
|
+
# bar:
|
260
|
+
# some: value
|
261
|
+
# baz:
|
262
|
+
# extends: bar
|
263
|
+
# ```
|
264
|
+
#
|
265
|
+
# Then `'foo.baz.some'` will equal `'value'` after resolving extensions. Note
|
266
|
+
# that `:load_config` calls this function, so normally you don't need to call
|
267
|
+
# it yourself. You can switch this behaviour off in `:load_config`.
|
268
|
+
#
|
269
|
+
# Note that this process has some intended side-effects:
|
270
|
+
#
|
271
|
+
# 1. If a hash can't be extended because the base cannot be found, an error
|
272
|
+
# is raised.
|
273
|
+
# 1. If a hash got successfully extended, the `extends` keyword itself is
|
274
|
+
# removed from the hash.
|
275
|
+
# 1. In a successfully extended hash, an `base` keyword, which contains
|
276
|
+
# the name of the base. In case of multiple recursive extensions, the
|
277
|
+
# final base is stored here.
|
278
|
+
#
|
279
|
+
# Also note that all of this means that :extends and :base are reserved
|
280
|
+
# keywords that cannot be used in configuration files other than for this
|
281
|
+
# purpose!
|
282
|
+
def resolve_extensions!
|
283
|
+
recursive_merge("", "")
|
284
|
+
end
|
285
|
+
|
286
|
+
private
|
287
|
+
|
288
|
+
def recursive_merge(parent, key)
|
289
|
+
loop do
|
290
|
+
full_key = "#{parent}#{separator}#{key}"
|
291
|
+
|
292
|
+
# Recurse down to the remaining root of the hierarchy
|
293
|
+
base = full_key
|
294
|
+
derived = nil
|
295
|
+
loop do
|
296
|
+
new_base, new_derived = resolve_extension(parent, base)
|
297
|
+
|
298
|
+
if new_derived.nil?
|
299
|
+
break
|
300
|
+
end
|
301
|
+
|
302
|
+
base = new_base
|
303
|
+
derived = new_derived
|
304
|
+
end
|
305
|
+
|
306
|
+
# If recursion found nothing to merge, we're done!
|
307
|
+
if derived.nil?
|
308
|
+
break
|
309
|
+
end
|
310
|
+
|
311
|
+
# Otherwise, merge what needs merging and continue
|
312
|
+
merge_extension(base, derived)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def resolve_extension(grandparent, parent)
|
317
|
+
fetch(parent, {}).each do |key, value|
|
318
|
+
# Recurse into hash values
|
319
|
+
if value.is_a? Hash
|
320
|
+
recursive_merge(parent, key)
|
321
|
+
end
|
322
|
+
|
323
|
+
# No hash, ignore any keys other than the special "extends" key
|
324
|
+
if key != "extends"
|
325
|
+
next
|
326
|
+
end
|
327
|
+
|
328
|
+
# If the key is "extends", return a normalized version of its value.
|
329
|
+
full_value = value.dup
|
330
|
+
if not full_value.start_with?(separator)
|
331
|
+
full_value = "#{grandparent}#{separator}#{value}"
|
332
|
+
end
|
333
|
+
|
334
|
+
if full_value == parent
|
335
|
+
next
|
336
|
+
end
|
337
|
+
return full_value, parent
|
338
|
+
end
|
339
|
+
|
340
|
+
return nil, nil
|
341
|
+
end
|
342
|
+
|
343
|
+
def merge_extension(base, derived)
|
344
|
+
# Remove old 'extends' key, but remember the value
|
345
|
+
extends = self[derived]["extends"]
|
346
|
+
self[derived].delete("extends")
|
347
|
+
|
348
|
+
# Recursively merge base into derived without overwriting
|
349
|
+
self[derived].extend(::Collapsium::RecursiveMerge)
|
350
|
+
self[derived].recursive_merge!(self[base], false)
|
351
|
+
|
352
|
+
# Then set the "base" keyword, but only if it's not yet set.
|
353
|
+
if not self[derived]["base"].nil?
|
354
|
+
return
|
355
|
+
end
|
356
|
+
self[derived]["base"] = extends
|
357
|
+
end
|
358
|
+
end # class Configuration
|
359
|
+
end # module Config
|
360
|
+
end # module Collapsium
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
# collapsium-config
|
4
|
+
# https://github.com/jfinkhaeuser/collapsium-config
|
5
|
+
#
|
6
|
+
# Copyright (c) 2016 Jens Finkhaeuser and other collapsium-config contributors.
|
7
|
+
# All rights reserved.
|
8
|
+
#
|
9
|
+
module Collapsium
|
10
|
+
module Config
|
11
|
+
# The current release version
|
12
|
+
VERSION = "0.1.0".freeze
|
13
|
+
end # module Config
|
14
|
+
end # module Collapsium
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../lib/collapsium-config/configuration'
|
3
|
+
|
4
|
+
describe Collapsium::Config::Configuration do
|
5
|
+
before do
|
6
|
+
@data_path = File.join(File.dirname(__FILE__), 'data')
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "basic file loading" do
|
10
|
+
it "fails to load a nonexistent file" do
|
11
|
+
expect { Collapsium::Config::Configuration.load_config("_nope_.yaml") }.to \
|
12
|
+
raise_error Errno::ENOENT
|
13
|
+
end
|
14
|
+
|
15
|
+
it "is asked to load an unrecognized extension" do
|
16
|
+
expect { Collapsium::Config::Configuration.load_config("_nope_.cfg") }.to \
|
17
|
+
raise_error ArgumentError
|
18
|
+
end
|
19
|
+
|
20
|
+
it "loads a yaml config with a top-level hash correctly" do
|
21
|
+
config = File.join(@data_path, 'hash.yml')
|
22
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
23
|
+
|
24
|
+
expect(cfg["foo"]).to eql "bar"
|
25
|
+
expect(cfg["baz"]).to eql "quux"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "loads a yaml config with a top-level array correctly" do
|
29
|
+
config = File.join(@data_path, 'array.yaml')
|
30
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
31
|
+
|
32
|
+
expect(cfg["config"]).to eql %w(foo bar)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "loads a JSON config correctly" do
|
36
|
+
config = File.join(@data_path, 'test.json')
|
37
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
38
|
+
|
39
|
+
expect(cfg["foo"]).to eql "bar"
|
40
|
+
expect(cfg["baz"]).to eql 42
|
41
|
+
end
|
42
|
+
|
43
|
+
it "treats an empty YAML file as an empty hash" do
|
44
|
+
config = File.join(@data_path, 'empty.yml')
|
45
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
46
|
+
expect(cfg).to be_empty
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "merge behaviour" do
|
51
|
+
it "merges a hashed config correctly" do
|
52
|
+
config = File.join(@data_path, 'merge-hash.yml')
|
53
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
54
|
+
|
55
|
+
expect(cfg["asdf"]).to eql 1
|
56
|
+
expect(cfg["foo.bar"]).to eql "baz"
|
57
|
+
expect(cfg["foo.quux"]).to eql [1, 42]
|
58
|
+
expect(cfg["foo.baz"]).to eql 3.14
|
59
|
+
expect(cfg["blargh"]).to eql false
|
60
|
+
end
|
61
|
+
|
62
|
+
it "merges an array config correctly" do
|
63
|
+
config = File.join(@data_path, 'merge-array.yaml')
|
64
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
65
|
+
|
66
|
+
expect(cfg["config"]).to eql %w(foo bar baz)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "merges an array and hash config" do
|
70
|
+
config = File.join(@data_path, 'merge-fail.yaml')
|
71
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
72
|
+
|
73
|
+
expect(cfg["config"]).to eql %w(array in main config)
|
74
|
+
expect(cfg["local"]).to eql "override is a hash"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "overrides configuration variables from the environment" do
|
78
|
+
config = File.join(@data_path, 'hash.yml')
|
79
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
80
|
+
|
81
|
+
ENV["BAZ"] = "override"
|
82
|
+
expect(cfg["foo"]).to eql "bar"
|
83
|
+
expect(cfg["baz"]).to eql "override"
|
84
|
+
ENV.delete("BAZ")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "extend functionality" do
|
89
|
+
it "extends configuration hashes" do
|
90
|
+
config = File.join(@data_path, 'driverconfig.yml')
|
91
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
92
|
+
|
93
|
+
# First, test for non-extended values
|
94
|
+
expect(cfg["drivers.mock.mockoption"]).to eql 42
|
95
|
+
expect(cfg["drivers.branch1.branch1option"]).to eql "foo"
|
96
|
+
expect(cfg["drivers.branch2.branch2option"]).to eql "bar"
|
97
|
+
expect(cfg["drivers.leaf.leafoption"]).to eql "baz"
|
98
|
+
|
99
|
+
# Now test extended values
|
100
|
+
expect(cfg["drivers.branch1.mockoption"]).to eql 42
|
101
|
+
expect(cfg["drivers.branch2.mockoption"]).to eql 42
|
102
|
+
expect(cfg["drivers.leaf.mockoption"]).to eql 42
|
103
|
+
|
104
|
+
expect(cfg["drivers.branch2.branch1option"]).to eql "foo"
|
105
|
+
expect(cfg["drivers.leaf.branch1option"]).to eql "override" # not "foo" !
|
106
|
+
|
107
|
+
expect(cfg["drivers.leaf.branch2option"]).to eql "bar"
|
108
|
+
|
109
|
+
# Also test that all levels go back to base == mock
|
110
|
+
expect(cfg["drivers.branch1.base"]).to eql 'mock'
|
111
|
+
expect(cfg["drivers.branch2.base"]).to eql 'mock'
|
112
|
+
expect(cfg["drivers.leaf.base"]).to eql 'mock'
|
113
|
+
end
|
114
|
+
|
115
|
+
it "extends configuration hashes when the base does not exist" do
|
116
|
+
config = File.join(@data_path, 'driverconfig.yml')
|
117
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
118
|
+
|
119
|
+
# Ensure the hash contains its own value
|
120
|
+
expect(cfg["drivers.base_does_not_exist.some"]).to eql "value"
|
121
|
+
|
122
|
+
# Also ensure the "base" is set properly
|
123
|
+
expect(cfg["drivers.base_does_not_exist.base"]).to eql "nonexistent_base"
|
124
|
+
end
|
125
|
+
|
126
|
+
it "does nothing when a hash extends itself" do
|
127
|
+
config = File.join(@data_path, 'recurse.yml')
|
128
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
129
|
+
|
130
|
+
# Most of the test is already over, i.e. we haven't run into recursion
|
131
|
+
# issues.
|
132
|
+
expect(cfg["extends_itself.test"]).to eql 42
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "include functionality" do
|
137
|
+
it "can include a file" do
|
138
|
+
config = File.join(@data_path, 'include-simple.yml')
|
139
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
140
|
+
|
141
|
+
expect(cfg["foo"]).to eql 42
|
142
|
+
expect(cfg["bar"]).to eql 'quux'
|
143
|
+
end
|
144
|
+
|
145
|
+
it "can include multiple files in different languages" do
|
146
|
+
config = File.join(@data_path, 'include-multiple.yml')
|
147
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
148
|
+
|
149
|
+
expect(cfg["foo"]).to eql 42
|
150
|
+
expect(cfg["bar"]).to eql 'quux'
|
151
|
+
expect(cfg["baz"]).to eql 'test'
|
152
|
+
end
|
153
|
+
|
154
|
+
it "can resolve includes recursively" do
|
155
|
+
config = File.join(@data_path, 'include-recursive.yml')
|
156
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
157
|
+
|
158
|
+
expect(cfg["foo"]).to eql 42
|
159
|
+
expect(cfg["bar"]).to eql 'quux'
|
160
|
+
expect(cfg["baz"]).to eql 'test'
|
161
|
+
end
|
162
|
+
|
163
|
+
it "extends configuration from across includes" do
|
164
|
+
config = File.join(@data_path, 'include-extend.yml')
|
165
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
166
|
+
|
167
|
+
expect(cfg["foo.bar"]).to eql 'quux'
|
168
|
+
expect(cfg["foo.baz"]).to eql 'test'
|
169
|
+
expect(cfg["bar.foo"]).to eql 'something'
|
170
|
+
expect(cfg["bar.baz"]).to eql 42
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "behaves like a UberHash" do
|
175
|
+
it "passed through access methods" do
|
176
|
+
config = File.join(@data_path, 'hash.yml')
|
177
|
+
cfg = Collapsium::Config::Configuration.load_config(config)
|
178
|
+
|
179
|
+
# UberHash's [] requires one argument
|
180
|
+
expect { cfg[] }.to raise_error(ArgumentError)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|