qonfig 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +24 -3
- data/CHANGELOG.md +47 -1
- data/README.md +472 -19
- data/lib/qonfig/command_set.rb +26 -4
- data/lib/qonfig/commands/add_nested_option.rb +14 -11
- data/lib/qonfig/commands/add_option.rb +8 -3
- data/lib/qonfig/commands/compose.rb +7 -0
- data/lib/qonfig/commands/load_from_env/value_converter.rb +84 -0
- data/lib/qonfig/commands/load_from_env.rb +97 -0
- data/lib/qonfig/commands/load_from_self.rb +77 -0
- data/lib/qonfig/commands/load_from_yaml.rb +62 -0
- data/lib/qonfig/configurable.rb +108 -0
- data/lib/qonfig/data_set/class_builder.rb +32 -0
- data/lib/qonfig/data_set.rb +96 -5
- data/lib/qonfig/dsl.rb +48 -7
- data/lib/qonfig/error.rb +47 -1
- data/lib/qonfig/loaders/yaml.rb +34 -0
- data/lib/qonfig/settings/builder.rb +22 -0
- data/lib/qonfig/settings/key_guard.rb +75 -0
- data/lib/qonfig/settings/lock.rb +64 -0
- data/lib/qonfig/settings.rb +215 -54
- data/lib/qonfig/version.rb +2 -2
- data/lib/qonfig.rb +13 -1
- data/qonfig.gemspec +3 -3
- metadata +18 -9
- data/lib/qonfig/settings_builder.rb +0 -20
data/lib/qonfig/settings.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Qonfig
|
4
|
+
# rubocop:disable Metrics/ClassLength
|
5
|
+
|
4
6
|
# @api private
|
5
7
|
# @since 0.1.0
|
6
8
|
class Settings
|
@@ -14,31 +16,32 @@ module Qonfig
|
|
14
16
|
# @since 0.1.0
|
15
17
|
def initialize
|
16
18
|
@__options__ = {}
|
19
|
+
@__lock__ = Lock.new
|
17
20
|
end
|
18
21
|
|
19
|
-
# @param key [Symbol,String]
|
22
|
+
# @param key [Symbol, String]
|
20
23
|
# @param value [Object]
|
21
24
|
# @return [void]
|
22
25
|
#
|
23
26
|
# @api private
|
24
27
|
# @since 0.1.0
|
25
28
|
def __define_setting__(key, value)
|
26
|
-
|
27
|
-
|
28
|
-
raise Qonfig::ArgumentError, 'Setting key should be a symbol or a string'
|
29
|
-
end
|
30
|
-
# :nocov:
|
29
|
+
__lock__.thread_safe_definition do
|
30
|
+
key = __indifferently_accessable_option_key__(key)
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
40
42
|
|
41
|
-
|
43
|
+
__define_accessor__(key)
|
44
|
+
end
|
42
45
|
end
|
43
46
|
|
44
47
|
# @param settings [Qonfig::Settings]
|
@@ -47,22 +50,20 @@ module Qonfig
|
|
47
50
|
# @api private
|
48
51
|
# @since 0.1.0
|
49
52
|
def __append_settings__(settings)
|
50
|
-
|
51
|
-
|
53
|
+
__lock__.thread_safe_merge do
|
54
|
+
settings.__options__.each_pair do |key, value|
|
55
|
+
__define_setting__(key, value)
|
56
|
+
end
|
52
57
|
end
|
53
58
|
end
|
54
59
|
|
55
|
-
# @param key [Symbol,String]
|
60
|
+
# @param key [Symbol, String]
|
56
61
|
# @return [Object]
|
57
62
|
#
|
58
63
|
# @api public
|
59
64
|
# @since 0.1.0
|
60
65
|
def [](key)
|
61
|
-
|
62
|
-
raise Qonfig::UnknownSettingError, "Setting with <#{key}> key does not exist!"
|
63
|
-
end
|
64
|
-
|
65
|
-
__options__[key]
|
66
|
+
__lock__.thread_safe_access { __get_value__(key) }
|
66
67
|
end
|
67
68
|
|
68
69
|
# @param key [String, Symbol]
|
@@ -72,27 +73,33 @@ module Qonfig
|
|
72
73
|
# @api public
|
73
74
|
# @since 0.1.0
|
74
75
|
def []=(key, value)
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
if __options__.frozen?
|
80
|
-
raise Qonfig::FrozenSettingsError, 'Can not modify frozen Settings'
|
81
|
-
end
|
76
|
+
__lock__.thread_safe_access { __set_value__(key, value) }
|
77
|
+
end
|
82
78
|
|
83
|
-
|
79
|
+
# @param keys [Array<String, Symbol>]
|
80
|
+
# @return [Object]
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
# @since 0.2.0
|
84
|
+
def __dig__(*keys)
|
85
|
+
__lock__.thread_safe_access { __deep_access__(*keys) }
|
84
86
|
end
|
85
87
|
|
86
88
|
# @return [Hash]
|
87
89
|
#
|
88
|
-
# @api
|
90
|
+
# @api private
|
89
91
|
# @since 0.1.0
|
90
92
|
def __to_hash__
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
93
|
+
__lock__.thread_safe_access { __build_hash_representation__ }
|
94
|
+
end
|
95
|
+
alias_method :__to_h__, :__to_hash__
|
96
|
+
|
97
|
+
# @return [void]
|
98
|
+
#
|
99
|
+
# @api private
|
100
|
+
# @since 0.2.0
|
101
|
+
def __clear__
|
102
|
+
__lock__.thread_safe_access { __clear_option_values__ }
|
96
103
|
end
|
97
104
|
|
98
105
|
# @param method_name [String, Symbol]
|
@@ -100,17 +107,19 @@ module Qonfig
|
|
100
107
|
# @param block [Proc]
|
101
108
|
# @return [void]
|
102
109
|
#
|
103
|
-
# @
|
110
|
+
# @raise [Qonfig::UnknownSettingError]
|
111
|
+
#
|
112
|
+
# @api private
|
104
113
|
# @since 0.1.0
|
105
114
|
def method_missing(method_name, *arguments, &block)
|
106
115
|
super
|
107
116
|
rescue NoMethodError
|
108
|
-
raise Qonfig::UnknownSettingError, "Setting with <#{method_name}>
|
117
|
+
raise Qonfig::UnknownSettingError, "Setting with <#{method_name}> key doesnt exist!"
|
109
118
|
end
|
110
119
|
|
111
120
|
# @return [Boolean]
|
112
121
|
#
|
113
|
-
# @api
|
122
|
+
# @api private
|
114
123
|
# @since 0.1.0
|
115
124
|
def respond_to_missing?(method_name, include_private = false)
|
116
125
|
# :nocov:
|
@@ -123,35 +132,187 @@ module Qonfig
|
|
123
132
|
# @api private
|
124
133
|
# @since 0.1.0
|
125
134
|
def __freeze__
|
126
|
-
|
135
|
+
__lock__.thread_safe_access do
|
136
|
+
__options__.freeze
|
127
137
|
|
128
|
-
|
129
|
-
|
138
|
+
__options__.each_value do |value|
|
139
|
+
value.__freeze__ if value.is_a?(Qonfig::Settings)
|
140
|
+
end
|
130
141
|
end
|
131
142
|
end
|
132
143
|
|
144
|
+
# @return [Boolean]
|
145
|
+
#
|
146
|
+
# @api private
|
147
|
+
# @since 0.2.0
|
148
|
+
def __is_frozen__
|
149
|
+
__lock__.thread_safe_access { __options__.frozen? }
|
150
|
+
end
|
151
|
+
|
133
152
|
private
|
134
153
|
|
135
|
-
# @
|
154
|
+
# @return [void]
|
155
|
+
#
|
156
|
+
# @api private
|
157
|
+
# @since 0.2.0
|
158
|
+
def __clear_option_values__
|
159
|
+
__options__.each_pair do |key, value|
|
160
|
+
if value.is_a?(Qonfig::Settings)
|
161
|
+
value.__clear__
|
162
|
+
else
|
163
|
+
__options__[key] = nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# @param key [String, Symbol]
|
136
169
|
# @return [Object]
|
137
170
|
#
|
171
|
+
# @raise [Qonfig::UnknownSettingError]
|
172
|
+
#
|
138
173
|
# @api private
|
139
|
-
# @since 0.
|
140
|
-
def
|
141
|
-
|
142
|
-
|
143
|
-
|
174
|
+
# @since 0.2.0
|
175
|
+
def __get_value__(key)
|
176
|
+
key = __indifferently_accessable_option_key__(key)
|
177
|
+
|
178
|
+
unless __options__.key?(key)
|
179
|
+
raise Qonfig::UnknownSettingError, "Setting with <#{key}> key does not exist!"
|
144
180
|
end
|
145
181
|
|
146
|
-
|
147
|
-
|
148
|
-
|
182
|
+
__options__[key]
|
183
|
+
end
|
184
|
+
|
185
|
+
# @param key [String, Symbol]
|
186
|
+
# @param value [Object]
|
187
|
+
# @return [void]
|
188
|
+
#
|
189
|
+
# @raise [Qonfig::UnknownSettingError]
|
190
|
+
# @raise [Qonfig::FrozenSettingsError]
|
191
|
+
# @raise [Qonfig::AmbiguousSettingValueError]
|
192
|
+
#
|
193
|
+
# @api private
|
194
|
+
# @since 0.2.0
|
195
|
+
def __set_value__(key, value)
|
196
|
+
key = __indifferently_accessable_option_key__(key)
|
197
|
+
|
198
|
+
unless __options__.key?(key)
|
199
|
+
raise Qonfig::UnknownSettingError, "Setting with <#{key}> key does not exist!"
|
200
|
+
end
|
201
|
+
|
202
|
+
if __options__.frozen?
|
203
|
+
raise Qonfig::FrozenSettingsError, 'Can not modify frozen settings'
|
204
|
+
end
|
205
|
+
|
206
|
+
if __options__[key].is_a?(Qonfig::Settings)
|
207
|
+
raise Qonfig::AmbiguousSettingValueError, 'Can not redefine option with nested options'
|
208
|
+
end
|
209
|
+
|
210
|
+
__options__[key] = value
|
211
|
+
end
|
212
|
+
|
213
|
+
# @param keys [Array<Symbol, String>]
|
214
|
+
# @option result [Object]
|
215
|
+
# @return [Object]
|
216
|
+
#
|
217
|
+
# @raise [Qonfig::ArgumentError]
|
218
|
+
# @raise [Qonfig::UnknownSettingError]
|
219
|
+
#
|
220
|
+
# @api private
|
221
|
+
# @since 0.2.0
|
222
|
+
def __deep_access__(*keys)
|
223
|
+
raise Qonfig::ArgumentError, 'Key list can not be empty' if keys.empty?
|
224
|
+
|
225
|
+
result = __get_value__(keys.first)
|
226
|
+
rest_keys = Array(keys[1..-1])
|
227
|
+
|
228
|
+
case
|
229
|
+
when rest_keys.empty?
|
230
|
+
result
|
231
|
+
when !result.is_a?(Qonfig::Settings)
|
232
|
+
raise(Qonfig::UnknownSettingError, 'Setting with required digging sequence does not exist!')
|
233
|
+
when result.is_a?(Qonfig::Settings)
|
234
|
+
result.__dig__(*rest_keys)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# @return [Qonfig::Settings::Lock]
|
239
|
+
#
|
240
|
+
# @api private
|
241
|
+
# @since 0.2.0
|
242
|
+
attr_reader :__lock__
|
243
|
+
|
244
|
+
# @param options_part [Hash]
|
245
|
+
# @return [Hash]
|
246
|
+
#
|
247
|
+
# @api private
|
248
|
+
# @since 0.2.0
|
249
|
+
def __build_hash_representation__(options_part = __options__)
|
250
|
+
options_part.each_with_object({}) do |(key, value), hash|
|
251
|
+
case
|
252
|
+
when value.is_a?(Hash)
|
253
|
+
hash[key] = __build_hash_representation__(value)
|
254
|
+
when value.is_a?(Qonfig::Settings)
|
255
|
+
hash[key] = value.__to_hash__
|
256
|
+
else
|
257
|
+
hash[key] = value
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# @param key [Symbol, String]
|
263
|
+
# @return [void]
|
264
|
+
#
|
265
|
+
# @api private
|
266
|
+
# @since 0.1.0
|
267
|
+
def __define_accessor__(key)
|
268
|
+
define_singleton_method(key) do
|
269
|
+
self.[](key)
|
149
270
|
end
|
150
271
|
|
151
|
-
define_singleton_method(key) { self.[](key) }
|
152
272
|
define_singleton_method("#{key}=") do |value|
|
153
273
|
self.[]=(key, value)
|
154
|
-
end
|
274
|
+
end
|
275
|
+
|
276
|
+
define_singleton_method("#{key}?") do
|
277
|
+
!!self.[](key)
|
278
|
+
end
|
155
279
|
end
|
280
|
+
|
281
|
+
# @param key [Symbol, String]
|
282
|
+
# @return [String]
|
283
|
+
#
|
284
|
+
# @raise [Qonfig::ArgumentError]
|
285
|
+
# @see Qonfig::Settings::KeyGuard
|
286
|
+
#
|
287
|
+
# @api private
|
288
|
+
# @since 0.2.0
|
289
|
+
def __indifferently_accessable_option_key__(key)
|
290
|
+
KeyGuard.new(key).prevent_incompatible_key_type!
|
291
|
+
key.to_s
|
292
|
+
end
|
293
|
+
|
294
|
+
# @param key [Symbol, String]
|
295
|
+
# @return [void]
|
296
|
+
#
|
297
|
+
# @raise [Qonfig::CoreMethodIntersectionError]
|
298
|
+
# @see Qonfig::Settings::KeyGuard
|
299
|
+
#
|
300
|
+
# @api private
|
301
|
+
# @since 0.2.0
|
302
|
+
def __prevent_core_method_intersection__(key)
|
303
|
+
KeyGuard.new(key).prevent_core_method_intersection!
|
304
|
+
end
|
305
|
+
|
306
|
+
# @return [Array<String>]
|
307
|
+
#
|
308
|
+
# @api private
|
309
|
+
# @since 0.2.0
|
310
|
+
CORE_METHODS = Array(
|
311
|
+
instance_methods(false) |
|
312
|
+
private_instance_methods(false) |
|
313
|
+
%i[super raise define_singleton_method]
|
314
|
+
).map(&:to_s).freeze
|
156
315
|
end
|
316
|
+
|
317
|
+
# rubocop:enable Metrics/ClassLength
|
157
318
|
end
|
data/lib/qonfig/version.rb
CHANGED
data/lib/qonfig.rb
CHANGED
@@ -1,14 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'yaml'
|
4
|
+
require 'erb'
|
5
|
+
|
3
6
|
module Qonfig
|
4
7
|
require_relative 'qonfig/error'
|
8
|
+
require_relative 'qonfig/loaders/yaml'
|
5
9
|
require_relative 'qonfig/commands/base'
|
6
10
|
require_relative 'qonfig/commands/add_option'
|
7
11
|
require_relative 'qonfig/commands/add_nested_option'
|
8
12
|
require_relative 'qonfig/commands/compose'
|
13
|
+
require_relative 'qonfig/commands/load_from_yaml'
|
14
|
+
require_relative 'qonfig/commands/load_from_self'
|
15
|
+
require_relative 'qonfig/commands/load_from_env'
|
16
|
+
require_relative 'qonfig/commands/load_from_env/value_converter'
|
9
17
|
require_relative 'qonfig/command_set'
|
10
18
|
require_relative 'qonfig/settings'
|
11
|
-
require_relative 'qonfig/
|
19
|
+
require_relative 'qonfig/settings/lock'
|
20
|
+
require_relative 'qonfig/settings/builder'
|
21
|
+
require_relative 'qonfig/settings/key_guard'
|
12
22
|
require_relative 'qonfig/dsl'
|
13
23
|
require_relative 'qonfig/data_set'
|
24
|
+
require_relative 'qonfig/data_set/class_builder'
|
25
|
+
require_relative 'qonfig/configurable'
|
14
26
|
end
|
data/qonfig.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.summary = 'Config object'
|
15
15
|
spec.description = 'Config. Defined as a class. Used as an instance. ' \
|
16
16
|
'Support for inheritance and composition. ' \
|
17
|
-
'Command-style DSL.
|
17
|
+
'Lazy instantiation. Thread-safe. Command-style DSL. ' \
|
18
18
|
'Extremely simple to define. Extremely simple to use. That\'s all.'
|
19
19
|
spec.homepage = 'https://github.com/0exp/qonfig'
|
20
20
|
spec.license = 'MIT'
|
@@ -30,9 +30,9 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_development_dependency 'coveralls', '~> 0.8'
|
31
31
|
spec.add_development_dependency 'simplecov', '~> 0.14'
|
32
32
|
spec.add_development_dependency 'simplecov-json', '~> 0.2'
|
33
|
-
spec.add_development_dependency 'rubocop', '~> 0.
|
33
|
+
spec.add_development_dependency 'rubocop', '~> 0.57'
|
34
34
|
spec.add_development_dependency 'rspec', '~> 3.7'
|
35
|
-
spec.add_development_dependency 'rubocop-rspec', '~> 1.
|
35
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 1.26'
|
36
36
|
|
37
37
|
spec.add_development_dependency 'bundler'
|
38
38
|
spec.add_development_dependency 'rake'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qonfig
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rustam Ibragimov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coveralls
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0.
|
61
|
+
version: '0.57'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0.
|
68
|
+
version: '0.57'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '1.
|
89
|
+
version: '1.26'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '1.
|
96
|
+
version: '1.26'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: bundler
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,8 +137,8 @@ dependencies:
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
description: Config. Defined as a class. Used as an instance. Support for inheritance
|
140
|
-
and composition. Command-style DSL.
|
141
|
-
Extremely simple to use. That's all.
|
140
|
+
and composition. Lazy instantiation. Thread-safe. Command-style DSL. Extremely simple
|
141
|
+
to define. Extremely simple to use. That's all.
|
142
142
|
email:
|
143
143
|
- iamdaiver@icloud.com
|
144
144
|
executables: []
|
@@ -164,11 +164,20 @@ files:
|
|
164
164
|
- lib/qonfig/commands/add_option.rb
|
165
165
|
- lib/qonfig/commands/base.rb
|
166
166
|
- lib/qonfig/commands/compose.rb
|
167
|
+
- lib/qonfig/commands/load_from_env.rb
|
168
|
+
- lib/qonfig/commands/load_from_env/value_converter.rb
|
169
|
+
- lib/qonfig/commands/load_from_self.rb
|
170
|
+
- lib/qonfig/commands/load_from_yaml.rb
|
171
|
+
- lib/qonfig/configurable.rb
|
167
172
|
- lib/qonfig/data_set.rb
|
173
|
+
- lib/qonfig/data_set/class_builder.rb
|
168
174
|
- lib/qonfig/dsl.rb
|
169
175
|
- lib/qonfig/error.rb
|
176
|
+
- lib/qonfig/loaders/yaml.rb
|
170
177
|
- lib/qonfig/settings.rb
|
171
|
-
- lib/qonfig/
|
178
|
+
- lib/qonfig/settings/builder.rb
|
179
|
+
- lib/qonfig/settings/key_guard.rb
|
180
|
+
- lib/qonfig/settings/lock.rb
|
172
181
|
- lib/qonfig/version.rb
|
173
182
|
- qonfig.gemspec
|
174
183
|
homepage: https://github.com/0exp/qonfig
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Qonfig
|
4
|
-
# @api private
|
5
|
-
# @since 0.1.0
|
6
|
-
module SettingsBuilder
|
7
|
-
class << self
|
8
|
-
# @param [Qonfig::CommandSet]
|
9
|
-
# @return [Qonfig::Settings]
|
10
|
-
#
|
11
|
-
# @ api private
|
12
|
-
# @since 0.1.0
|
13
|
-
def build(commands)
|
14
|
-
Qonfig::Settings.new.tap do |settings|
|
15
|
-
commands.each { |command| command.call(settings) }
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|