qonfig 0.25.0 → 0.26.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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/.rubocop.yml +19 -1
  4. data/.travis.yml +7 -2
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile.lock +102 -0
  7. data/README.md +2 -0
  8. data/gemfiles/with_external_deps.gemfile.lock +112 -0
  9. data/gemfiles/without_external_deps.gemfile.lock +102 -0
  10. data/lib/qonfig.rb +1 -1
  11. data/lib/qonfig/commands/definition/load_from_env.rb +2 -0
  12. data/lib/qonfig/commands/definition/load_from_env/value_converter.rb +2 -0
  13. data/lib/qonfig/imports/direct_key.rb +8 -2
  14. data/lib/qonfig/imports/mappings.rb +8 -2
  15. data/lib/qonfig/loaders/basic.rb +2 -0
  16. data/lib/qonfig/loaders/json.rb +2 -1
  17. data/lib/qonfig/settings.rb +5 -1
  18. data/lib/qonfig/settings/key_matcher.rb +1 -1
  19. data/lib/qonfig/uploaders/base.rb +2 -0
  20. data/lib/qonfig/uploaders/yaml.rb +2 -0
  21. data/lib/qonfig/version.rb +1 -1
  22. data/qonfig.gemspec +4 -6
  23. data/sig/.keep +0 -0
  24. data/spec/features/clear_options_spec.rb +92 -0
  25. data/spec/features/compacted_config_spec.rb +308 -0
  26. data/spec/features/composition_spec.rb +207 -0
  27. data/spec/features/config_definition_and_representation_spec.rb +535 -0
  28. data/spec/features/definition_order_spec.rb +69 -0
  29. data/spec/features/dig_functionality_spec.rb +47 -0
  30. data/spec/features/dot_notation_spec.rb +159 -0
  31. data/spec/features/export_settings_spec.rb +138 -0
  32. data/spec/features/expose_json_spec.rb +281 -0
  33. data/spec/features/expose_self/format_option_dynamic_spec.rb +69 -0
  34. data/spec/features/expose_self/format_option_json_spec.rb +74 -0
  35. data/spec/features/expose_self/format_option_unsupported_spec.rb +27 -0
  36. data/spec/features/expose_self/format_option_yaml_spec.rb +77 -0
  37. data/spec/features/expose_self_spec.rb +97 -0
  38. data/spec/features/expose_yaml_spec.rb +263 -0
  39. data/spec/features/freeze_state_spec.rb +122 -0
  40. data/spec/features/get_config_keys_spec.rb +62 -0
  41. data/spec/features/get_config_values_spec.rb +41 -0
  42. data/spec/features/has_a_key_spec.rb +48 -0
  43. data/spec/features/import_settings_spec.rb +323 -0
  44. data/spec/features/indifferent_access_spec.rb +57 -0
  45. data/spec/features/inheritance_spec.rb +110 -0
  46. data/spec/features/instantiation_without_class_definition_spec.rb +59 -0
  47. data/spec/features/iteration_over_setting_keys_spec.rb +48 -0
  48. data/spec/features/load_from_env_spec.rb +240 -0
  49. data/spec/features/load_from_json_spec.rb +97 -0
  50. data/spec/features/load_from_self/format_option_json_spec.rb +31 -0
  51. data/spec/features/load_from_self/format_option_unsupported_spec.rb +27 -0
  52. data/spec/features/load_from_self/format_option_yaml_spec.rb +49 -0
  53. data/spec/features/load_from_self/with_erb_instructions_spec.rb +33 -0
  54. data/spec/features/load_from_self/with_hash_like_data_representation_spec.rb +66 -0
  55. data/spec/features/load_from_self/with_non_hash_like_data_representation_spec.rb +19 -0
  56. data/spec/features/load_from_self/without_end_data_spec.rb +11 -0
  57. data/spec/features/load_from_yaml_spec.rb +110 -0
  58. data/spec/features/load_setting_values_from_file/by_instance_method_examples.rb +171 -0
  59. data/spec/features/load_setting_values_from_file/by_macros_examples.rb +165 -0
  60. data/spec/features/load_setting_values_from_file/load_from_json_spec.rb +21 -0
  61. data/spec/features/load_setting_values_from_file/load_from_self/json_format/end_data_with_env_spec.rb +100 -0
  62. data/spec/features/load_setting_values_from_file/load_from_self/json_format/with_end_data_spec.rb +129 -0
  63. data/spec/features/load_setting_values_from_file/load_from_self/json_format/with_incorrect_end_data_spec.rb +34 -0
  64. data/spec/features/load_setting_values_from_file/load_from_self/json_format/without_end_data_spec.rb +65 -0
  65. data/spec/features/load_setting_values_from_file/load_from_self/yaml_format/end_data_with_env_spec.rb +94 -0
  66. data/spec/features/load_setting_values_from_file/load_from_self/yaml_format/with_end_data_spec.rb +126 -0
  67. data/spec/features/load_setting_values_from_file/load_from_self/yaml_format/with_incorrect_end_data_spec.rb +32 -0
  68. data/spec/features/load_setting_values_from_file/load_from_self/yaml_format/without_end_data_spec.rb +65 -0
  69. data/spec/features/load_setting_values_from_file/load_from_yaml_spec.rb +21 -0
  70. data/spec/features/load_setting_values_from_file/shared_behavior_spec.rb +33 -0
  71. data/spec/features/mixin_spec.rb +387 -0
  72. data/spec/features/non_redefineable_core_methods_spec.rb +29 -0
  73. data/spec/features/plugins/pretty_print_spec.rb +86 -0
  74. data/spec/features/plugins/toml/expose_self/format_option_toml_spec.rb +71 -0
  75. data/spec/features/plugins/toml/expose_toml_spec.rb +221 -0
  76. data/spec/features/plugins/toml/load_from_self/format_option_toml_spec.rb +27 -0
  77. data/spec/features/plugins/toml/load_from_toml_spec.rb +109 -0
  78. data/spec/features/plugins/toml/load_setting_values_from_file/load_from_toml_spec.rb +27 -0
  79. data/spec/features/plugins/toml/load_setting_values_from_file/load_toml_from_self/end_data_with_env_spec.rb +95 -0
  80. data/spec/features/plugins/toml/load_setting_values_from_file/load_toml_from_self/with_end_data_spec.rb +125 -0
  81. data/spec/features/plugins/toml/load_setting_values_from_file/load_toml_from_self/with_incorrect_end_data_spec.rb +34 -0
  82. data/spec/features/plugins/toml/load_setting_values_from_file/load_toml_from_self/without_end_data_spec.rb +65 -0
  83. data/spec/features/plugins/toml/load_setting_values_from_file/shared_behavior_spec.rb +34 -0
  84. data/spec/features/plugins/toml/save_to_toml_spec.rb +149 -0
  85. data/spec/features/plugins/vault/expose_vault_spec.rb +117 -0
  86. data/spec/features/plugins/vault/load_from_vault_spec.rb +80 -0
  87. data/spec/features/plugins_spec.rb +89 -0
  88. data/spec/features/reload_spec.rb +75 -0
  89. data/spec/features/run_code_with_temporary_settings_spec.rb +104 -0
  90. data/spec/features/save_to_file/save_to_json_spec.rb +157 -0
  91. data/spec/features/save_to_file/save_to_yaml_spec.rb +189 -0
  92. data/spec/features/settings_as_predicates_spec.rb +47 -0
  93. data/spec/features/settings_redefinition_spec.rb +30 -0
  94. data/spec/features/slice_functionality_spec.rb +69 -0
  95. data/spec/features/subset_functionality_spec.rb +49 -0
  96. data/spec/features/validation_spec.rb +916 -0
  97. data/spec/fixtures/array_settings.yml +3 -0
  98. data/spec/fixtures/expose_json/incompatible_root_structure.json +6 -0
  99. data/spec/fixtures/expose_json/incompatible_structure.json +4 -0
  100. data/spec/fixtures/expose_json/project.development.json +7 -0
  101. data/spec/fixtures/expose_json/project.json +30 -0
  102. data/spec/fixtures/expose_json/project.production.json +7 -0
  103. data/spec/fixtures/expose_json/project.staging.json +7 -0
  104. data/spec/fixtures/expose_json/project.test.json +7 -0
  105. data/spec/fixtures/expose_yaml/incompatible_structure.yml +2 -0
  106. data/spec/fixtures/expose_yaml/project.development.yml +4 -0
  107. data/spec/fixtures/expose_yaml/project.production.yml +4 -0
  108. data/spec/fixtures/expose_yaml/project.staging.yml +4 -0
  109. data/spec/fixtures/expose_yaml/project.test.yml +4 -0
  110. data/spec/fixtures/expose_yaml/project.yml +25 -0
  111. data/spec/fixtures/json_array_sample.json +14 -0
  112. data/spec/fixtures/json_object_sample.json +9 -0
  113. data/spec/fixtures/json_with_empty_object.json +6 -0
  114. data/spec/fixtures/json_with_erb.json +6 -0
  115. data/spec/fixtures/plugins/toml/expose_toml/project.development.toml +4 -0
  116. data/spec/fixtures/plugins/toml/expose_toml/project.production.toml +4 -0
  117. data/spec/fixtures/plugins/toml/expose_toml/project.staging.toml +4 -0
  118. data/spec/fixtures/plugins/toml/expose_toml/project.test.toml +4 -0
  119. data/spec/fixtures/plugins/toml/expose_toml/project.toml +27 -0
  120. data/spec/fixtures/plugins/toml/mini_file.toml +6 -0
  121. data/spec/fixtures/plugins/toml/toml_sample_with_all_types.toml +72 -0
  122. data/spec/fixtures/plugins/toml/values_file/with_env.toml +13 -0
  123. data/spec/fixtures/plugins/toml/values_file/without_env.toml +6 -0
  124. data/spec/fixtures/rubocop_settings.yml +12 -0
  125. data/spec/fixtures/shared_settings_with_aliases.yml +10 -0
  126. data/spec/fixtures/travis_settings.yml +6 -0
  127. data/spec/fixtures/values_file/with_env.json +19 -0
  128. data/spec/fixtures/values_file/with_env.yml +17 -0
  129. data/spec/fixtures/values_file/without_env.json +8 -0
  130. data/spec/fixtures/values_file/without_env.yml +5 -0
  131. data/spec/fixtures/with_empty_hash.yml +4 -0
  132. data/spec/fixtures/with_erb_instructions.yml +3 -0
  133. data/spec/spec_helper.rb +27 -0
  134. data/spec/support/meta_scopes.rb +20 -0
  135. data/spec/support/spec_support.rb +42 -0
  136. metadata +128 -11
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe '(#subset)-functionality' do
4
+ specify '#subset functionality works as expected :)' do
5
+ class SubsetableConfig < Qonfig::DataSet
6
+ setting :db do
7
+ setting :creds do
8
+ setting :user, 'D@iVeR'
9
+ setting :data, test: false
10
+ end
11
+ end
12
+
13
+ setting :adapter, :sidekiq
14
+ end
15
+
16
+ config = SubsetableConfig.new
17
+
18
+ # access to the subset (with indifferent keys type)
19
+ expect(config.subset(:db, ['db', :creds], ['db', 'creds', :user], 'adapter')).to match(
20
+ 'db' => { 'creds' => { 'user' => 'D@iVeR', 'data' => { test: false } } }, # :db
21
+ 'creds' => { 'user' => 'D@iVeR', 'data' => { test: false } }, # ['db', :creds]
22
+ 'user' => 'D@iVeR', # ['db', 'creds', :user]
23
+ 'adapter' => :sidekiq # 'adepter'
24
+ )
25
+
26
+ # try to use subset with with unexistent keys
27
+ # NOTE: :megazavr key does not exist
28
+ expect { config.subset(%i[db creds megazavr]) }.to raise_error(Qonfig::UnknownSettingError)
29
+ # NOTE: :test key does not exist
30
+ expect { config.subset(:db, :test) }.to raise_error(Qonfig::UnknownSettingError)
31
+
32
+ # you cant use subset operation over setting values - you can do it only over the setting keys!
33
+ expect { config.subset(%i[db creds data test]) }.to raise_error(Qonfig::UnknownSettingError)
34
+
35
+ # subset invokation with empty key list
36
+ # rubocop:disable Lint/RedundantSplatExpansion
37
+ expect(config.subset(*[])).to eq({})
38
+ expect(config.subset).to eq({})
39
+ # rubocop:enable Lint/RedundantSplatExpansion
40
+
41
+ # subset invokation over unexistent option
42
+ expect { config.subset(%i[db creds session]) }.to raise_error(Qonfig::UnknownSettingError)
43
+ expect { config.subset(%i[a b c d]) }.to raise_error(Qonfig::UnknownSettingError)
44
+
45
+ # invokation with incorret subset key attributes
46
+ expect { config.subset([:db, :creds, Object.new]) }.to raise_error(Qonfig::ArgumentError)
47
+ expect { config.subset(:db, 123) }.to raise_error(Qonfig::ArgumentError)
48
+ end
49
+ end
@@ -0,0 +1,916 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Validation' do
4
+ describe 'DSL' do
5
+ specify 'fails when validation method isnt chosen' do
6
+ expect do
7
+ # NOTE: no validation block, no dataset method
8
+ Class.new(Qonfig::DataSet) { validate }
9
+ end.to raise_error(Qonfig::ValidatorArgumentError)
10
+ end
11
+
12
+ specify 'fails when you try to use both block validation and method validation' do
13
+ expect do
14
+ Class.new(Qonfig::DataSet) do
15
+ validate '*', by: :test do # NOTE: dataset mtehod
16
+ true
17
+ end # NOTE: block
18
+ end
19
+ end.to raise_error(Qonfig::ValidatorArgumentError)
20
+ end
21
+
22
+ specify 'fails when you try to use both predefined validator and any validation method' do
23
+ expect do
24
+ Class.new(Qonfig::DataSet) do
25
+ validate '*', :integer do
26
+ true
27
+ end
28
+ end
29
+ end.to raise_error(Qonfig::ValidatorArgumentError)
30
+
31
+ expect do
32
+ Class.new(Qonfig::DataSet) do
33
+ validate '*', :integer, by: :check
34
+ end
35
+ end.to raise_error(Qonfig::ValidatorArgumentError)
36
+
37
+ expect do
38
+ Class.new(Qonfig::DataSet) do
39
+ validate '*', :integer, by: :check do
40
+ true
41
+ end
42
+ end
43
+ end.to raise_error(Qonfig::ValidatorArgumentError)
44
+ end
45
+
46
+ specify 'fails when required predefined validator does not exist' do
47
+ # NOTE: incorrect name => error
48
+ expect do
49
+ Class.new(Qonfig::DataSet) do
50
+ validate '*', 1923923
51
+ end
52
+ end.to raise_error(Qonfig::ValidatorArgumentError)
53
+
54
+ # NOTE: nonexistent predefined validator => error
55
+ expect do
56
+ Class.new(Qonfig::DataSet) do
57
+ validate '*', :abracadabra
58
+ end
59
+ end.to raise_error(Qonfig::ValidatorArgumentError)
60
+
61
+ # NOTE: existent predefined validator => ok
62
+ expect do
63
+ Class.new(Qonfig::DataSet) do
64
+ validate '*', :integer
65
+ validate '*', :float
66
+ validate '*', :string
67
+ validate '*', :symbol
68
+ validate '*', :numeric
69
+ validate '*', :hash, strict: true
70
+ validate '*', :array, strict: true
71
+ validate '*', :big_decimal, strict: true
72
+ validate '*', :boolean, strict: true
73
+ end
74
+ end.not_to raise_error
75
+ end
76
+
77
+ specify 'predefned validatiors can be selected by string and symbols' do
78
+ expect do
79
+ Class.new(Qonfig::DataSet) do
80
+ validate 'a', :integer
81
+ validate 'b', 'integer', strict: true
82
+ end
83
+ end.not_to raise_error
84
+ end
85
+
86
+ specify 'dataset method validation (by:): fails on incorrect method name' do
87
+ expect do
88
+ # NOTE: only strings and symbols are supported
89
+ Class.new(Qonfig::DataSet) { validate by: 123 }
90
+ end.to raise_error(Qonfig::ValidatorArgumentError)
91
+
92
+ expect do
93
+ # NOTE: only strings and symbols are supported
94
+ Class.new(Qonfig::DataSet) { validate by: '123' }
95
+ end.not_to raise_error
96
+
97
+ expect do
98
+ # NOTE: only strings and symbols are supported
99
+ Class.new(Qonfig::DataSet) { validate by: :my_method }
100
+ end.not_to raise_error
101
+ end
102
+
103
+ specify 'you can set validation method that is not defined yet' do
104
+ expect do
105
+ Class.new(Qonfig::DataSet) do
106
+ validate by: :my_method # NOTE: not defined method
107
+ validate by: 'another_method' # NOTE: not defined method
108
+ end
109
+ end.not_to raise_error
110
+ end
111
+
112
+ specify 'correct setting key patterns' do
113
+ expect do
114
+ Class.new(Qonfig::DataSet) do
115
+ validate :db do # NOTE: symbol => correct
116
+ end
117
+
118
+ validate 'db.creds' do # NOTE: string => correct
119
+ end
120
+
121
+ validate :user, by: :my_method # NOTE: symbol => correct
122
+ validate 'password', by: :my_method # NOTE: string => correct
123
+ end
124
+ end.not_to raise_error
125
+ end
126
+
127
+ specify 'incorrect setting key patterns' do
128
+ expect do
129
+ Class.new(Qonfig::DataSet) do
130
+ validate 123, by: :my_method
131
+ end
132
+ end.to raise_error(Qonfig::ValidatorArgumentError)
133
+
134
+ expect do
135
+ Class.new(Qonfig::DataSet) do
136
+ validate 123 do
137
+ true
138
+ end
139
+ end
140
+ end.to raise_error(Qonfig::ValidatorArgumentError)
141
+ end
142
+
143
+ specify ':strict and non-strict both can be used' do
144
+ expect do
145
+ Class.new(Qonfig::DataSet) do
146
+ validate(:kek, strict: true) {}
147
+ validate(:pek, strict: false) {}
148
+ end
149
+ end.not_to raise_error
150
+ end
151
+
152
+ specify 'fails with error on non-boolean values of :strict attribute' do
153
+ expect do
154
+ Class.new(Qonfig::DataSet) do
155
+ validate(:check, strict: Object.new) {}
156
+ end
157
+ end.to raise_error(Qonfig::ValidatorArgumentError)
158
+
159
+ expect do
160
+ Class.new(Qonfig::DataSet) do
161
+ validate(:check, strict: nil) {}
162
+ end
163
+ end.to raise_error(Qonfig::ValidatorArgumentError)
164
+ end
165
+ end
166
+
167
+ describe 'validations' do
168
+ let(:config_klass) do
169
+ Class.new(Qonfig::DataSet) do
170
+ setting :telegraf_url, 'test' # NOTE: all right
171
+
172
+ # NOTE: check that telegraf_url is a string value
173
+ validate('telegraf_url', strict: true) do |value|
174
+ value.is_a?(String)
175
+ end
176
+ end
177
+ end
178
+
179
+ specify 'validates invalid settings on instnation' do
180
+ config_klass = Class.new(Qonfig::DataSet) do
181
+ setting :telegraf_url, 12345 # NOTE: should be a string
182
+ validate 'telegraf_url' do |value|
183
+ value.is_a?(String)
184
+ end
185
+ end
186
+
187
+ expect { config_klass.new }.to raise_error(Qonfig::ValidationError)
188
+ end
189
+
190
+ specify 'validates settings defined by configuration options on instantiation' do
191
+ expect { config_klass.new(telegraf_url: '123') }.not_to raise_error
192
+ expect { config_klass.new(telegraf_url: 123) }.to raise_error(Qonfig::ValidationError)
193
+ end
194
+
195
+ specify 'validates settings defined by configuration block' do
196
+ expect do
197
+ config_klass.new.configure do |config|
198
+ config.telegraf_url = 123 # NOTE: should be a string
199
+ end
200
+ end.to raise_error(Qonfig::ValidationError)
201
+ end
202
+
203
+ specify 'validates settings setted in runtime' do
204
+ config = config_klass.new
205
+
206
+ expect { config.settings.telegraf_url = 1234 }.to raise_error(Qonfig::ValidationError)
207
+ expect { config.settings.telegraf_url = '55' }.not_to raise_error
208
+ end
209
+
210
+ specify 'invokes validations on config reload' do
211
+ # reload with hash
212
+ expect do
213
+ config_klass.new.reload!(telegraf_url: 123) # NOTE: should be a string
214
+ end.to raise_error(Qonfig::ValidationError)
215
+
216
+ # reload with configuration block
217
+ expect do
218
+ config_klass.new.reload! do |conf|
219
+ conf.telegraf_url = 123 # NOTE: should be a string
220
+ end
221
+ end.to raise_error(Qonfig::ValidationError)
222
+ end
223
+
224
+ specify 'invokes validations on config clearing' do
225
+ # NOTE:
226
+ # before #clear!: telegraf_config => 'test' (correct value)
227
+ # after #clear!: telegraf_config => nil (incorrect value)
228
+
229
+ expect { config_klass.new.clear! }.to raise_error(Qonfig::ValidationError)
230
+ end
231
+
232
+ specify 'config state after exception interception (#valid? / #validate!)' do
233
+ config = config_klass.new
234
+ expect(config.valid?).to eq(true)
235
+
236
+ begin
237
+ config.clear! # NOTE: set telegraf_url to nil (to incorrect value)
238
+ rescue Qonfig::ValidationError
239
+ end
240
+
241
+ expect(config.valid?).to eq(false)
242
+ end
243
+
244
+ specify 'deeply nested settings validation' do
245
+ deep_config_klass = Class.new(Qonfig::DataSet) do
246
+ setting :db do
247
+ setting :user, 'D@iVeR'
248
+ setting :password, 'test123'
249
+ end
250
+
251
+ # NOTE: setting key pattern :)
252
+ validate('db.user') { |value| value.is_a?(String) }
253
+ end
254
+
255
+ # NOTE: all right (originally)
256
+ expect { deep_config_klass.new }.not_to raise_error
257
+
258
+ # NOTE: change validated setting to incorrect value
259
+ # rubocop:disable Layout/LineLength
260
+ expect { deep_config_klass.new(db: { user: 123 }) }.to raise_error(Qonfig::ValidationError)
261
+ expect { deep_config_klass.new.settings.db.user = 123 }.to raise_error(Qonfig::ValidationError)
262
+ # rubocop:enable Layout/LineLength
263
+
264
+ # NOTE: change non-validated setting to any value
265
+ expect { deep_config_klass.new(db: { password: 123 }) }.not_to raise_error
266
+ expect { deep_config_klass.new.settings.db.password = 555 }.not_to raise_error
267
+ end
268
+
269
+ specify 'child class inherits the base class validations' do
270
+ base_config_klass = Class.new(Qonfig::DataSet) do
271
+ setting :adapter, 'sidekiq'
272
+ validate(:adapter, strict: true) { |value| value.is_a?(String) }
273
+ end
274
+
275
+ child_config_klass = Class.new(base_config_klass) do
276
+ setting :enabled, false
277
+ validate('enabled') { |value| value.is_a?(TrueClass) || value.is_a?(FalseClass) }
278
+
279
+ # NOTE: should inherit :adapter valdations
280
+ end
281
+
282
+ # NOTE: all right (originally)
283
+ expect { child_config_klass.new }.not_to raise_error
284
+ expect do
285
+ config = child_config_klass.new
286
+ config.settings.adapter = 'resque'
287
+ config.settings.enabled = true
288
+ end.not_to raise_error
289
+
290
+ # NOTE: inherited validations
291
+ # rubocop:disable Layout/LineLength
292
+ expect { child_config_klass.new(adapter: 123) }.to raise_error(Qonfig::ValidationError)
293
+ expect { child_config_klass.new.settings.adapter = 123 }.to raise_error(Qonfig::ValidationError)
294
+ expect { child_config_klass.new { |conf| conf.adapter = 123 } }.to raise_error(Qonfig::ValidationError)
295
+ expect { child_config_klass.new.reload!(adapter: 123) }.to raise_error(Qonfig::ValidationError)
296
+ # rubocop:enable Layout/LineLength
297
+
298
+ config = child_config_klass.new
299
+ expect(config.valid?).to eq(true)
300
+ begin
301
+ config.settings.adapter = 123
302
+ rescue Qonfig::ValidationError
303
+ end
304
+ expect(config.valid?).to eq(false)
305
+ expect(config.settings.adapter).to eq(123)
306
+
307
+ # NOTE: own validations
308
+ # rubocop:disable Layout/LineLength
309
+ expect { child_config_klass.new(enabled: '123') }.to raise_error(Qonfig::ValidationError)
310
+ expect { child_config_klass.new.settings.enabled = '123' }.to raise_error(Qonfig::ValidationError)
311
+ expect { child_config_klass.new { |conf| conf.enabled = '123' } }.to raise_error(Qonfig::ValidationError)
312
+ expect { child_config_klass.new.reload!(enabled: '123') }.to raise_error(Qonfig::ValidationError)
313
+ expect { child_config_klass.new.clear! }.to raise_error(Qonfig::ValidationError)
314
+ # rubocop:enable Layout/LineLength
315
+
316
+ config = child_config_klass.new
317
+ expect(config.valid?).to eq(true)
318
+ begin
319
+ config.settings.enabled = '123'
320
+ rescue Qonfig::ValidationError
321
+ end
322
+ expect(config.valid?).to eq(false)
323
+ expect(config.settings.enabled).to eq('123')
324
+ end
325
+
326
+ specify 'config composition inherits validators' do
327
+ config_klass = Class.new(Qonfig::DataSet) do
328
+ setting :telegraf_url, 'udp://localhost:9094'
329
+ validate(:telegraf_url) { |value| value.is_a?(String) }
330
+ end
331
+
332
+ composed_config_klass = Class.new(Qonfig::DataSet) do
333
+ # NOTE: root configs
334
+ compose(config_klass)
335
+
336
+ # NOTE: nested configs
337
+ setting(:nested) { compose(config_klass) }
338
+ end
339
+
340
+ # NOTE: all right
341
+ expect { composed_config_klass.new }.not_to raise_error
342
+ expect do
343
+ config = composed_config_klass.new
344
+ config.settings.telegraf_url = '123'
345
+ config.settings.nested.telegraf_url = '123'
346
+ end.not_to raise_error
347
+
348
+ expect do # NOTE: check root config validation
349
+ composed_config_klass.new.settings.telegraf_url = 123 # NOTE: should be a string
350
+ end.to raise_error(Qonfig::ValidationError)
351
+
352
+ expect do # NOTE: check nested config validation
353
+ composed_config_klass.new.settings.nested.telegraf_url = 123
354
+ end.to raise_error(Qonfig::ValidationError)
355
+ end
356
+
357
+ specify 'setting patterns (validation of a set of configs chosen by setting pattern)' do
358
+ config_klass = Class.new(Qonfig::DataSet) do
359
+ setting :db do
360
+ setting :creds do
361
+ setting :user, 'D@iVeR'
362
+ setting :password, 'test123'
363
+ end
364
+ end
365
+
366
+ setting :sidekiq do
367
+ setting :admin do
368
+ setting :user, 'D@iVeR'
369
+ setting :password, '123test'
370
+ end
371
+
372
+ setting :logger_level, 'info'
373
+ end
374
+
375
+ setting :adapter, :resque
376
+ setting :port, 1019
377
+
378
+ # only the root setting key
379
+ # (port)
380
+ validate('port', strict: true) do |value|
381
+ value.is_a?(Numeric)
382
+ end
383
+
384
+ # all .user setting keys
385
+ # (db.creds.user, sidekiq.admin.user)
386
+ validate('#.user', strict: true) do |value|
387
+ value.is_a?(String)
388
+ end
389
+
390
+ # one level inside db AND all password setting keys there
391
+ # (db.creds.password)
392
+ validate('db.*.password', strict: true) do |value|
393
+ value.is_a?(String)
394
+ end
395
+
396
+ # all .adapter setting keys
397
+ # (adapter)
398
+ validate('#.adapter', strict: true) do |value|
399
+ value.is_a?(Symbol)
400
+ end
401
+
402
+ # all keys inside sidekiq setting group
403
+ # (sidekiq.admin.user, sidekiq.admin.password, sidekiq.logger_level)
404
+ validate('sidekiq.#', strict: true) do |value|
405
+ value.is_a?(String)
406
+ end
407
+ end
408
+
409
+ # NOTE: all right (originally)
410
+ expect { config_klass.new }.not_to raise_error
411
+
412
+ # rubocop:disable Layout/LineLength
413
+ expect { config_klass.new.settings.db.creds.user = 123 }.to raise_error(Qonfig::ValidationError)
414
+ expect { config_klass.new.settings.sidekiq.admin.user = 123 }.to raise_error(Qonfig::ValidationError)
415
+ expect { config_klass.new.settings.sidekiq.admin.password = 123 }.to raise_error(Qonfig::ValidationError)
416
+ expect { config_klass.new.settings.sidekiq.logger_level = nil }.to raise_error(Qonfig::ValidationError)
417
+ expect { config_klass.new.settings.db.creds.password = 123 }.to raise_error(Qonfig::ValidationError)
418
+ expect { config_klass.new.settings.adapter = 'que' }.to raise_error(Qonfig::ValidationError)
419
+ expect { config_klass.new.settings.port = '555' }.to raise_error(Qonfig::ValidationError)
420
+ # rubocop:enable Layout/LineLength
421
+
422
+ expect { config_klass.new.settings.db.creds.user = '123' }.not_to raise_error
423
+ expect { config_klass.new.settings.sidekiq.admin.user = '123' }.not_to raise_error
424
+ expect { config_klass.new.settings.sidekiq.admin.password = '123' }.not_to raise_error
425
+ expect { config_klass.new.settings.sidekiq.logger_level = 'warn' }.not_to raise_error
426
+ expect { config_klass.new.settings.db.creds.password = '123' }.not_to raise_error
427
+ expect { config_klass.new.settings.adapter = :que }.not_to raise_error
428
+ expect { config_klass.new.settings.port = 5599 }.not_to raise_error
429
+ end
430
+
431
+ specify 'proc-based validation wokrs inside dataset instance context' do
432
+ config_klass = Class.new(Qonfig::DataSet) do
433
+ setting :some_key, 555
434
+
435
+ validate 'some_key' do |value|
436
+ value > some_method
437
+ end
438
+
439
+ def some_method
440
+ 123
441
+ end
442
+ end
443
+
444
+ # NOTE: all right (originally)
445
+ expect { config_klass.new }.not_to raise_error
446
+
447
+ # NOTE: invalid value
448
+ expect { config_klass.new(some_key: 122) }.to raise_error(Qonfig::ValidationError)
449
+ # NOTE: valid value
450
+ expect { config_klass.new(some_key: 1234) }.not_to raise_error
451
+ end
452
+
453
+ specify 'setting key validation by custom method defined directly on dataset' do
454
+ config_klass = Class.new(Qonfig::DataSet) do
455
+ setting :db do
456
+ setting :enabled, false
457
+ end
458
+
459
+ # NOTE: validate setting keys by custom dataset method
460
+ validate 'db.#', by: :check_credentials
461
+
462
+ def check_credentials(setting_key_value)
463
+ setting_key_value.is_a?(TrueClass) || setting_key_value.is_a?(FalseClass)
464
+ end
465
+ end
466
+
467
+ # NOTE: all right (orginally)
468
+ expect { config_klass.new }.not_to raise_error
469
+
470
+ # NOTE: invalid values
471
+ expect { config_klass.new.settings.db.enabled = 123 }.to raise_error(Qonfig::ValidationError)
472
+ expect { config_klass.new(db: { enabled: 123 }) }.to raise_error(Qonfig::ValidationError)
473
+
474
+ # NOTE: valid values
475
+ expect { config_klass.new.settings.db.enabled = true }.not_to raise_error
476
+ expect { config_klass.new(db: { enabled: true }) }.not_to raise_error
477
+ end
478
+
479
+ specify 'full data set object validation via #validate' do
480
+ # валидация всего сеттингса скопом (синтаксис "validate { |settings| } / validate by:")
481
+ config_klass = Class.new(Qonfig::DataSet) do
482
+ setting :namespace do
483
+ setting :enabled, :true
484
+ end
485
+
486
+ setting :go_for_cybersport, 'NO'
487
+
488
+ # NOTE: no setting key pattern => full dataset object validation
489
+ validate { settings.namespace.enabled.is_a?(Symbol) }
490
+
491
+ # NOTE: no setting key pattern => full dataset object validation
492
+ validate by: :check_all
493
+
494
+ def check_all
495
+ settings.go_for_cybersport == 'NO'
496
+ end
497
+ end
498
+
499
+ # NOTE: all right (originally)
500
+ expect { config_klass.new }.not_to raise_error
501
+
502
+ # NOTE: invalid values
503
+ # (namespace.enabled should be a symbol)
504
+ expect do
505
+ config_klass.new.settings.namespace.enabled = 123
506
+ end.to raise_error(Qonfig::ValidationError)
507
+ # (go_for_cybersport should have the 'NO' string value)
508
+ expect do
509
+ config_klass.new.settings.go_for_cybersport = 'YES'
510
+ end.to raise_error(Qonfig::ValidationError)
511
+
512
+ # NOTE: valid values
513
+ expect { config_klass.new.settings.namespace.enabled = :false }.not_to raise_error
514
+ expect { config_klass.new.settings.go_for_cybersport = 'NO' }.not_to raise_error
515
+ end
516
+ end
517
+
518
+ describe 'strict behaviour' do
519
+ specify 'non-strict by default (validation ignores nil values)' do
520
+ config = Qonfig::DataSet.build do
521
+ setting :login, 'D@iVeR'
522
+ setting :password, 'atata123'
523
+ setting :enabled, true
524
+
525
+ validate :login, :string
526
+ validate(:password) { |value| value.is_a?(String) }
527
+ validate :enabled, by: :check_enabled_setting
528
+
529
+ def check_enabled_setting(value)
530
+ value.is_a?(FalseClass) || value.is_a?(TrueClass)
531
+ end
532
+ end
533
+
534
+ expect(config.valid?).to eq(true)
535
+ expect { config.settings.login = nil }.not_to raise_error
536
+ expect { config.settings.password = nil }.not_to raise_error
537
+ expect { config.settings.enabled = nil }.not_to raise_error
538
+ expect(config.valid?).to eq(true)
539
+ end
540
+
541
+ specify 'strict validation does not ignore nil value' do
542
+ config = Qonfig::DataSet.build do
543
+ setting :login, '0exp'
544
+ setting :password, 'test123'
545
+ setting :enabled, false
546
+
547
+ validate :login, :string, strict: true
548
+ validate(:password, strict: true) { |value| value.is_a?(String) }
549
+ validate :enabled, by: :check_enabled_setting, strict: true
550
+
551
+ def check_enabled_setting(value)
552
+ value.is_a?(FalseClass) || value.is_a?(TrueClass)
553
+ end
554
+ end
555
+
556
+ expect(config.valid?).to eq(true)
557
+ expect { config.settings.login = nil }.to raise_error(Qonfig::ValidationError)
558
+ expect { config.settings.password = nil }.to raise_error(Qonfig::ValidationError)
559
+ expect { config.settings.enabled = nil }.to raise_error(Qonfig::ValidationError)
560
+ expect(config.valid?).to eq(false)
561
+ end
562
+ end
563
+
564
+ describe 'predefined validators' do
565
+ specify 'common behaviour (strict)' do
566
+ config_klass = Class.new(Qonfig::DataSet) do
567
+ setting :enabled, false
568
+ setting :count, 123
569
+ setting :amount, 23.55
570
+ setting :adapter, 'sidekiq'
571
+ setting :switcher, :on
572
+ setting :data, [1, 2, 3]
573
+ setting :mappings, a: 1, b: 2
574
+ setting :age, 20
575
+
576
+ validate :enabled, :boolean, strict: true
577
+ validate :count, :integer, strict: true
578
+ validate :amount, :float, strict: true
579
+ validate :adapter, :string, strict: true
580
+ validate :switcher, :symbol, strict: true
581
+ validate :data, :array, strict: true
582
+ validate :mappings, :hash, strict: true
583
+ validate :age, :numeric, strict: true
584
+ end
585
+
586
+ # NOTE: all right (originally)
587
+ expect { config_klass.new }.not_to raise_error
588
+
589
+ # NOTE: invalid values
590
+ expect { config_klass.new.settings.enabled = nil }.to raise_error(Qonfig::ValidationError)
591
+ expect { config_klass.new.settings.count = '5' }.to raise_error(Qonfig::ValidationError)
592
+ expect { config_klass.new.settings.amount = 22 }.to raise_error(Qonfig::ValidationError)
593
+ expect { config_klass.new.settings.adapter = :resque }.to raise_error(Qonfig::ValidationError)
594
+ expect { config_klass.new.settings.switcher = 'off' }.to raise_error(Qonfig::ValidationError)
595
+ expect { config_klass.new.settings.data = {} }.to raise_error(Qonfig::ValidationError)
596
+ expect { config_klass.new.settings.mappings = [] }.to raise_error(Qonfig::ValidationError)
597
+ expect { config_klass.new.settings.age = nil }.to raise_error(Qonfig::ValidationError)
598
+
599
+ # NOTE: valid values
600
+ expect { config_klass.new.settings.enabled = true }.not_to raise_error
601
+ expect { config_klass.new.settings.count = 5 }.not_to raise_error
602
+ expect { config_klass.new.settings.amount = 22.0 }.not_to raise_error
603
+ expect { config_klass.new.settings.adapter = 'resque' }.not_to raise_error
604
+ expect { config_klass.new.settings.switcher = :off }.not_to raise_error
605
+ expect { config_klass.new.settings.data = [] }.not_to raise_error
606
+ expect { config_klass.new.settings.mappings = {} }.not_to raise_error
607
+ expect { config_klass.new.settings.age = 20.1 }.not_to raise_error
608
+ end
609
+
610
+ specify 'common behaviour (non-strict)' do
611
+ config_klass = Class.new(Qonfig::DataSet) do
612
+ setting :enabled, false
613
+ setting :count, 123
614
+ setting :amount, 23.55
615
+ setting :adapter, 'sidekiq'
616
+ setting :switcher, :on
617
+ setting :data, [1, 2, 3]
618
+ setting :mappings, a: 1, b: 2
619
+ setting :age, 20
620
+
621
+ validate :enabled, :boolean
622
+ validate :count, :integer
623
+ validate :amount, :float
624
+ validate :adapter, :string
625
+ validate :switcher, :symbol
626
+ validate :data, :array
627
+ validate :mappings, :hash
628
+ validate :age, :numeric
629
+ end
630
+
631
+ # NOTE: all right (originally)
632
+ expect { config_klass.new }.not_to raise_error
633
+
634
+ # NOTE: invalid values
635
+ expect { config_klass.new.settings.count = '5' }.to raise_error(Qonfig::ValidationError)
636
+ expect { config_klass.new.settings.amount = 22 }.to raise_error(Qonfig::ValidationError)
637
+ expect { config_klass.new.settings.adapter = :resque }.to raise_error(Qonfig::ValidationError)
638
+ expect { config_klass.new.settings.switcher = 'off' }.to raise_error(Qonfig::ValidationError)
639
+ expect { config_klass.new.settings.data = {} }.to raise_error(Qonfig::ValidationError)
640
+ expect { config_klass.new.settings.mappings = [] }.to raise_error(Qonfig::ValidationError)
641
+
642
+ # NOTE: non-strict values (validation ignores nil values)
643
+ expect { config_klass.new.settings.age = nil }.not_to raise_error
644
+ expect { config_klass.new.settings.enabled = nil }.not_to raise_error
645
+
646
+ # NOTE: valid values
647
+ expect { config_klass.new.settings.enabled = true }.not_to raise_error
648
+ expect { config_klass.new.settings.count = 5 }.not_to raise_error
649
+ expect { config_klass.new.settings.amount = 22.0 }.not_to raise_error
650
+ expect { config_klass.new.settings.adapter = 'resque' }.not_to raise_error
651
+ expect { config_klass.new.settings.switcher = :off }.not_to raise_error
652
+ expect { config_klass.new.settings.data = [] }.not_to raise_error
653
+ expect { config_klass.new.settings.mappings = {} }.not_to raise_error
654
+ expect { config_klass.new.settings.age = 20.1 }.not_to raise_error
655
+ end
656
+
657
+ describe 'valid_with?' do
658
+ specify '(#valid_with?): config object is valid or invalid with new potenital configs' do
659
+ config = Qonfig::DataSet.build do
660
+ setting :enabled, false
661
+ setting :count, 123
662
+ setting :amount, 23.55
663
+ setting :level, 100_500
664
+ setting :sound do
665
+ setting :genre, 'death-metal'
666
+ end
667
+
668
+ validate :enabled, :boolean
669
+ validate :count, :integer, strict: true
670
+
671
+ validate by: :check_level
672
+ validate 'sound.#' do |value|
673
+ value.is_a?(String)
674
+ end
675
+
676
+ def check_level
677
+ settings.level.is_a?(Numeric)
678
+ end
679
+ end
680
+
681
+ expect(config.valid_with?(
682
+ enabled: true,
683
+ count: 444,
684
+ amount: 66.21,
685
+ level: 123.55,
686
+ sound: { genre: 'melodic-death-metal' }
687
+ )).to eq(true)
688
+
689
+ expect(config.valid_with?(enabled: '123')).to eq(false)
690
+ expect(config.valid_with?(sound: { genre: false })).to eq(false)
691
+ expect(config.valid_with?(level: '2')).to eq(false)
692
+ expect(config.valid_with?(count: nil)).to eq(false)
693
+ expect(config.valid_with?(level: '7', count: nil, enabled: 123)).to eq(false)
694
+
695
+ # NOTE: check that original config does not mutated after validation checking
696
+ expect(config.settings.enabled).to eq(false)
697
+ expect(config.settings.count).to eq(123)
698
+ expect(config.settings.amount).to eq(23.55)
699
+ expect(config.settings.level).to eq(100_500)
700
+ expect(config.settings.sound.genre).to eq('death-metal')
701
+ end
702
+ end
703
+
704
+ specify '(.valid_with?/#valid_with?) support for do-config notation :)' do
705
+ config_klass = Class.new(Qonfig::DataSet) do
706
+ setting :enabled, false
707
+ setting(:db) { setting :user, 'D@iVeR' }
708
+ validate :enabled, :boolean, strict: true
709
+ validate 'db.#', :text, strict: true
710
+ end
711
+
712
+ config = config_klass.new
713
+
714
+ # class-level checker
715
+ expect(
716
+ (config_klass.valid_with?(enabled: true) do |conf|
717
+ conf.db.user = '0exp'
718
+ end)
719
+ ).to eq(true)
720
+ expect(
721
+ (config_klass.valid_with?(enabled: false) do |conf|
722
+ conf.db.user = 123
723
+ end)
724
+ ).to eq(false)
725
+ expect(
726
+ (config_klass.valid_with?(enabled: nil) do |conf|
727
+ conf.db.user = 'test'
728
+ end)
729
+ ).to eq(false)
730
+
731
+ # instance-level checker
732
+ expect(
733
+ (config.valid_with?(enabled: true) do |conf|
734
+ conf.db.user = '0exp'
735
+ end)
736
+ ).to eq(true)
737
+ expect(
738
+ (config.valid_with?(enabled: false) do |conf|
739
+ conf.db.user = 123
740
+ end)
741
+ ).to eq(false)
742
+ expect(
743
+ (config.valid_with?(enabled: nil) do |conf|
744
+ conf.db.user = 'test'
745
+ end)
746
+ ).to eq(false)
747
+
748
+ # original instance is not changed
749
+ expect(config.settings.enabled).to eq(false)
750
+ expect(config.settings.db.user).to eq('D@iVeR')
751
+ end
752
+
753
+ specify '(.valid_with?) potential config instances will be valid or invalid with new configs' do
754
+ config_klass = Class.new(Qonfig::DataSet) do
755
+ setting :enabled, false
756
+ setting :count, 123
757
+ setting :amount, 23.55
758
+ setting :level, 100_500
759
+ setting :sound do
760
+ setting :genre, 'death-metal'
761
+ end
762
+
763
+ validate :enabled, :boolean
764
+ validate :count, :integer, strict: true
765
+
766
+ validate by: :check_level
767
+ validate 'sound.#' do |value|
768
+ value.is_a?(String)
769
+ end
770
+
771
+ def check_level
772
+ settings.level.is_a?(Numeric)
773
+ end
774
+ end
775
+
776
+ expect(config_klass.valid_with?(
777
+ enabled: true,
778
+ count: 444,
779
+ amount: 66.21,
780
+ level: 123.55,
781
+ sound: { genre: 'melodic-death-metal' }
782
+ )).to eq(true)
783
+
784
+ expect(config_klass.valid_with?(enabled: '123')).to eq(false)
785
+ expect(config_klass.valid_with?(sound: { genre: false })).to eq(false)
786
+ expect(config_klass.valid_with?(level: '2')).to eq(false)
787
+ expect(config_klass.valid_with?(count: nil)).to eq(false)
788
+ expect(config_klass.valid_with?(level: '7', count: nil, enabled: 123)).to eq(false)
789
+ end
790
+ end
791
+
792
+ describe 'custom validators' do
793
+ specify 'you can define your own global validators and concrete-class-related validators' do
794
+ # custom global validator
795
+ Qonfig::DataSet.define_validator(:globality) do |value|
796
+ value == 'global'
797
+ end
798
+
799
+ config_klass = Class.new(Qonfig::DataSet) do
800
+ # custom class-related validator
801
+ define_validator(:int_or_sym) do |value|
802
+ value.is_a?(Integer) || value.is_a?(Symbol)
803
+ end
804
+
805
+ # custom class-related validator
806
+ define_validator(:queue_adapter) do |value|
807
+ value == :que || value == :sidekiq
808
+ end
809
+
810
+ setting :some_value, 123
811
+ setting :adapter, :sidekiq
812
+ setting :global, 'global'
813
+
814
+ validate :some_value, :int_or_sym # class-related validator
815
+ validate :adapter, :queue_adapter # class-related validator
816
+ validate :global, :globality, strict: true # global validator + srict cheker
817
+ end
818
+
819
+ expect { config_klass.new }.not_to raise_error
820
+
821
+ # invalid :global setting values
822
+ expect { config_klass.new(global: 123) }.to raise_error(Qonfig::ValidationError)
823
+ expect { config_klass.new(global: nil) }.to raise_error(Qonfig::ValidationError)
824
+ # invalid :some_value setting value
825
+ expect { config_klass.new(some_value: 123.456) }.to raise_error(Qonfig::ValidationError)
826
+ # invalid :adaper setting value
827
+ expect { config_klass.new(adapter: :sneakers) }.to raise_error(Qonfig::ValidationError)
828
+ end
829
+
830
+ specify 'fails when validation logic is not provided at definition step' do
831
+ config_klass = Class.new(Qonfig::DataSet)
832
+
833
+ expect do
834
+ config_klass.define_validator(:simple_validator)
835
+ end.to raise_error(Qonfig::ValidatorArgumentError)
836
+
837
+ expect do
838
+ config_klass.define_validator(:simple_validator) {}
839
+ end.not_to raise_error
840
+ end
841
+
842
+ specify 'inheritance works as expeced' do
843
+ Qonfig::DataSet.define_validator(:global_inheritable) do |value|
844
+ value == 0
845
+ end
846
+
847
+ first_klass = Class.new(Qonfig::DataSet) do
848
+ define_validator(:first_validator) { |value| value == 1 }
849
+ setting :a
850
+ end
851
+
852
+ second_klass = Class.new(first_klass) do
853
+ define_validator(:second_validator) { |value| value == 2 }
854
+ setting :b
855
+ end
856
+
857
+ third_klass = Class.new(second_klass) do
858
+ define_validator(:third_validator) { |value| value == 3 }
859
+ setting :c
860
+ setting :d
861
+
862
+ validate :a, :first_validator
863
+ validate :b, :second_validator
864
+ validate :c, :third_validator
865
+ validate :d, :global_inheritable
866
+ end
867
+
868
+ expect { third_klass.new(a: 1, b: 2, c: 3, d: 0) }.not_to raise_error
869
+ expect { third_klass.new }.not_to raise_error
870
+
871
+ expect { third_klass.new(a: '1') }.to raise_error(Qonfig::ValidationError)
872
+ expect { third_klass.new(b: '2') }.to raise_error(Qonfig::ValidationError)
873
+ expect { third_klass.new(c: '3') }.to raise_error(Qonfig::ValidationError)
874
+ expect { third_klass.new(d: '0') }.to raise_error(Qonfig::ValidationError)
875
+ end
876
+
877
+ specify 'predefined validators can be redefined' do
878
+ # custom global predefined validator
879
+ Qonfig::DataSet.define_validator(:global_predefined) do |value|
880
+ value == :predefined
881
+ end
882
+
883
+ config_klass_with_redefinition = Class.new(Qonfig::DataSet) do
884
+ define_validator(:global_predefined) do |value|
885
+ value == :class_level_predefined
886
+ end
887
+
888
+ setting :some_option
889
+ validate :some_option, :global_predefined
890
+ end
891
+
892
+ config_klass_without_redefinition = Class.new(Qonfig::DataSet) do
893
+ setting :some_option
894
+ validate :some_option, :global_predefined
895
+ end
896
+
897
+ # NOTE: config with redefined validator
898
+ expect do
899
+ # check that redefined validator is used
900
+ config_klass_with_redefinition.new(some_option: :class_level_predefined)
901
+ end.not_to raise_error
902
+ expect do
903
+ # check that global validator is not reached
904
+ config_klass_with_redefinition.new(some_option: :predefined)
905
+ end.to raise_error(Qonfig::ValidationError)
906
+
907
+ # NOTE: config without redefined validator
908
+ expect do
909
+ config_klass_without_redefinition.new(some_option: :class_level_predefined)
910
+ end.to raise_error(Qonfig::ValidationError)
911
+ expect do
912
+ config_klass_without_redefinition.new(some_option: :predefined)
913
+ end.not_to raise_error
914
+ end
915
+ end
916
+ end