collapsium-config 0.4.3 → 0.5.0

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