collapsium-config 0.4.3 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7226c990a104075ae8b906ac940c962db5bc24a4
4
- data.tar.gz: 5f0e566d08dc54452653f9155455b41bc350a838
3
+ metadata.gz: 31e518d37d4cdaa12f8a8718c3ca5816939b6ab2
4
+ data.tar.gz: 3d466b1b37734a90beafa962991fd9735734ea39
5
5
  SHA512:
6
- metadata.gz: 94ce0f061cfe720f4d02b37eb28d39c2cff34da9711c934792096f53997114139d651cb34a98035f8c0978874a12c62282104986c8d3e53366d746608e58f414
7
- data.tar.gz: 8a47d3515b417eee8497f2924417dd034d92ad5c0b30574270ddc23f6b1c7263f732d5ebc6794b09061b98de3cb51b0acab46ff9584d8c430a5bf02f6a4d104d
6
+ metadata.gz: 7052b7b696d6b7cb5422fa2e6236656de330ffc28ecf08fdb0db52593b36a5fab2d219333ef8ea0c55b3ae83c7a18704d0c7fb998c77caece40e8ebac69fd94a
7
+ data.tar.gz: 85bddfd097bce75d0285c01e1f1da0dfc612f127be4bfc769481669e75df7425a6e67ecd9b11f551378da7edef29cd944a8ca38928a49cfd2da4305023145fc1
data/.travis.yml CHANGED
@@ -5,7 +5,7 @@ rvm:
5
5
  - 2.2
6
6
  script:
7
7
  - bundle exec rake
8
- - bundle exec codeclimate-test-reporter
8
+ - bundle exec codeclimate-test-reporter || true
9
9
  addons:
10
10
  code_climate:
11
11
  repo_token: 3cca4d10a51839bc5d115d41a0c12b98cbdb27a21e9c86544324fa028b799b84
data/Gemfile.lock CHANGED
@@ -1,23 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- collapsium-config (0.4.3)
5
- collapsium (~> 0.7)
4
+ collapsium-config (0.5.0)
5
+ collapsium (~> 0.8)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  ast (2.3.0)
11
- codeclimate-test-reporter (1.0.3)
11
+ codeclimate-test-reporter (1.0.4)
12
12
  simplecov
13
- collapsium (0.7.0)
13
+ collapsium (0.8.2)
14
14
  diff-lcs (1.2.5)
15
15
  docile (1.1.5)
16
16
  json (2.0.2)
17
- parser (2.3.2.0)
17
+ parser (2.3.3.1)
18
18
  ast (~> 2.2)
19
19
  powerpack (0.1.1)
20
- rainbow (2.1.0)
20
+ rainbow (2.2.1)
21
21
  rake (11.3.0)
22
22
  rspec (3.5.0)
23
23
  rspec-core (~> 3.5.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.45.0)
35
+ rubocop (0.46.0)
36
36
  parser (>= 2.3.1.1, < 3.0)
37
37
  powerpack (~> 0.1)
38
38
  rainbow (>= 1.99.1, < 3.0)
@@ -44,8 +44,8 @@ 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.1)
48
- yard (0.9.5)
47
+ unicode-display_width (1.1.2)
48
+ yard (0.9.6)
49
49
 
50
50
  PLATFORMS
51
51
  ruby
@@ -56,7 +56,7 @@ DEPENDENCIES
56
56
  collapsium-config!
57
57
  rake (~> 11.3)
58
58
  rspec (~> 3.5)
59
- rubocop (~> 0.45)
59
+ rubocop (~> 0.46)
60
60
  simplecov (~> 0.12)
61
61
  yard (~> 0.9)
62
62
 
@@ -38,13 +38,13 @@ Gem::Specification.new do |spec|
38
38
  spec.required_ruby_version = '>= 2.0'
39
39
 
40
40
  spec.add_development_dependency "bundler", "~> 1.12"
41
- spec.add_development_dependency "rubocop", "~> 0.45"
41
+ spec.add_development_dependency "rubocop", "~> 0.46"
42
42
  spec.add_development_dependency "rake", "~> 11.3"
43
43
  spec.add_development_dependency "rspec", "~> 3.5"
44
44
  spec.add_development_dependency "simplecov", "~> 0.12"
45
45
  spec.add_development_dependency "yard", "~> 0.9"
46
46
 
