qonfig 0.10.0 → 0.11.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +1 -1
  4. data/.rubocop.yml +1 -1
  5. data/.travis.yml +3 -3
  6. data/CHANGELOG.md +10 -0
  7. data/README.md +146 -1
  8. data/Rakefile +2 -0
  9. data/lib/qonfig/command_set.rb +53 -55
  10. data/lib/qonfig/commands/add_nested_option.rb +36 -40
  11. data/lib/qonfig/commands/add_option.rb +33 -37
  12. data/lib/qonfig/commands/base.rb +9 -13
  13. data/lib/qonfig/commands/compose.rb +29 -33
  14. data/lib/qonfig/commands/expose_yaml.rb +154 -158
  15. data/lib/qonfig/commands/load_from_env.rb +77 -79
  16. data/lib/qonfig/commands/load_from_json.rb +52 -56
  17. data/lib/qonfig/commands/load_from_self.rb +57 -61
  18. data/lib/qonfig/commands/load_from_yaml.rb +54 -58
  19. data/lib/qonfig/commands.rb +15 -0
  20. data/lib/qonfig/configurable.rb +88 -90
  21. data/lib/qonfig/data_set/class_builder.rb +17 -21
  22. data/lib/qonfig/data_set.rb +186 -138
  23. data/lib/qonfig/dsl.rb +106 -108
  24. data/lib/qonfig/{error.rb → exceptions.rb} +13 -1
  25. data/lib/qonfig/loaders/basic.rb +30 -32
  26. data/lib/qonfig/loaders/json.rb +16 -23
  27. data/lib/qonfig/loaders/yaml.rb +16 -23
  28. data/lib/qonfig/loaders.rb +9 -0
  29. data/lib/qonfig/plugins/abstract.rb +7 -11
  30. data/lib/qonfig/plugins/access_mixin.rb +21 -25
  31. data/lib/qonfig/plugins/registry.rb +120 -124
  32. data/lib/qonfig/plugins.rb +56 -54
  33. data/lib/qonfig/settings/builder.rb +10 -14
  34. data/lib/qonfig/settings/key_guard.rb +60 -64
  35. data/lib/qonfig/settings/lock.rb +53 -57
  36. data/lib/qonfig/settings.rb +392 -354
  37. data/lib/qonfig/uploaders/base.rb +18 -0
  38. data/lib/qonfig/uploaders/file.rb +55 -0
  39. data/lib/qonfig/uploaders/json.rb +35 -0
  40. data/lib/qonfig/uploaders/yaml.rb +93 -0
  41. data/lib/qonfig/uploaders.rb +10 -0
  42. data/lib/qonfig/version.rb +1 -1
  43. data/lib/qonfig.rb +4 -21
  44. data/qonfig.gemspec +1 -1
  45. metadata +13 -6
@@ -1,419 +1,457 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Qonfig
4
- # rubocop:disable Metrics/ClassLength
3
+ # @api private
4
+ # @since 0.1.0
5
+ # rubocop:disable Metrics/ClassLength
6
+ class Qonfig::Settings
7
+ require_relative 'settings/lock'
8
+ require_relative 'settings/builder'
9
+ require_relative 'settings/key_guard'
10
+
11
+ # @return [Proc]
12
+ #
13
+ # @api private
14
+ # @since 0.11.0
15
+ BASIC_SETTING_KEY_TRANSFORMER = (proc { |value| value }).freeze
16
+
17
+ # @return [Proc]
18
+ #
19
+ # @api private
20
+ # @since 0.11.0
21
+ BASIC_SETTING_VALUE_TRANSFORMER = (proc { |value| value }).freeze
5
22
 
23
+ # @return [Hash]
24
+ #
6
25
  # @api private
7
26
  # @since 0.1.0
