qonfig 0.12.0 → 0.13.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +457 -137
  4. data/lib/qonfig/command_set.rb +7 -1
  5. data/lib/qonfig/commands/add_nested_option.rb +3 -1
  6. data/lib/qonfig/commands/add_option.rb +2 -1
  7. data/lib/qonfig/commands/base.rb +2 -1
  8. data/lib/qonfig/commands/compose.rb +16 -10
  9. data/lib/qonfig/commands/expose_yaml.rb +2 -1
  10. data/lib/qonfig/commands/load_from_env.rb +2 -1
  11. data/lib/qonfig/commands/load_from_json.rb +2 -1
  12. data/lib/qonfig/commands/load_from_self.rb +2 -1
  13. data/lib/qonfig/commands/load_from_yaml.rb +2 -1
  14. data/lib/qonfig/data_set/lock.rb +46 -0
  15. data/lib/qonfig/data_set.rb +92 -23
  16. data/lib/qonfig/errors.rb +8 -0
  17. data/lib/qonfig/plugins/toml/commands/expose_toml.rb +2 -1
  18. data/lib/qonfig/plugins/toml/commands/load_from_toml.rb +2 -1
  19. data/lib/qonfig/settings/builder.rb +20 -4
  20. data/lib/qonfig/settings/callbacks.rb +43 -0
  21. data/lib/qonfig/settings/key_matcher.rb +175 -0
  22. data/lib/qonfig/settings/lock.rb +3 -3
  23. data/lib/qonfig/settings.rb +144 -33
  24. data/lib/qonfig/validator/basic.rb +53 -0
  25. data/lib/qonfig/validator/builder/attribute_consistency.rb +181 -0
  26. data/lib/qonfig/validator/builder.rb +169 -0
  27. data/lib/qonfig/validator/collection.rb +73 -0
  28. data/lib/qonfig/validator/dsl.rb +51 -0
  29. data/lib/qonfig/validator/method_based.rb +49 -0
  30. data/lib/qonfig/validator/predefined/common.rb +53 -0
  31. data/lib/qonfig/validator/predefined/registry.rb +83 -0
  32. data/lib/qonfig/validator/predefined/registry_control_mixin.rb +43 -0
  33. data/lib/qonfig/validator/predefined.rb +41 -0
  34. data/lib/qonfig/validator/proc_based.rb +53 -0
  35. data/lib/qonfig/validator.rb +58 -0
  36. data/lib/qonfig/version.rb +1 -1
  37. data/lib/qonfig.rb +1 -0
  38. data/qonfig.gemspec +1 -1
  39. metadata +19 -5
  40. data/lib/qonfig/data_set/validator.rb +0 -7
@@ -32,14 +32,16 @@ class Qonfig::Commands::AddNestedOption < Qonfig::Commands::Base
32
32
  end
33
33
  end
34
34
 
35
+ # @param data_set [Qonfig::DataSet]
35
36
  # @param settings [Qonfig::Settings]
36
37
  # @return [void]
37
38
  #
38
39
  # @api private
39
40
  # @since 0.1.0
40
- def call(settings)
41
+ def call(data_set, settings)
41
42
  nested_settings = nested_data_set_klass.new.settings
42
43
 
44
+ nested_settings.__mutation_callbacks__.add(settings.__mutation_callbacks__)
43
45
  settings.__define_setting__(key, nested_settings)
44
46
  end
45
47
  end
@@ -30,12 +30,13 @@ class Qonfig::Commands::AddOption < Qonfig::Commands::Base
30
30
  @value = value
31
31
  end
32
32
 
33
+ # @param data_set [Qonfig::DataSet]
33
34
  # @param settings [Qonfig::Settings]
34
35
  # @return [void]
35
36
  #
36
37
  # @api private
37
38
  # @since 0.1.0
38
- def call(settings)
39
+ def call(data_set, settings)
39
40
  settings.__define_setting__(key, value)
40
41
  end
41
42
  end
@@ -3,10 +3,11 @@
3
3
  # @api private
4
4
  # @since 0.1.0
5
5
  class Qonfig::Commands::Base
6
+ # @param data_set [Qonfig::DataSet]
6
7
  # @param settings [Qonfig::Settings]
7
8
  # @return [void]
8
9
  #
9
10
  # @api private
10
11
  # @since 0.1.0
11
- def call(settings); end
12
+ def call(data_set, settings); end
12
13
  end
