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