8
- class Settings
9
- # @return [Hash]
10
- #
11
- # @api private
12
- # @since 0.1.0
13
- attr_reader :__options__
14
-
15
- # @api private
16
- # @since 0.1.0
17
- def initialize
18
- @__options__ = {}
19
- @__lock__ = Lock.new
20
- end
27
+ attr_reader :__options__
21
28
 
22
- # @param key [Symbol, String]
23
- # @param value [Object]
24
- # @return [void]
25
- #
26
- # @api private
27
- # @since 0.1.0
28
- def __define_setting__(key, value)
29
- __lock__.thread_safe_definition do
30
- key = __indifferently_accessable_option_key__(key)
31
-
32
- __prevent_core_method_intersection__(key)
33
-
34
- case
35
- when !__options__.key?(key)
36
- __options__[key] = value
37
- when __options__[key].is_a?(Qonfig::Settings) && value.is_a?(Qonfig::Settings)
38
- __options__[key].__append_settings__(value)
39
- else
40
- __options__[key] = value
41
- end
42
-
43
- __define_accessor__(key)
44
- end
45
- end
29
+ # @api private
30
+ # @since 0.1.0
31
+ def initialize
32
+ @__options__ = {}
33
+ @__lock__ = Lock.new
34
+ end
35
+
36
+ # @param key [Symbol, String]
37
+ # @param value [Object]
38
+ # @return [void]
39
+ #
40
+ # @api private
41
+ # @since 0.1.0
42
+ def __define_setting__(key, value)
43
+ __lock__.thread_safe_definition do
44
+ key = __indifferently_accessable_option_key__(key)
45
+
46
+ __prevent_core_method_intersection__(key)
46
47
 
47
- # @param settings [Qonfig::Settings]
48
- # @return [void]
49
- #
50
- # @api private
51
- # @since 0.1.0
52
- def __append_settings__(settings)
53
- __lock__.thread_safe_merge do
54
- settings.__options__.each_pair do |key, value|
55
- __define_setting__(key, value)
56
- end
48
+ case
49
+ when !__options__.key?(key)
50
+ __options__[key] = value
51
+ when __options__[key].is_a?(Qonfig::Settings) && value.is_a?(Qonfig::Settings)
52
+ __options__[key].__append_settings__(value)
53
+ else
54
+ __options__[key] = value
57
55
  end
58
- end
59
56
 
60
- # @param key [Symbol, String]
61
- # @return [Object]
62
- #
63
- # @api public
64
- # @since 0.1.0
65
- def [](key)
66
- __lock__.thread_safe_access { __get_value__(key) }
57
+ __define_accessor__(key)
67
58
  end
59
+ end
68
60
 
69
- # @param key [String, Symbol]
70
- # @param value [Object]
71
- # @return [void]
72
- #
73
- # @api public
74
- # @since 0.1.0
75
- def []=(key, value)
76
- __lock__.thread_safe_access { __set_value__(key, value) }
61
+ # @param settings [Qonfig::Settings]
62
+ # @return [void]
63
+ #
64
+ # @api private
65
+ # @since 0.1.0
66
+ def __append_settings__(settings)
67
+ __lock__.thread_safe_merge do
68
+ settings.__options__.each_pair do |key, value|
69
+ __define_setting__(key, value)
70
+ end
77
71
  end
72
+ end
78
73
 
79
- # @param options_map [Hash]
80
- # @return [void]
81
- #
82
- # @api private
83
- # @since 0.3.0
84
- def __apply_values__(options_map)
85
- __lock__.thread_safe_access { __set_values_from_map__(options_map) }
86
- end
74
+ # @param key [Symbol, String]
75
+ # @return [Object]
76
+ #
77
+ # @api public
78
+ # @since 0.1.0
79
+ def [](key)
80
+ __lock__.thread_safe_access { __get_value__(key) }
81
+ end
87
82
 