47
- spec.add_dependency 'collapsium', '~> 0.7'
47
+ spec.add_dependency 'collapsium', '~> 0.8'
48
48
  end
49
49
  # rubocop:enable Metrics/BlockLength
50
50
  # rubocop:enable Style/SpaceAroundOperators
@@ -18,6 +18,13 @@ module Collapsium
18
18
  # The default configuration file path
19
19
  DEFAULT_CONFIG_PATH = 'config.yml'.freeze
20
20
 
21
+ # Default options for configuration loading
22
+ DEFAULT_CONFIG_OPTIONS = {
23
+ resolve_extensions: true,
24
+ nonexistent_base: :ignore,
25
+ data: nil,
26
+ }.freeze
27
+
21
28
  ##
22
29
  # Modules can have class methods, too, but it's a little more verbose to
23
30
  # provide them.
@@ -32,6 +39,16 @@ module Collapsium
32
39
  return @config_file || DEFAULT_CONFIG_PATH
33
40
  end
34
41
 
42
+ # Set configuration loading options
43
+ def config_options=(opts)
44
+ @config_options = opts
45
+ end
46
+
47
+ # @return [Hash] configuration loading options
48
+ def config_options
49
+ return @config_options || DEFAULT_CONFIG_OPTIONS
50
+ end
51
+
35
52
  # @api private
36
53
  attr_accessor :config
37
54
  end # module ClassMethods
@@ -42,7 +59,8 @@ module Collapsium
42
59
  def config
43
60
  if Config.config.nil? or Config.config.empty?
44
61
  begin
45
- Config.config = Configuration.load_config(Config.config_file)
62
+ Config.config = Configuration.load_config(Config.config_file,
63
+ Config.config_options)
46
64
  rescue Errno::ENOENT
47
65
  Config.config = {}
48
66
  end
@@ -98,13 +98,31 @@ module Collapsium
98
98
  # @param options [Hash] options hash with the following keys:
99
99
  # - resolve_extensions [Boolean] flag whether to resolve configuration
100
100
  # hash extensions. (see `#resolve_extensions`)
101
+ # - nonexistent_base [Symbol] either of :extend or :ignore; flag that
102
+ # determines how a nonexistent base should be treated:
103
+ # - :extend behaves as if it does exist, but refers to an empty Hash
104
+ # - :ignore does not modify the `.base` or `.extends` properties,
105
+ # and simply ignores the reference.
101
106
  # - data [Hash] data Hash to pass on to the templating mechanism.
102
107
  def load_config(path, options = {})
103
108
  # Option defaults
109
+ options = options.dup
104
110
  if options[:resolve_extensions].nil?
105
111
  options[:resolve_extensions] = true
106
112
  end
113
+ if not [true, false].include?(options[:resolve_extensions])
114
+ raise "The :resolve_extensions option must be a boolean value!"
115
+ end
116
+ if options[:nonexistent_base].nil?
117
+ options[:nonexistent_base] = :ignore
118
+ end
119
+ if not [:extend, :ignore].include?(options[:nonexistent_base])
120
+ raise "The :nonexistent_base option must be one of :ignore or :extend!"
121
+ end
107
122
  options[:data] ||= {}
123
+ if not options[:data].is_a? Hash
124
+ raise "The :data option must be a Hash!"
125
+ end
108
126
 
109
127
  # Load base and local configuration files
110
128
  base, config = load_base_config(path, options[:data])
@@ -121,7 +139,7 @@ module Collapsium
121
139
 
122
140
  # Now resolve config hashes that extend other hashes.
123
141
  if options[:resolve_extensions]
124
- cfg.resolve_extensions!
142
+ cfg.resolve_extensions!(options)
125
143
  end
126
144
 
127
145
  return cfg
@@ -251,35 +269,35 @@ module Collapsium
251
269
  # Also note that all of this means that :extends and :base are reserved
252
270
  # keywords that cannot be used in configuration files other than for this
253
271
  # purpose!
254
- def resolve_extensions!
272
+ def resolve_extensions!(options)
255
273
  # The root object is always a Hash, so has keys, which can be processed
256
274
  # recursively.
257
- recursive_resolve(self)
275
+ recursive_resolve(self, "", options)
258
276
  end
259
277
 
260
- def recursive_resolve(root, prefix = "")
278
+ def recursive_resolve(root, prefix = "", options = {})
261
279
  # The self object is a Hash or an Array. Let's iterate over its children
