qonfig 0.0.0 → 0.12.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/.gitignore +6 -2
- data/.jrubyrc +1 -0
- data/.rspec +1 -1
- data/.rubocop.yml +15 -0
- data/.travis.yml +43 -4
- data/CHANGELOG.md +121 -0
- data/Gemfile +4 -2
- data/LICENSE.txt +1 -1
- data/README.md +1060 -19
- data/Rakefile +18 -4
- data/bin/console +5 -11
- data/bin/rspec +55 -0
- data/bin/setup +1 -0
- data/gemfiles/with_external_deps.gemfile +8 -0
- data/gemfiles/without_external_deps.gemfile +5 -0
- data/lib/qonfig.rb +22 -2
- data/lib/qonfig/command_set.rb +67 -0
- data/lib/qonfig/commands.rb +15 -0
- data/lib/qonfig/commands/add_nested_option.rb +45 -0
- data/lib/qonfig/commands/add_option.rb +41 -0
- data/lib/qonfig/commands/base.rb +12 -0
- data/lib/qonfig/commands/compose.rb +37 -0
- data/lib/qonfig/commands/expose_yaml.rb +159 -0
- data/lib/qonfig/commands/load_from_env.rb +95 -0
- data/lib/qonfig/commands/load_from_env/value_converter.rb +84 -0
- data/lib/qonfig/commands/load_from_json.rb +56 -0
- data/lib/qonfig/commands/load_from_self.rb +73 -0
- data/lib/qonfig/commands/load_from_yaml.rb +58 -0
- data/lib/qonfig/configurable.rb +116 -0
- data/lib/qonfig/data_set.rb +213 -0
- data/lib/qonfig/data_set/class_builder.rb +27 -0
- data/lib/qonfig/data_set/validator.rb +7 -0
- data/lib/qonfig/dsl.rb +122 -0
- data/lib/qonfig/errors.rb +111 -0
- data/lib/qonfig/loaders.rb +9 -0
- data/lib/qonfig/loaders/basic.rb +38 -0
- data/lib/qonfig/loaders/json.rb +24 -0
- data/lib/qonfig/loaders/yaml.rb +24 -0
- data/lib/qonfig/plugins.rb +65 -0
- data/lib/qonfig/plugins/abstract.rb +13 -0
- data/lib/qonfig/plugins/access_mixin.rb +38 -0
- data/lib/qonfig/plugins/registry.rb +125 -0
- data/lib/qonfig/plugins/toml.rb +26 -0
- data/lib/qonfig/plugins/toml/commands/expose_toml.rb +146 -0
- data/lib/qonfig/plugins/toml/commands/load_from_toml.rb +49 -0
- data/lib/qonfig/plugins/toml/data_set.rb +19 -0
- data/lib/qonfig/plugins/toml/dsl.rb +27 -0
- data/lib/qonfig/plugins/toml/loaders/toml.rb +24 -0
- data/lib/qonfig/plugins/toml/tomlrb_fixes.rb +92 -0
- data/lib/qonfig/plugins/toml/uploaders/toml.rb +25 -0
- data/lib/qonfig/settings.rb +457 -0
- data/lib/qonfig/settings/builder.rb +18 -0
- data/lib/qonfig/settings/key_guard.rb +71 -0
- data/lib/qonfig/settings/lock.rb +60 -0
- data/lib/qonfig/uploaders.rb +10 -0
- data/lib/qonfig/uploaders/base.rb +18 -0
- data/lib/qonfig/uploaders/file.rb +55 -0
- data/lib/qonfig/uploaders/json.rb +35 -0
- data/lib/qonfig/uploaders/yaml.rb +93 -0
- data/lib/qonfig/version.rb +7 -1
- data/qonfig.gemspec +29 -17
- metadata +122 -16
@@ -0,0 +1,213 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api public
|
4
|
+
# @since 0.1.0
|
5
|
+
class Qonfig::DataSet
|
6
|
+
require_relative 'data_set/class_builder'
|
7
|
+
require_relative 'data_set/validator'
|
8
|
+
|
9
|
+
# @since 0.1.0
|
10
|
+
extend Qonfig::DSL
|
11
|
+
|
12
|
+
# @return [Qonfig::Settings]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.1.0
|
16
|
+
attr_reader :settings
|
17
|
+
|
18
|
+
# @param options_map [Hash]
|
19
|
+
# @param configurations [Proc]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
# @since 0.1.0
|
23
|
+
def initialize(options_map = {}, &configurations)
|
24
|
+
@__access_lock__ = Mutex.new
|
25
|
+
@__definition_lock__ = Mutex.new
|
26
|
+
|
27
|
+
thread_safe_definition { load!(options_map, &configurations) }
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [void]
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
# @since 0.1.0
|
34
|
+
def freeze!
|
35
|
+
thread_safe_access { settings.__freeze__ }
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [void]
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
# @since 0.2.0
|
42
|
+
def frozen?
|
43
|
+
thread_safe_access { settings.__is_frozen__ }
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param options_map [Hash]
|
47
|
+
# @param configurations [Proc]
|
48
|
+
# @return [void]
|
49
|
+
#
|
50
|
+
# @raise [Qonfig::FrozenSettingsError]
|
51
|
+
#
|
52
|
+
# @api public
|
53
|
+
# @since 0.2.0
|
54
|
+
def reload!(options_map = {}, &configurations)
|
55
|
+
thread_safe_definition do
|
56
|
+
raise Qonfig::FrozenSettingsError, 'Frozen config can not be reloaded' if frozen?
|
57
|
+
load!(options_map, &configurations)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param options_map [Hash]
|
62
|
+
# @return [void]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
# @since 0.1.0
|
66
|
+
def configure(options_map = {})
|
67
|
+
thread_safe_access do
|
68
|
+
settings.__apply_values__(options_map)
|
69
|
+
yield(settings) if block_given?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @option key_transformer [Proc]
|
74
|
+
# @option value_transformer [Proc]
|
75
|
+
# @return [Hash]
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
# @since 0.1.0
|
79
|
+
def to_h(
|
80
|
+
key_transformer: Qonfig::Settings::BASIC_SETTING_KEY_TRANSFORMER,
|
81
|
+
value_transformer: Qonfig::Settings::BASIC_SETTING_VALUE_TRANSFORMER
|
82
|
+
)
|
83
|
+
thread_safe_access do
|
84
|
+
settings.__to_hash__(
|
85
|
+
transform_key: key_transformer,
|
86
|
+
transform_value: value_transformer
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
alias_method :to_hash, :to_h
|
91
|
+
|
92
|
+
# @option path [String]
|
93
|
+
# @option options [Hash<Symbol|String,Any>] Native (ruby-stdlib) ::JSON#generate attributes
|
94
|
+
# @param value_processor [Block]
|
95
|
+
# @return [void]
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
# @since 0.11.0
|
99
|
+
def save_to_json(path:, options: Qonfig::Uploaders::JSON::DEFAULT_OPTIONS, &value_processor)
|
100
|
+
thread_safe_access do
|
101
|
+
Qonfig::Uploaders::JSON.upload(settings, path: path, options: options, &value_processor)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
alias_method :dump_to_json, :save_to_json
|
105
|
+
|
106
|
+
# @option path [String]
|
107
|
+
# @option symbolize_keys [Boolean]
|
108
|
+
# @option options [Hash<Symbol|String,Any>] Native (ruby-stdlib) ::YAML#dump attributes
|
109
|
+
# @param value_processor [Block]
|
110
|
+
# @return [void]
|
111
|
+
#
|
112
|
+
# @api public
|
113
|
+
# @since 0.11.0
|
114
|
+
def save_to_yaml(
|
115
|
+
path:,
|
116
|
+
symbolize_keys: false,
|
117
|
+
options: Qonfig::Uploaders::YAML::DEFAULT_OPTIONS,
|
118
|
+
&value_processor
|
119
|
+
)
|
120
|
+
thread_safe_access do
|
121
|
+
Qonfig::Uploaders::YAML.upload(
|
122
|
+
settings,
|
123
|
+
path: path,
|
124
|
+
options: options.merge(symbolize_keys: symbolize_keys),
|
125
|
+
&value_processor
|
126
|
+
)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
alias_method :dump_to_yaml, :save_to_yaml
|
130
|
+
|
131
|
+
# @param key [String, Symbol]
|
132
|
+
# @return [Object]
|
133
|
+
#
|
134
|
+
# @api public
|
135
|
+
# @since 0.2.0
|
136
|
+
def [](key)
|
137
|
+
thread_safe_access { settings[key] }
|
138
|
+
end
|
139
|
+
|
140
|
+
# @param keys [Array<String, Symbol>]
|
141
|
+
# @return [Object]
|
142
|
+
#
|
143
|
+
# @api public
|
144
|
+
# @since 0.2.0
|
145
|
+
def dig(*keys)
|
146
|
+
thread_safe_access { settings.__dig__(*keys) }
|
147
|
+
end
|
148
|
+
|
149
|
+
# @param keys [Array<String, Symbol>]
|
150
|
+
# @return [Hash]
|
151
|
+
#
|
152
|
+
# @api public
|
153
|
+
# @since 0.9.0
|
154
|
+
def slice(*keys)
|
155
|
+
thread_safe_access { settings.__slice__(*keys) }
|
156
|
+
end
|
157
|
+
|
158
|
+
# @param keys [Array<String, Symbol>]
|
159
|
+
# @return [Hash,Any]
|
160
|
+
#
|
161
|
+
# @api public
|
162
|
+
# @since 0.10.0
|
163
|
+
def slice_value(*keys)
|
164
|
+
thread_safe_access { settings.__slice_value__(*keys) }
|
165
|
+
end
|
166
|
+
|
167
|
+
# @return [void]
|
168
|
+
#
|
169
|
+
# @api public
|
170
|
+
# @since 0.2.0
|
171
|
+
def clear!
|
172
|
+
thread_safe_access { settings.__clear__ }
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
# @return [Qonfig::Settings]
|
178
|
+
#
|
179
|
+
# @api private
|
180
|
+
# @since 0.2.0
|
181
|
+
def build_settings
|
182
|
+
Qonfig::Settings::Builder.build(self.class.commands.dup)
|
183
|
+
end
|
184
|
+
|
185
|
+
# @param options_map [Hash]
|
186
|
+
# @param configurations [Proc]
|
187
|
+
# @return [void]
|
188
|
+
#
|
189
|
+
# @api private
|
190
|
+
# @since 0.2.0
|
191
|
+
def load!(options_map = {}, &configurations)
|
192
|
+
@settings = build_settings
|
193
|
+
configure(options_map, &configurations)
|
194
|
+
end
|
195
|
+
|
196
|
+
# @param instructions [Proc]
|
197
|
+
# @return [Object]
|
198
|
+
#
|
199
|
+
# @api private
|
200
|
+
# @since 0.2.0
|
201
|
+
def thread_safe_access(&instructions)
|
202
|
+
@__access_lock__.synchronize(&instructions)
|
203
|
+
end
|
204
|
+
|
205
|
+
# @param instructions [Proc]
|
206
|
+
# @return [Object]
|
207
|
+
#
|
208
|
+
# @api private
|
209
|
+
# @since 0.2.0
|
210
|
+
def thread_safe_definition(&instructions)
|
211
|
+
@__definition_lock__.synchronize(&instructions)
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
module Qonfig::DataSet::ClassBuilder
|
6
|
+
class << self
|
7
|
+
# @param hash [Hash]
|
8
|
+
# @return [Class<Qonfig::DataSet>]
|
9
|
+
#
|
10
|
+
# @see Qonfig::DataSet
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
# @since 0.2.0
|
14
|
+
def build_from_hash(hash)
|
15
|
+
Class.new(Qonfig::DataSet).tap do |data_set_klass|
|
16
|
+
hash.each_pair do |key, value|
|
17
|
+
if value.is_a?(Hash) && value.any?
|
18
|
+
sub_data_set_klass = build_from_hash(value)
|
19
|
+
data_set_klass.setting(key) { compose sub_data_set_klass }
|
20
|
+
else
|
21
|
+
data_set_klass.setting key, value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/qonfig/dsl.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.1.0
|
5
|
+
module Qonfig::DSL
|
6
|
+
class << self
|
7
|
+
# @param child_klass [Qonfig::DataSet]
|
8
|
+
# @return [void]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.1.0
|
12
|
+
def extended(child_klass)
|
13
|
+
child_klass.instance_variable_set(:@commands, Qonfig::CommandSet.new)
|
14
|
+
|
15
|
+
child_klass.singleton_class.prepend(Module.new do
|
16
|
+
def inherited(child_klass)
|
17
|
+
child_klass.instance_variable_set(:@commands, Qonfig::CommandSet.new)
|
18
|
+
child_klass.commands.concat(commands)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Qonfig::CommandSet]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
# @since 0.1.0
|
29
|
+
def commands
|
30
|
+
@commands
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param key [Symbol, String]
|
34
|
+
# @param initial_value [Object]
|
35
|
+
# @param nested_settings [Proc]
|
36
|
+
# @return [void]
|
37
|
+
#
|
38
|
+
# @see Qonfig::Commands::AddNestedOption
|
39
|
+
# @see Qonfig::Commands::AddOption
|
40
|
+
#
|
41
|
+
# @api public
|
42
|
+
# @since 0.1.0
|
43
|
+
def setting(key, initial_value = nil, &nested_settings)
|
44
|
+
if block_given?
|
45
|
+
commands << Qonfig::Commands::AddNestedOption.new(key, nested_settings)
|
46
|
+
else
|
47
|
+
commands << Qonfig::Commands::AddOption.new(key, initial_value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param data_set_klass [Class<Qonfig::DataSet>]
|
52
|
+
# @return [void]
|
53
|
+
#
|
54
|
+
# @see Qonfig::Comamnds::Compose
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
# @sine 0.1.0
|
58
|
+
def compose(data_set_klass)
|
59
|
+
commands << Qonfig::Commands::Compose.new(data_set_klass)
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param file_path [String]
|
63
|
+
# @option strict [Boolean]
|
64
|
+
# @return [void]
|
65
|
+
#
|
66
|
+
# @see Qonfig::Commands::LoadFromYAML
|
67
|
+
#
|
68
|
+
# @api public
|
69
|
+
# @since 0.2.0
|
70
|
+
def load_from_yaml(file_path, strict: true)
|
71
|
+
commands << Qonfig::Commands::LoadFromYAML.new(file_path, strict: strict)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [void]
|
75
|
+
#
|
76
|
+
# @see Qonfig::Commands::LoadFromSelf
|
77
|
+
#
|
78
|
+
# @api public
|
79
|
+
# @since 0.2.0
|
80
|
+
def load_from_self
|
81
|
+
caller_location = caller(1, 1).first
|
82
|
+
commands << Qonfig::Commands::LoadFromSelf.new(caller_location)
|
83
|
+
end
|
84
|
+
|
85
|
+
# @option convert_values [Boolean]
|
86
|
+
# @option prefix [NilClass, String, Regexp]
|
87
|
+
# @return [void]
|
88
|
+
#
|
89
|
+
# @see Qonfig::Commands::LoadFromENV
|
90
|
+
#
|
91
|
+
# @api public
|
92
|
+
# @since 0.2.0
|
93
|
+
def load_from_env(convert_values: false, prefix: nil, trim_prefix: false)
|
94
|
+
commands << Qonfig::Commands::LoadFromENV.new(
|
95
|
+
convert_values: convert_values,
|
96
|
+
prefix: prefix,
|
97
|
+
trim_prefix: trim_prefix
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
# @param file_path [String]
|
102
|
+
# @option strict [Boolean]
|
103
|
+
# @return [void]
|
104
|
+
#
|
105
|
+
# @api public
|
106
|
+
# @since 0.5.0
|
107
|
+
def load_from_json(file_path, strict: true)
|
108
|
+
commands << Qonfig::Commands::LoadFromJSON.new(file_path, strict: strict)
|
109
|
+
end
|
110
|
+
|
111
|
+
# @param file_path [String]
|
112
|
+
# @option strict [Boolean]
|
113
|
+
# @option via [Symbol]
|
114
|
+
# @option env [Symbol, String]
|
115
|
+
# @return [void]
|
116
|
+
#
|
117
|
+
# @api public
|
118
|
+
# @since 0.7.0
|
119
|
+
def expose_yaml(file_path, strict: true, via:, env:)
|
120
|
+
commands << Qonfig::Commands::ExposeYAML.new(file_path, strict: strict, via: via, env: env)
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Qonfig
|
4
|
+
# @api public
|
5
|
+
# @since 0.1.0
|
6
|
+
Error = Class.new(StandardError)
|
7
|
+
|
8
|
+
# @api public
|
9
|
+
# @since 0.1.0
|
10
|
+
ArgumentError = Class.new(ArgumentError)
|
11
|
+
|
12
|
+
# @api public
|
13
|
+
# @since 0.12.0
|
14
|
+
PluginError = Class.new(Error)
|
15
|
+
|
16
|
+
# @api public
|
17
|
+
# @since 0.11.0
|
18
|
+
IncorrectHashTransformationError = Class.new(ArgumentError)
|
19
|
+
|
20
|
+
# @api public
|
21
|
+
# @since 0.11.0
|
22
|
+
IncorrectKeyTransformerError = Class.new(IncorrectHashTransformationError)
|
23
|
+
|
24
|
+
# @api public
|
25
|
+
# @since 0.11.0
|
26
|
+
IncorrectValueTransformerError = Class.new(IncorrectHashTransformationError)
|
27
|
+
|
28
|
+
# @see Qonfig::Settings
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
# @since 0.1.0
|
32
|
+
UnknownSettingError = Class.new(Error)
|
33
|
+
|
34
|
+
# @see Qonfig::Settings
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
# @since 0.2.0
|
38
|
+
AmbiguousSettingValueError = Class.new(Error)
|
39
|
+
|
40
|
+
# @see Qonfig::Settings
|
41
|
+
# @see Qonfig::Settings::KeyGuard
|
42
|
+
# @see Qonfig::Commands::AddOption
|
43
|
+
# @see Qonfig::Commands::AddNestedOption
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
# @since 0.2.0
|
47
|
+
CoreMethodIntersectionError = Class.new(Error)
|
48
|
+
|
49
|
+
# @see Qonfig::Settings
|
50
|
+
# @see Qonfig::DataSet
|
51
|
+
#
|
52
|
+
# @api public
|
53
|
+
# @since 0.1.0
|
54
|
+
FrozenSettingsError = begin # rubocop:disable Naming/ConstantName
|
55
|
+
# :nocov:
|
56
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
|
57
|
+
Class.new(::FrozenError)
|
58
|
+
else
|
59
|
+
Class.new(::RuntimeError)
|
60
|
+
end
|
61
|
+
# :nocov:
|
62
|
+
end
|
63
|
+
|
64
|
+
# @see Qonfig::Commands::LoadFromYAML
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
# @since 0.2.0
|
68
|
+
IncompatibleYAMLStructureError = Class.new(Error)
|
69
|
+
|
70
|
+
# @see Qonfig::Commands::LoadFromJSON
|
71
|
+
#
|
72
|
+
# @api public
|
73
|
+
# @since 0.5.0
|
74
|
+
IncompatibleJSONStructureError = Class.new(Error)
|
75
|
+
|
76
|
+
# @see Qonfig::Loaders::YAML
|
77
|
+
#
|
78
|
+
# @api public
|
79
|
+
# @since 0.2.0
|
80
|
+
FileNotFoundError = Class.new(Errno::ENOENT)
|
81
|
+
|
82
|
+
# @see Qonfig::Commands::LoadFromSelf
|
83
|
+
#
|
84
|
+
# @api public
|
85
|
+
# @since 0.2.0
|
86
|
+
SelfDataNotFoundError = Class.new(Error)
|
87
|
+
|
88
|
+
# @see Qonfig::Plugins::Regsitry
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
# @since 0.4.0
|
92
|
+
AlreadyRegisteredPluginError = Class.new(Error)
|
93
|
+
|
94
|
+
# @see Qonfig::Plugins::Registry
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
# @since 0.4.0
|
98
|
+
UnregisteredPluginError = Class.new(Error)
|
99
|
+
|
100
|
+
# @see Qonfig::Commands::ExposeYAML
|
101
|
+
#
|
102
|
+
# @api public
|
103
|
+
# @since 0.7.0
|
104
|
+
ExposeError = Class.new(Error)
|
105
|
+
|
106
|
+
# @see Qonfig::Plugin::TOMLFormat
|
107
|
+
#
|
108
|
+
# @api public
|
109
|
+
# @since 0.12.0
|
110
|
+
UnresolvedPluginDependencyError = Class.new(PluginError)
|
111
|
+
end
|