88
- # @param keys [Array<String, Symbol>]
89
- # @return [Object]
90
- #
91
- # @api private
92
- # @since 0.2.0
93
- def __dig__(*keys)
94
- __lock__.thread_safe_access { __deep_access__(*keys) }
95
- end
83
+ # @param key [String, Symbol]
84
+ # @param value [Object]
85
+ # @return [void]
86
+ #
87
+ # @api public
88
+ # @since 0.1.0
89
+ def []=(key, value)
90
+ __lock__.thread_safe_access { __set_value__(key, value) }
91
+ end
96
92
 
97
- # @param keys [Array<String, Symbol>]
98
- # @return [Hash]
99
- #
100
- # @api private
101
- # @since 0.9.0
102
- def __slice__(*keys)
103
- __lock__.thread_safe_access { __deep_slice__(*keys) }
104
- end
93
+ # @param options_map [Hash]
94
+ # @return [void]
95
+ #
96
+ # @api private
97
+ # @since 0.3.0
98
+ def __apply_values__(options_map)
99
+ __lock__.thread_safe_access { __set_values_from_map__(options_map) }
100
+ end
105
101
 
106
- # @param keys [Array<String, Symbol>]
107
- # @return [Hash, Any]
108
- #
109
- # @api private
110
- # @since 0.10.0
111
- def __slice_value__(*keys)
112
- __lock__.thread_safe_access { __deep_slice_value__(*keys) }
113
- end
102
+ # @param keys [Array<String, Symbol>]
103
+ # @return [Object]
104
+ #
105
+ # @api private
106
+ # @since 0.2.0
107
+ def __dig__(*keys)
108
+ __lock__.thread_safe_access { __deep_access__(*keys) }
109
+ end
114
110
 
115
- # @return [Hash]
116
- #
117
- # @api private
118
- # @since 0.1.0
119
- def __to_hash__
120
- __lock__.thread_safe_access { __build_hash_representation__ }
121
- end
122
- alias_method :__to_h__, :__to_hash__
123
-
124
- # @return [void]
125
- #
126
- # @api private
127
- # @since 0.2.0
128
- def __clear__
129
- __lock__.thread_safe_access { __clear_option_values__ }
130
- end
111
+ # @param keys [Array<String, Symbol>]
112
+ # @return [Hash]
113
+ #
114
+ # @api private
115
+ # @since 0.9.0
116
+ def __slice__(*keys)
117
+ __lock__.thread_safe_access { __deep_slice__(*keys) }
118
+ end
131
119
 
132
- # @param method_name [String, Symbol]
133
- # @param arguments [Array<Object>]
134
- # @param block [Proc]
135
- # @return [void]
136
- #
137
- # @raise [Qonfig::UnknownSettingError]
138
- #
139
- # @api private
140
- # @since 0.1.0
141
- def method_missing(method_name, *arguments, &block)
142
- super
143
- rescue NoMethodError
144
- ::Kernel.raise(Qonfig::UnknownSettingError, "Setting with <#{method_name}> key doesnt exist!")
145
- end
120
+ # @param keys [Array<String, Symbol>]
121
+ # @return [Hash, Any]
122
+ #
123
+ # @api private
124
+ # @since 0.10.0
125
+ def __slice_value__(*keys)
126
+ __lock__.thread_safe_access { __deep_slice_value__(*keys) }
127
+ end
146
128
 
147
- # @return [Boolean]
148
- #
149
- # @api private
150
- # @since 0.1.0
151
- def respond_to_missing?(method_name, include_private = false)
152
- # :nocov:
153
- __options__.key?(method_name.to_s) || __options__.key?(method_name.to_sym) || super
154
- # :nocov:
129
+ # @option transform_key [Proc]
130
+ # @option transform_value [Proc]
131
+ # @return [Hash]
132
+ #
133
+ # @api private
134
+ # @since 0.1.0
135
+ # rubocop:disable Metrics/LineLength
136
+ def __to_hash__(transform_key: BASIC_SETTING_KEY_TRANSFORMER, transform_value: BASIC_SETTING_VALUE_TRANSFORMER)
137
+ unless transform_key.is_a?(Proc)
138
+ ::Kernel.raise(Qonfig::IncorrectKeyTransformerError, 'Key transformer should be a proc')
155
139
  end