262
280
  # one by one. Defaulting to a Hash here is just convenience, it could
263
281
  # equally be an Array.
264
282
  children = root.fetch(prefix, {})
265
283
 
266
- merge_base(root, prefix, children)
284
+ merge_base(root, prefix, children, options)
267
285
 
268
286
  if children.is_a? Hash
269
287
  children.each do |key, _|
270
288
  full_key = normalize_path("#{prefix}#{separator}#{key}")
271
- recursive_resolve(root, full_key)
289
+ recursive_resolve(root, full_key, options)
272
290
  end
273
291
  elsif children.is_a? Array
274
292
  children.each_with_index do |_, idx|
275
293
  key = idx.to_s
276
294
  full_key = normalize_path("#{prefix}#{separator}#{key}")
277
- recursive_resolve(root, full_key)
295
+ recursive_resolve(root, full_key, options)
278
296
  end
279
297
  end
280
298
  end
281
299
 
282
- def merge_base(root, path, value)
300
+ def merge_base(root, path, value, options)
283
301
  # If the value is not a Hash, we can't do anything here.
284
302
  if not value.is_a? Hash
285
303
  return
@@ -292,18 +310,19 @@ module Collapsium
292
310
  end
293
311
 
294
312
  # Now to resolve the path to the base and remove the "extends" keyword.
295
- bases = fetch_base_values(root, parent_path(path), value)
313
+ bases = fetch_base_values(root, parent_path(path), value, options)
296
314
 
297
315
  # Merge the bases
298
- merge_base_values(root, value, bases)
316
+ merge_base_values(root, value, bases, options)
299
317
 
300
318
  # And we're done, set the value to what was being merged.
301
319
  root[path] = value
302
320
  end
303
321
 
304
- def fetch_base_values(root, parent, value)
322
+ def fetch_base_values(root, parent, value, options)
305
323
  base_paths = array_value(value["extends"])
306
324
  bases = []
325
+ base_count = 0
307
326
  base_paths.each do |base_path|
308
327
  if not base_path.start_with?(separator)
309
328
  base_path = "#{parent}#{separator}#{base_path}"
@@ -315,30 +334,38 @@ module Collapsium
315
334
  # We default to nil. Only Hash base values can be processed.
316
335
  base_value = root.fetch(base_path, nil)
317
336
  if not base_value.is_a? Hash
337
+ bases << [base_path, nil]
318
338
  next
319
339
  end
320
340
 
321
341
  bases << [base_path, base_value]
342
+ base_count += 1
322
343
  end
323
344
 
324
345
  # Only delete the "extends" keyword if we found all base.
325
- if bases.length == base_paths.length
346
+ if options[:nonexistent_base] == :extend or \
347
+ base_count == base_paths.length
326
348
  value.delete("extends")
327
349
  end
328
350
 
329
351
  return bases
330
352
  end
331
353
 
332
- def merge_base_values(root, value, bases)
354
+ def merge_base_values(root, value, bases, options)
333
355
  # We need to recursively resolve the base values before merging them into
334
356
  # value. To preserve the override order, we need to overwrite values when
335
357
  # merging bases...
336
358
  merged_base = Configuration.new
337
359
  bases.each do |base_path, base_value|
338
- base_value.recursive_resolve(root, base_path)
339
- merged_base.recursive_merge!(base_value, true)
360
+ if not base_value.nil?
361
+ base_value.recursive_resolve(root, base_path)
362
+ merged_base.recursive_merge!(base_value, true)
363
+ end
340
364
 
341
365
  # Modify bases for this path: we go depth first into the hierarchy
366
+ if options[:nonexistent_base] == :ignore and base_value.nil?
367
+ next
368
+ end
342
369
  base_val = merged_base.fetch("base", []).dup
343
370
  base_val << base_path
344
371
  base_val.uniq!
@@ -9,6 +9,6 @@
9
9
  module Collapsium
10
10
  module Config
11
11
  # The current release version
12
- VERSION = "0.4.3".freeze
12
+ VERSION = "0.5.0".freeze
13
13
  end # module Config
14
14
  end # module Collapsium
@@ -49,6 +49,25 @@ describe Collapsium::Config::Configuration do
49
49
  cfg = Collapsium::Config::Configuration.load_config(config)
50
50
  expect(cfg).to be_empty
51
51
  end
