qonfig 0.12.0 → 0.13.0

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