156
140
 
157
- # @return [void]
158
- #
159
- # @api private
160
- # @since 0.1.0
161
- def __freeze__
162
- __lock__.thread_safe_access do
163
- __options__.freeze
164
-
165
- __options__.each_value do |value|
166
- value.__freeze__ if value.is_a?(Qonfig::Settings)
167
- end
168
- end
141
+ unless transform_value.is_a?(Proc)
142
+ ::Kernel.raise(Qonfig::IncorrectValueTransformerError, 'Value transformer should be a proc')
169
143
  end
170
144
 
171
- # @return [Boolean]
172
- #
173
- # @api private
174
- # @since 0.2.0
175
- def __is_frozen__
176
- __lock__.thread_safe_access { __options__.frozen? }
145
+ __lock__.thread_safe_access do
146
+ __build_hash_representation__(transform_key: transform_key, transform_value: transform_value)
177
147
  end
148
+ end
149
+ # rubocop:enable Metrics/LineLength
150
+ alias_method :__to_h__, :__to_hash__
178
151
 
179
- private
180
-
181
- # @return [Qonfig::Settings::Lock]
182
- #
183
- # @api private
184
- # @since 0.2.0
185
- attr_reader :__lock__
186
-
187
- # @param options_map [Hash]
188
- # @return [void]
189
- #
190
- # @raise [Qonfig::ArgumentError]
191
- # @raise [Qonfig::AmbiguousSettingValueError]
192
- #
193
- # @api private
194
- # @since 0.3.0
195
- def __set_values_from_map__(options_map)
196
- ::Kernel.raise(
197
- Qonfig::ArgumentError, 'Options map should be represented as a hash'
198
- ) unless options_map.is_a?(Hash)
199
-
200
- options_map.each_pair do |key, value|
201
- current_value = __get_value__(key)
202
-
203
- # NOTE: some duplications here was made only for the better code readability
204
- case
205
- when !current_value.is_a?(Qonfig::Settings)
206
- __set_value__(key, value)
207
- when current_value.is_a?(Qonfig::Settings) && value.is_a?(Hash)
208
- current_value.__apply_values__(value)
209
- when current_value.is_a?(Qonfig::Settings) && !value.is_a?(Hash)
210
- ::Kernel.raise(
211
- Qonfig::AmbiguousSettingValueError,
212
- "Can not redefine option <#{key}> that contains nested options"
213
- )
214
- end
215
- end
216
- end
152
+ # @return [void]
153
+ #
154
+ # @api private
155
+ # @since 0.2.0
156
+ def __clear__
157
+ __lock__.thread_safe_access { __clear_option_values__ }
158
+ end
217
159
 
218
- # @return [void]
219
- #
220
- # @raise [Qonfig::FrozenSettingsError]
221
- #
222
- # @api private
223
- # @since 0.2.0
224
- def __clear_option_values__
225
- ::Kernel.raise(
226
- Qonfig::FrozenSettingsError, 'Can not modify frozen settings'
227
- ) if __options__.frozen?
228
-
229
- __options__.each_pair do |key, value|
230
- if value.is_a?(Qonfig::Settings)
231
- value.__clear__
232
- else
233
- __options__[key] = nil
234
- end
235
- end
236
- end
160
+ # @param method_name [String, Symbol]
161
+ # @param arguments [Array<Object>]
162
+ # @param block [Proc]
163
+ # @return [void]
164
+ #
165
+ # @raise [Qonfig::UnknownSettingError]
166
+ #
167
+ # @api private
168
+ # @since 0.1.0
169
+ def method_missing(method_name, *arguments, &block)
170
+ super
171
+ rescue NoMethodError
172
+ ::Kernel.raise(Qonfig::UnknownSettingError, "Setting with <#{method_name}> key doesnt exist!")
173
+ end
237
174
 