@@ -7,31 +7,37 @@ class Qonfig::Commands::Compose < Qonfig::Commands::Base
7
7
  #
8
8
  # @api private
9
9
  # @since 0.1.0
10
- attr_reader :data_set_klass
10
+ attr_reader :composable_data_set_klass
11
11
 
12
- # @param data_set_klass [Qonfig::DataSet]
12
+ # @param composable_data_set_klass [Qonfig::DataSet]
13
13
  #
14
14
  # @raise [Qonfig::ArgumentError]
15
15
  #
16
16
  # @api private
17
17
  # @since 0.1.0
18
- def initialize(data_set_klass)
19
- raise(
20
- Qonfig::ArgumentError,
21
- 'Composed config class should be a subtype of Qonfig::DataSet'
22
- ) unless data_set_klass.is_a?(Class) && data_set_klass < Qonfig::DataSet
18
+ def initialize(composable_data_set_klass)
19
+ unless composable_data_set_klass.is_a?(Class) && composable_data_set_klass < Qonfig::DataSet
20
+ raise(
21
+ Qonfig::ArgumentError,
22
+ 'Composed config class should be a subtype of Qonfig::DataSet'
23
+ )
24
+ end
23
25
 
24
- @data_set_klass = data_set_klass
26
+ @composable_data_set_klass = composable_data_set_klass
25
27
  end
26
28
 
29
+ # @param data_set [Qonfig::DataSet]
27
30
  # @param settings [Qonfig::Settings]
28
31
  # @return [void]
29
32
  #
30
33
  # @api private
31
34
  # @since 0.1.0
32
- def call(settings)
33
- composite_settings = data_set_klass.new.settings
35
+ def call(data_set, settings)
36
+ # NOTE: append new validators
37
+ data_set.class.validators.concat(composable_data_set_klass.validators.dup)
34
38
 
39
+ # NOTE: append new settings
40
+ composite_settings = composable_data_set_klass.new.settings
35
41
  settings.__append_settings__(composite_settings)
36
42
  end
37
43
  end
@@ -60,12 +60,13 @@ class Qonfig::Commands::ExposeYAML < Qonfig::Commands::Base
60
60
  @env = env
61
61
  end
62
62
 
63
+ # @param data_set [Qonfig::DataSet]
63
64
  # @param settings [Qonfig::Settings]
64
65
  # @return [void]
65
66
  #
66
67
  # @api private
67
68
  # @since 0.7.0
68
- def call(settings)
69
+ def call(data_set, settings)
69
70
  case via
70
71
  when EXPOSERS[:file_name]
71
72
  expose_file_name!(settings)