52
+
53
+ it "validates options" do
54
+ config = File.join(@data_path, 'hash.yml')
55
+
56
+ # :resolve_extensions must be a boolean
57
+ opts = { resolve_extensions: 42 }
58
+ expect { Collapsium::Config::Configuration.load_config(config, opts) }.to \
59
+ raise_error RuntimeError
60
+
61
+ # :data must be a Hash
62
+ opts = { data: 42 }
63
+ expect { Collapsium::Config::Configuration.load_config(config, opts) }.to \
64
+ raise_error RuntimeError
65
+
66
+ # :nonexistent_base must be one of :ignore, :extend
67
+ opts = { nonexistent_base: :foo }
68
+ expect { Collapsium::Config::Configuration.load_config(config, opts) }.to \
69
+ raise_error RuntimeError
70
+ end
52
71
  end
53
72
 
54
73
  describe "merge behaviour" do
@@ -175,16 +194,34 @@ describe Collapsium::Config::Configuration do
175
194
  .drivers.branch2)
176
195
  end
177
196
 
178
- it "works when a base does not exist" do
179
- # Ensure the hash contains its own value
180
- expect(@config["drivers.base_does_not_exist.some"]).to eql "value"
197
+ context "Extension" do
198
+ it "ignores nonexistent bases by default" do
199
+ # Ensure the hash contains its own value
200
+ expect(@config["drivers.base_does_not_exist.some"]).to eql "value"
201
+
202
+ # Also ensure the "base" is _not_ set properly
203
+ expect(@config["drivers.base_does_not_exist.base"]).to be_nil
204
+
205
+ # On the other hand, "extends" should stay.
206
+ expect(@config["drivers.base_does_not_exist.extends"]).to eql \
207
+ "nonexistent_base"
208
+ end
209
+
210
+ it "can pretend nonexistent bases are empty" do
211
+ opts = { nonexistent_base: :extend }
212
+ config = Collapsium::Config::Configuration.load_config(@config_path,
213
+ opts)
214
+
215
+ # Ensure the hash contains its own value
216
+ expect(config["drivers.base_does_not_exist.some"]).to eql "value"
181
217
 
182
- # Also ensure the "base" is _not_ set properly
183
- expect(@config["drivers.base_does_not_exist.base"]).to be_nil
218
+ # Here, the base must be set!
219
+ expect(config["drivers.base_does_not_exist.base"]).to eql \
220
+ %w(.drivers.nonexistent_base)
184
221
 
185
- # On the other hand, "extends" should stay.
186
- expect(@config["drivers.base_does_not_exist.extends"]).to eql \
187
- "nonexistent_base"
222
+ # Then "extends" needs to vanish.
223
+ expect(config["drivers.base_does_not_exist.extends"]).to be_nil
224
+ end
188
225
  end
189
226
  end
190
227
 
data/spec/data/global.yml CHANGED
@@ -2,3 +2,5 @@
2
2
  test: 42
3
3
  some:
4
4
  path: 123
5
+ foo:
6
+ extends: some
data/spec/module_spec.rb CHANGED
@@ -4,11 +4,12 @@ require_relative '../lib/collapsium-config'
4
4
  include Collapsium::Config
5
5
 
6
6
  describe Collapsium::Config do
7
- before do
7
+ before :each do
8
8
  @data_path = File.join(File.dirname(__FILE__), 'data')
9
9
  ENV.delete("SOME_PATH")
10
10
  ENV.delete("SOME")
11
11
  ENV.delete("PATH")
12
+ Collapsium::Config.config = nil
12
13
  end
13
14
 
14
15
  it "fails to load configuration from the default path (it does not exist)" do
@@ -55,5 +56,12 @@ describe Collapsium::Config do
55
56
  expect(config["some.path"]).to eql "override"
56
57
  expect(config["some"]["path"]).to eql "override"
57
58
  end
59
+
60
+ it "accepts non-default options" do
61
+ Collapsium::Config.config_file = File.join(@data_path, 'global.yml')
62
+ Collapsium::Config.config_options = { resolve_extensions: false }
63
+
64
+ expect(config["foo.base"]).to be_nil
65
+ end
58
66
  end
59
67
  end
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.3
4
+ version: 0.5.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-22 00:00:00.000000000 Z
11
+ date: 2017-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.45'
33
+ version: '0.46'
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.45'
40
+ version: '0.46'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.7'
103
+ version: '0.8'
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.7'
110
+ version: '0.8'
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 "