238
- # @param key [String, Symbol]
239
- # @return [Object]
240
- #
241
- # @raise [Qonfig::UnknownSettingError]
242
- #
243
- # @api private
244
- # @since 0.2.0
245
- def __get_value__(key)
246
- key = __indifferently_accessable_option_key__(key)
175
+ # @return [Boolean]
176
+ #
177
+ # @api private
178
+ # @since 0.1.0
179
+ def respond_to_missing?(method_name, include_private = false)
180
+ # :nocov:
181
+ __options__.key?(method_name.to_s) || __options__.key?(method_name.to_sym) || super
182
+ # :nocov:
183
+ end
247
184
 
248
- unless __options__.key?(key)
249
- ::Kernel.raise(Qonfig::UnknownSettingError, "Setting with <#{key}> key does not exist!")
250
- end
185
+ # @return [void]
186
+ #
187
+ # @api private
188
+ # @since 0.1.0
189
+ def __freeze__
190
+ __lock__.thread_safe_access do
191
+ __options__.freeze
251
192
 
252
- __options__[key]
193
+ __options__.each_value do |value|
194
+ value.__freeze__ if value.is_a?(Qonfig::Settings)
195
+ end
253
196
  end
197
+ end
254
198
 
255
- # @param key [String, Symbol]
256
- # @param value [Object]
257
- # @return [void]
258
- #
259
- # @raise [Qonfig::UnknownSettingError]
260
- # @raise [Qonfig::FrozenSettingsError]
261
- # @raise [Qonfig::AmbiguousSettingValueError]
262
- #
263
- # @api private
264
- # @since 0.2.0
265
- def __set_value__(key, value)
266
- key = __indifferently_accessable_option_key__(key)
199
+ # @return [Boolean]
200
+ #
201
+ # @api private
202
+ # @since 0.2.0
203
+ def __is_frozen__
204
+ __lock__.thread_safe_access { __options__.frozen? }
205
+ end
267
206
 
268
- unless __options__.key?(key)
269
- ::Kernel.raise(Qonfig::UnknownSettingError, "Setting with <#{key}> key does not exist!")
270
- end
207
+ private
271
208
 
272
- if __options__.frozen?
273
- ::Kernel.raise(Qonfig::FrozenSettingsError, 'Can not modify frozen settings')
274
- end
209
+ # @return [Qonfig::Settings::Lock]
210
+ #
211
+ # @api private
212
+ # @since 0.2.0
213
+ attr_reader :__lock__
214
+
215
+ # @param options_map [Hash]
216
+ # @return [void]
217
+ #
218
+ # @raise [Qonfig::ArgumentError]
219
+ # @raise [Qonfig::AmbiguousSettingValueError]
220
+ #
221
+ # @api private
222
+ # @since 0.3.0
223
+ def __set_values_from_map__(options_map)
224
+ ::Kernel.raise(
225
+ Qonfig::ArgumentError, 'Options map should be represented as a hash'
226
+ ) unless options_map.is_a?(Hash)
275
227
 
276
- if __options__[key].is_a?(Qonfig::Settings)
228
+ options_map.each_pair do |key, value|
229
+ current_value = __get_value__(key)
230
+
231
+ # NOTE: some duplications here was made only for the better code readability
232
+ case
233
+ when !current_value.is_a?(Qonfig::Settings)
234
+ __set_value__(key, value)
235
+ when current_value.is_a?(Qonfig::Settings) && value.is_a?(Hash)
236
+ current_value.__apply_values__(value)
237
+ when current_value.is_a?(Qonfig::Settings) && !value.is_a?(Hash)
277
238
  ::Kernel.raise(
278
239
  Qonfig::AmbiguousSettingValueError,
279
240
  "Can not redefine option <#{key}> that contains nested options"
280
241
  )
281
242
  end