@@ -55,12 +55,13 @@ class Qonfig::Commands::LoadFromENV < Qonfig::Commands::Base
55
55
  @trim_pattern = prefix.is_a?(Regexp) ? prefix : /\A(#{Regexp.escape(prefix.to_s)})/m
56
56
  end
57
57
 
58
+ # @param data_set [Qonfig::DataSet]
58
59
  # @param settings [Qonfig::Settings]
59
60
  # @return [void]
60
61
  #
61
62
  # @api private
62
63
  # @since 0.2.0
63
- def call(settings)
64
+ def call(data_set, settings)
64
65
  env_data = extract_env_data
65
66
 
66
67
  env_based_settings = build_data_set_class(env_data).new.settings
@@ -25,12 +25,13 @@ class Qonfig::Commands::LoadFromJSON < Qonfig::Commands::Base
25
25
  @strict = strict
26
26
  end
27
27
 
28
+ # @param data_set [Qonfig::DataSet]
28
29
  # @param settings [Qonfig::Settings]
29
30
  # @return [void]
30
31
  #
31
32
  # @api private
32
33
  # @since 0.5.0
33
- def call(settings)
34
+ def call(data_set, settings)
34
35
  json_data = Qonfig::Loaders::JSON.load_file(file_path, fail_on_unexist: strict)
35
36
 
36
37
  raise(
@@ -17,12 +17,13 @@ class Qonfig::Commands::LoadFromSelf < Qonfig::Commands::Base
17
17
  @caller_location = caller_location
18
18
  end
19
19
 
20
+ # @param data_set [Qonfig::DataSet]
20
21
  # @param settings [Qonfig::Settings]
21
22
  # @return [void]
22
23
  #
23
24
  # @api private
24
25
  # @since 0.2.0
25
- def call(settings)
26
+ def call(data_set, settings)
26
27
  yaml_data = load_self_placed_yaml_data
27
28
 
28
29
  yaml_based_settings = build_data_set_klass(yaml_data).new.settings
@@ -25,6 +25,7 @@ class Qonfig::Commands::LoadFromYAML < Qonfig::Commands::Base
25
25
  @strict = strict
26
26
  end
27
27
 
28
+ # @param data_set [Qonfig::DataSet]
28
29
  # @param settings [Qonfig::Settings]
29
30
  # @return [void]
30
31
  #
@@ -32,7 +33,7 @@ class Qonfig::Commands::LoadFromYAML < Qonfig::Commands::Base
32
33
  #
33
34
  # @api private
34
35
  # @since 0.2.0
35
- def call(settings)
36
+ def call(data_set, settings)
36
37
  yaml_data = Qonfig::Loaders::YAML.load_file(file_path, fail_on_unexist: strict)
37
38
 
38
39
  raise(
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::DataSet::Lock
6
+ # @return [void]
7
+ #
8
+ # @api private
9
+ # @since 0.13.0
10
+ def initialize
11
+ @access_lock = Mutex.new
12
+ @definition_lock = Mutex.new
13
+ end
14
+
15
+ # @param instructions [Proc]
16
+ # @return [void]
17
+ #
18
+ # @api private
19
+ # @since 0.13.0
20
+ def thread_safe_access(&instructions)
21
+ access_lock.owned? ? yield : access_lock.synchronize(&instructions)
22
+ end
23
+
24
+ # @param instructions [Proc]
25
+ # @return [void]
26
+ #
27
+ # @api private
28
+ # @since 0.13.0
29
+ def thread_safe_definition(&instructions)
30
+ definition_lock.owned? ? yield : definition_lock.synchronize(&instructions)
31
+ end
32
+
33
+ private
34
+
35
+ # @return [Mutex]
36
+ #
37
+ # @api private
38
+ # @since 0.13.0
39
+ attr_reader :access_lock
40
+
41
+ # @return [Mutex]
42
+ #
43
+ # @api private
44
+ # @since 0.13.0
45
+ attr_reader :definition_lock
46
+ end
@@ -2,29 +2,30 @@
2
2
 
3
3
  # @api public
4
4
  # @since 0.1.0
5
- class Qonfig::DataSet
5
+ class Qonfig::DataSet # rubocop:disable Metrics/ClassLength
6
6
  require_relative 'data_set/class_builder'
7
- require_relative 'data_set/validator'
7
+ require_relative 'data_set/lock'
8
8
 
9
9
  # @since 0.1.0
10
10
  extend Qonfig::DSL
11
11
 
12
+ # @since 0.13.0
13
+ extend Qonfig::Validator::DSL
14
+
12
15
  # @return [Qonfig::Settings]
13
16
  #
14
17
  # @api private
15
18
  # @since 0.1.0
16
19
  attr_reader :settings
17
20
 
18
- # @param options_map [Hash]
21
+ # @param settings_map [Hash]
19
22
  # @param configurations [Proc]
20
23
  #
21
24
  # @api public
22
25
  # @since 0.1.0
23
- def initialize(options_map = {}, &configurations)
24
- @__access_lock__ = Mutex.new
25
- @__definition_lock__ = Mutex.new
26
-
27
- thread_safe_definition { load!(options_map, &configurations) }
26
+ def initialize(settings_map = {}, &configurations)
27
+ @__lock__ = Qonfig::DataSet::Lock.new
28
+ thread_safe_definition { load!(settings_map, &configurations) }
28
29
  end
29
30
 
30
31
  # @return [void]
@@ -43,7 +44,7 @@ class Qonfig::DataSet
43
44
  thread_safe_access { settings.__is_frozen__ }
44
45
  end
45
46
 
46
- # @param options_map [Hash]
47
+ # @param settings_map [Hash]
47
48
  # @param configurations [Proc]
48
49
  # @return [void]
49
50
  #
@@ -51,22 +52,21 @@ class Qonfig::DataSet
51
52
  #
52
53
  # @api public
53
54
  # @since 0.2.0
54
- def reload!(options_map = {}, &configurations)
55
+ def reload!(settings_map = {}, &configurations)
55
56
  thread_safe_definition do
56
57
  raise Qonfig::FrozenSettingsError, 'Frozen config can not be reloaded' if frozen?
57
- load!(options_map, &configurations)
58
+ load!(settings_map, &configurations)
58
59
  end
59
60
  end
60
61
 
61
- # @param options_map [Hash]
62
+ # @param settings_map [Hash]
62
63
  # @return [void]
63
64
  #
64
65
  # @api public
65
66
  # @since 0.1.0
66
- def configure(options_map = {})
67
+ def configure(settings_map = {}, &configurations)
67
68
  thread_safe_access do
68
- settings.__apply_values__(options_map)
69
- yield(settings) if block_given?
69
+ apply_settings(settings_map, &configurations)
70
70
  end
71
71
  end
72
72
 
@@ -172,25 +172,94 @@ class Qonfig::DataSet
172
172
  thread_safe_access { settings.__clear__ }
173
173
  end
174
174
 
175
+ # @param block [Proc]
176
+ # @return [Enumerable]
177
+ #
178
+ # @yield [setting_key, setting_value]
179
+ # @yieldparam setting_key [String]
180
+ # @yieldparam setting_value [Object]
181
+ #
182
+ # @api public
183
+ # @since 0.13.0
184
+ def each_setting(&block)
185
+ thread_safe_access { settings.__each_setting__(&block) }
186
+ end
187
+
188
+ # @param block [Proc]
189
+ # @return [Enumerable]
190
+ #
191
+ # @yield [setting_key, setting_value]
192
+ # @yieldparam setting_key [String]
193
+ # @yieldparam setting_value [Object]
194
+ #
195
+ # @api public
196
+ # @since 0.13.0
197
+ def deep_each_setting(&block)
198
+ thread_safe_access { settings.__deep_each_setting__(&block) }
199
+ end
200
+
201
+ # @return [Boolean]
202
+ #
203
+ # @api public
204
+ # @since 0.13.0
205
+ def valid?
206
+ thread_safe_access { validator.valid? }
207
+ end
208
+
209
+ # @return [void]
210
+ #
211
+ # @api public
212
+ # @since 0.13.0
213
+ def validate!
214
+ thread_safe_access { validator.validate! }
215
+ end
216
+
175
217
  private
176
218
 
177
- # @return [Qonfig::Settings]
219
+ # @return [Qonfig::Validator]
220
+ #
221
+ # @api private
222
+ # @since 0.13.0
223
+ attr_reader :validator
224
+
225
+ # @return [void]
178
226
  #
179
227
  # @api private
180
228
  # @since 0.2.0
181
229
  def build_settings
182
- Qonfig::Settings::Builder.build(self.class.commands.dup)
230
+ @settings = Qonfig::Settings::Builder.build(self)
231
+ validator.validate!
232
+ end
233
+
234
+ # @return [void]
235
+ #
236
+ # @api private
237
+ # @since 0.13.0
238
+ def build_validator
239
+ @validator = Qonfig::Validator.new(self)
240
+ end
241
+
242
+ # @param settings_map [Hash]
243
+ # @param configurations [Proc]
244
+ # @return [void]
245
+ #
246
+ # @api private
247
+ # @since 0.13.0
248
+ def apply_settings(settings_map = {}, &configurations)
249
+ settings.__apply_values__(settings_map)
250
+ yield(settings) if block_given?
183
251
  end
184
252
 
185
- # @param options_map [Hash]
253
+ # @param settings_map [Hash]
186
254
  # @param configurations [Proc]
187
255
  # @return [void]
188
256
  #
189
257
  # @api private
190
258
  # @since 0.2.0
191
- def load!(options_map = {}, &configurations)
192
- @settings = build_settings
193
- configure(options_map, &configurations)
259
+ def load!(settings_map = {}, &configurations)
260
+ build_validator
261
+ build_settings
262
+ apply_settings(settings_map, &configurations)
194
263
  end
195
264
 
196
265
  # @param instructions [Proc]
@@ -199,7 +268,7 @@ class Qonfig::DataSet
199
268
  # @api private
200
269
  # @since 0.2.0
201
270
  def thread_safe_access(&instructions)
202
- @__access_lock__.synchronize(&instructions)
271
+ @__lock__.thread_safe_access(&instructions)
203
272
  end
204
273
 
205
274
  # @param instructions [Proc]
@@ -208,6 +277,6 @@ class Qonfig::DataSet
208
277
  # @api private
209
278
  # @since 0.2.0
210
279
  def thread_safe_definition(&instructions)
211
- @__definition_lock__.synchronize(&instructions)
280
+ @__lock__.thread_safe_definition(&instructions)
212
281
  end
213
282
  end
data/lib/qonfig/errors.rb CHANGED
@@ -9,6 +9,14 @@ module Qonfig
9
9
  # @since 0.1.0
10
10
  ArgumentError = Class.new(ArgumentError)
11
11
 
12
+ # @api public
13
+ # @since 0.13.0
14
+ ValidatorArgumentError = Class.new(ArgumentError)
15
+
16
+ # @api public
17
+ # @since 0.13.0
18
+ ValidationError = Class.new(Error)
19
+
12
20
  # @api public
13
21
  # @since 0.12.0
14
22
  PluginError = Class.new(Error)
@@ -60,12 +60,13 @@ class Qonfig::Commands::ExposeTOML < Qonfig::Commands::Base
60
60
  @env = env
61
61
  end
62
62
 
63
+ # @param data_set [Qonfig::DataSet]
63
64
  # @param settings [Qonfig::Settings]
64
65
  # @return [void]
65
66
  #
66
67
  # @api private
67
68
  # @since 0.12.0
68
- def call(settings)
69
+ def call(data_set, settings)
69
70
  case via
70
71
  when EXPOSERS[:file_name]
71
72
  expose_file_name!(settings)
@@ -25,12 +25,13 @@ class Qonfig::Commands::LoadFromTOML < Qonfig::Commands::Base
25
25
  @strict = strict
26
26
  end
27
27
 
28
+ # @param data_set [Qonfig::DataSet]
28
29
  # @param settings [Qonfig::Settings]
29
30
  # @return [void]
30
31
  #
31
32
  # @api private
32
33
  # @since 0.12.0
33
- def call(settings)
34
+ def call(data_set, settings)
34
35
  toml_data = Qonfig::Loaders::TOML.load_file(file_path, fail_on_unexist: strict)
35
36
  toml_based_settings = build_data_set_class(toml_data).new.settings
36
37
  settings.__append_settings__(toml_based_settings)
@@ -4,14 +4,30 @@
4
4
  # @since 0.2.0
5
5
  module Qonfig::Settings::Builder
6
6
  class << self
7
- # @param commands [Qonfig::CommandSet]
7
+ # @param data_set [Qonfig::DataSet]
8
8
  # @return [Qonfig::Settings]
9
9
  #
10
10
  # @api private
11
11
  # @since 0.2.0
12
- def build(commands)
13
- Qonfig::Settings.new.tap do |settings|
14
- commands.each { |command| command.call(settings) }
12
+ def build(data_set)
13
+ Qonfig::Settings.new(build_mutation_callbacks(data_set)).tap do |settings|
14
+ data_set.class.commands.dup.each do |command|
15
+ command.call(data_set, settings)
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ # @param data_set [Qonfig::DataSet]
23
+ # @return [Qonfig::Settings::Callbacks]
24
+ #
25
+ # @api private
26
+ # @since 0.13.0
27
+ def build_mutation_callbacks(data_set)
28
+ Qonfig::Settings::Callbacks.new.tap do |callbacks|
29
+ # NOTE: validation callbacks
30
+ callbacks.add(proc { data_set.validate! })
15
31
  end
16
32
  end
17
33
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Settings::Callbacks
6
+ # @api private
7
+ # @since 0.13.0
8
+ include Enumerable
9
+
10
+ # @return [void]
11
+ #
12
+ # @api private
13
+ # @since 0.13.0
14
+ def initialize
15
+ @callbacks = []
16
+ end
17
+
18
+ # @return [void]
19
+ #
20
+ # @api private
21
+ # @since 0.13.0
22
+ def call
23
+ callbacks.each(&:call)
24
+ end
25
+
26
+ # @param callback [Proc, Qonfig::Settings::Callbacks, #call]
27
+ # @return [void]
28
+ #
29
+ # @api private
30
+ # @since 0.13.0
31
+ def add(callback)
32
+ callbacks << callback
33
+ end
34
+ attr_reader :callback
35
+
36
+ private
37
+
38
+ # @return [Array<Proc>]
39
+ #
40
+ # @api private
41
+ # @since 0.13.0
42
+ attr_reader :callbacks
43
+ end