243
+ end
244
+ end
245
+
246
+ # @return [void]
247
+ #
248
+ # @raise [Qonfig::FrozenSettingsError]
249
+ #
250
+ # @api private
251
+ # @since 0.2.0
252
+ def __clear_option_values__
253
+ ::Kernel.raise(
254
+ Qonfig::FrozenSettingsError, 'Can not modify frozen settings'
255
+ ) if __options__.frozen?
256
+
257
+ __options__.each_pair do |key, value|
258
+ if value.is_a?(Qonfig::Settings)
259
+ value.__clear__
260
+ else
261
+ __options__[key] = nil
262
+ end
263
+ end
264
+ end
282
265
 
283
- __options__[key] = value
266
+ # @param key [String, Symbol]
267
+ # @return [Object]
268
+ #
269
+ # @raise [Qonfig::UnknownSettingError]
270
+ #
271
+ # @api private
272
+ # @since 0.2.0
273
+ def __get_value__(key)
274
+ key = __indifferently_accessable_option_key__(key)
275
+
276
+ unless __options__.key?(key)
277
+ ::Kernel.raise(Qonfig::UnknownSettingError, "Setting with <#{key}> key does not exist!")
284
278
  end
285
279
 
286
- # @param keys [Array<Symbol, String>]
287
- # @return [Object]
288
- #
289
- # @raise [Qonfig::ArgumentError]
290
- # @raise [Qonfig::UnknownSettingError]
291
- #
292
- # @api private
293
- # @since 0.2.0
294
- def __deep_access__(*keys)
295
- ::Kernel.raise(Qonfig::ArgumentError, 'Key list can not be empty') if keys.empty?
280
+ __options__[key]
281
+ end
296
282
 
297
- result = __get_value__(keys.first)
298
- rest_keys = Array(keys[1..-1])
283
+ # @param key [String, Symbol]
284
+ # @param value [Object]
285
+ # @return [void]
286
+ #
287
+ # @raise [Qonfig::UnknownSettingError]
288
+ # @raise [Qonfig::FrozenSettingsError]
289
+ # @raise [Qonfig::AmbiguousSettingValueError]
290
+ #
291
+ # @api private
292
+ # @since 0.2.0
293
+ def __set_value__(key, value)
294
+ key = __indifferently_accessable_option_key__(key)
299
295
 
300
- case
301
- when rest_keys.empty?
302
- result
303
- when !result.is_a?(Qonfig::Settings)
304
- ::Kernel.raise(
305
- Qonfig::UnknownSettingError,
306
- 'Setting with required digging sequence does not exist!'
307
- )
308
- when result.is_a?(Qonfig::Settings)
309
- result.__dig__(*rest_keys)
310
- end
296
+ unless __options__.key?(key)
297
+ ::Kernel.raise(Qonfig::UnknownSettingError, "Setting with <#{key}> key does not exist!")
311
298
  end
312
299
 
313
- # @param keys [Array<Symbol, String>]
314
- # @return [Hash]
315
- #
316
- # @raise [Qonfig::ArgumentError]
317
- # @raise [Qonfig::UnknownSettingError]
318
- #
319
- # @api private
320
- # @since 0.9.0
321
- def __deep_slice__(*keys)
322
- {}.tap do |result|
323
- __deep_access__(*keys).tap do |setting|
324
- required_key = __indifferently_accessable_option_key__(keys.last)
325
- result[required_key] = setting.is_a?(Qonfig::Settings) ? setting.__to_h__ : setting
326
- end
327
- end
300
+ if __options__.frozen?
301
+ ::Kernel.raise(Qonfig::FrozenSettingsError, 'Can not modify frozen settings')
328
302
  end
329
303
 
330
- # @param keys [Array<Symbol, String>]
331
- # @return [Hash]
332
- #
333
- # @raise [Qonfig::ArgumentError]
334
- # @raise [Qonfig::UnknownSettingError]
335
- #
336
- # @api private
337
- # @since 0.1.0
338
- def __deep_slice_value__(*keys)
339
- required_key = __indifferently_accessable_option_key__(keys.last)
340
- __deep_slice__(*keys)[required_key]
304
+ if __options__[key].is_a?(Qonfig::Settings)
305
+ ::Kernel.raise(
306
+ Qonfig::AmbiguousSettingValueError,
307
+ "Can not redefine option <#{key}> that contains nested options"
308
+ )
341
309
  end
342
310
 
343
- # @param options_part [Hash]
344
- # @return [Hash]
345
- #
346
- # @api private
347
- # @since 0.2.0
348
- def __build_hash_representation__(options_part = __options__)
349
- options_part.each_with_object({}) do |(key, value), hash|
350
- case
351
- when value.is_a?(Hash)
352
- hash[key] = __build_hash_representation__(value)
353
- when value.is_a?(Qonfig::Settings)
354
- hash[key] = value.__to_hash__
355
- else
356
- hash[key] = value
357
- end
358
- end
311
+ __options__[key] = value
312
+ end
313
+
314
+ # @param keys [Array<Symbol, String>]
315
+ # @return [Object]
316
+ #
317
+ # @raise [Qonfig::ArgumentError]
318
+ # @raise [Qonfig::UnknownSettingError]
319
+ #
320
+ # @api private
321
+ # @since 0.2.0
322
+ def __deep_access__(*keys)
323
+ ::Kernel.raise(Qonfig::ArgumentError, 'Key list can not be empty') if keys.empty?
324
+
325
+ result = __get_value__(keys.first)
326
+ rest_keys = Array(keys[1..-1])
327
+
328
+ case
329
+ when rest_keys.empty?
330
+ result
331
+ when !result.is_a?(Qonfig::Settings)
332
+ ::Kernel.raise(
333
+ Qonfig::UnknownSettingError,
334
+ 'Setting with required digging sequence does not exist!'
335
+ )
336
+ when result.is_a?(Qonfig::Settings)
337
+ result.__dig__(*rest_keys)
359
338
  end
339
+ end
360
340
 
361
- # @param key [Symbol, String]
362
- # @return [void]
363
- #
364
- # @api private
365
- # @since 0.1.0
366
- def __define_accessor__(key)
367
- define_singleton_method(key) do
368
- self.[](key)
341
+ # @param keys [Array<Symbol, String>]
342
+ # @return [Hash]
343
+ #
344
+ # @raise [Qonfig::ArgumentError]
345
+ # @raise [Qonfig::UnknownSettingError]
346
+ #
347
+ # @api private
348
+ # @since 0.9.0
349
+ def __deep_slice__(*keys)
350
+ {}.tap do |result|
351
+ __deep_access__(*keys).tap do |setting|
352
+ required_key = __indifferently_accessable_option_key__(keys.last)
353
+ result[required_key] = setting.is_a?(Qonfig::Settings) ? setting.__to_h__ : setting
369
354
  end
355
+ end
356
+ end
370
357
 
371
- define_singleton_method("#{key}=") do |value|
372
- self.[]=(key, value)
373
- end
358
+ # @param keys [Array<Symbol, String>]
359
+ # @return [Hash]
360
+ #
361
+ # @raise [Qonfig::ArgumentError]
362
+ # @raise [Qonfig::UnknownSettingError]
363
+ #
364
+ # @api private
365
+ # @since 0.1.0
366
+ def __deep_slice_value__(*keys)
367
+ required_key = __indifferently_accessable_option_key__(keys.last)
368
+ __deep_slice__(*keys)[required_key]
369
+ end
370
+
371
+ # @param options_part [Hash]
372
+ # @option transform_key [Proc]
373
+ # @option transform_value [Proc]
374
+ # @return [Hash]
375
+ #
376
+ # @api private
377
+ # @since 0.2.0
378
+ def __build_hash_representation__(options_part = __options__, transform_key:, transform_value:)
379
+ options_part.each_with_object({}) do |(key, value), hash|
380
+ final_key = transform_key.call(key)
374
381
 
375
- define_singleton_method("#{key}?") do
376
- !!self.[](key)
382
+ case
383
+ when value.is_a?(Hash)
384
+ hash[final_key] = __build_hash_representation__(
385
+ value,
386
+ transform_key: transform_key,
387
+ transform_value: transform_value
388
+ )
389
+ when value.is_a?(Qonfig::Settings)
390
+ hash[final_key] = value.__to_hash__(
391
+ transform_key: transform_key,
392
+ transform_value: transform_value
393
+ )
394
+ else
395
+ final_value = transform_value.call(value)
396
+ hash[final_key] = final_value
377
397
  end
378
398
  end
399
+ end
379
400
 
380
- # @param key [Symbol, String]
381
- # @return [String]
382
- #
383
- # @raise [Qonfig::ArgumentError]
384
- # @see Qonfig::Settings::KeyGuard
385
- #
386
- # @api private
387
- # @since 0.2.0
388
- def __indifferently_accessable_option_key__(key)
389
- KeyGuard.new(key).prevent_incompatible_key_type!
390
- key.to_s
401
+ # @param key [Symbol, String]
402
+ # @return [void]
403
+ #
404
+ # @api private
405
+ # @since 0.1.0
406
+ def __define_accessor__(key)
407
+ define_singleton_method(key) do
408
+ self.[](key)
391
409
  end
392
410
 
393
- # @param key [Symbol, String]
394
- # @return [void]
395
- #
396
- # @raise [Qonfig::CoreMethodIntersectionError]
397
- # @see Qonfig::Settings::KeyGuard
398
- #
399
- # @api private
400
- # @since 0.2.0
401
- def __prevent_core_method_intersection__(key)
402
- KeyGuard.new(key).prevent_core_method_intersection!
411
+ define_singleton_method("#{key}=") do |value|
412
+ self.[]=(key, value)
403
413
  end
404
414
 
405
- # rubocop:disable Layout/ClassStructure
406
- # @return [Array<String>]
407
- #
408
- # @api private
409
- # @since 0.2.0
410
- CORE_METHODS = Array(
411
- instance_methods(false) |
412
- private_instance_methods(false) |
413
- %i[super define_singleton_method self]
414
- ).map(&:to_s).freeze
415
- # rubocop:enable Layout/ClassStructure
415
+ define_singleton_method("#{key}?") do
416
+ !!self.[](key)
417
+ end
416
418
  end
417
419
 
418
- # rubocop:enable Metrics/ClassLength
420
+ # @param key [Symbol, String]
421
+ # @return [String]
422
+ #
423
+ # @raise [Qonfig::ArgumentError]
424
+ # @see Qonfig::Settings::KeyGuard
425
+ #
426
+ # @api private
427
+ # @since 0.2.0
428
+ def __indifferently_accessable_option_key__(key)
429
+ KeyGuard.new(key).prevent_incompatible_key_type!
430
+ key.to_s
431
+ end
432
+
433
+ # @param key [Symbol, String]
434
+ # @return [void]
435
+ #
436
+ # @raise [Qonfig::CoreMethodIntersectionError]
437
+ # @see Qonfig::Settings::KeyGuard
438
+ #
439
+ # @api private
440
+ # @since 0.2.0
441
+ def __prevent_core_method_intersection__(key)
442
+ KeyGuard.new(key).prevent_core_method_intersection!
443
+ end
444
+
445
+ # rubocop:disable Layout/ClassStructure
446
+ # @return [Array<String>]
447
+ #
448
+ # @api private
449
+ # @since 0.2.0
450
+ CORE_METHODS = Array(
451
+ instance_methods(false) |
452
+ private_instance_methods(false) |
453
+ %i[super define_singleton_method self]
454
+ ).map(&:to_s).freeze
455
+ # rubocop:enable Layout/ClassStructure
419
456
  end
457
+ # rubocop:enable Metrics/ClassLength