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,163 +1,211 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Qonfig
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
+ #
4
21
  # @api public
5
22
  # @since 0.1.0
6
- class DataSet
7
- # @since 0.1.0
8
- extend Qonfig::DSL
9
-
10
- # @return [Qonfig::Settings]
11
- #
12
- # @api private
13
- # @since 0.1.0
14
- attr_reader :settings
15
-
16
- # @param options_map [Hash]
17
- # @param configurations [Proc]
18
- #
19
- # @api public
20
- # @since 0.1.0
21
- def initialize(options_map = {}, &configurations)
22
- @__access_lock__ = Mutex.new
23
- @__definition_lock__ = Mutex.new
24
-
25
- thread_safe_definition { load!(options_map, &configurations) }
26
- end
23
+ def initialize(options_map = {}, &configurations)
24
+ @__access_lock__ = Mutex.new
25
+ @__definition_lock__ = Mutex.new
27
26
 
28
- # @return [void]
29
- #
30
- # @api public
31
- # @since 0.1.0
32
- def freeze!
33
- thread_safe_access { settings.__freeze__ }
34
- end
27
+ thread_safe_definition { load!(options_map, &configurations) }
28
+ end
35
29
 
36
- # @return [void]
37
- #
38
- # @api public
39
- # @since 0.2.0
40
- def frozen?
41
- thread_safe_access { settings.__is_frozen__ }
42
- end
30
+ # @return [void]
31
+ #
32
+ # @api public
33
+ # @since 0.1.0
34
+ def freeze!
35
+ thread_safe_access { settings.__freeze__ }
36
+ end
43
37
 
44
- # @param options_map [Hash]
45
- # @param configurations [Proc]
46
- # @return [void]
47
- #
48
- # @raise [Qonfig::FrozenSettingsError]
49
- #
50
- # @api public
51
- # @since 0.2.0
52
- def reload!(options_map = {}, &configurations)
53
- thread_safe_definition do
54
- raise Qonfig::FrozenSettingsError, 'Frozen config can not be reloaded' if frozen?
55
- load!(options_map, &configurations)
56
- end
57
- end
38
+ # @return [void]
39
+ #
40
+ # @api public
41
+ # @since 0.2.0
42
+ def frozen?
43
+ thread_safe_access { settings.__is_frozen__ }
44
+ end
58
45
 
59
- # @param options_map [Hash]
60
- # @return [void]
61
- #
62
- # @api public
63
- # @since 0.1.0
64
- def configure(options_map = {})
65
- thread_safe_access do
66
- settings.__apply_values__(options_map)
67
- yield(settings) if block_given?
68
- end
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)
69
58
  end
59
+ end
70
60
 
71
- # @return [Hash]
72
- #
73
- # @api public
74
- # @since 0.1.0
75
- def to_h
76
- thread_safe_access { settings.__to_hash__ }
77
- end
78
- alias_method :to_hash, :to_h
79
-
80
- # @param key [String, Symbol]
81
- # @return [Object]
82
- #
83
- # @api public
84
- # @since 0.2.0
85
- def [](key)
86
- thread_safe_access { settings[key] }
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?
87
70
  end
71
+ end
88
72
 
89
- # @param keys [Array<String, Symbol>]
90
- # @return [Object]
91
- #
92
- # @api public
93
- # @since 0.2.0
94
- def dig(*keys)
95
- thread_safe_access { settings.__dig__(*keys) }
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
+ )
96
88
  end
89
+ end
90
+ alias_method :to_hash, :to_h
97
91
 
98
- # @param keys [Array<String, Symbol>]
99
- # @return [Hash]
100
- #
101
- # @api public
102
- # @since 0.9.0
103
- def slice(*keys)
104
- thread_safe_access { settings.__slice__(*keys) }
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)
105
102
  end
103
+ end
106
104
 
107
- # @param keys [Array<String, Symbol>]
108
- # @return [Hash,Any]
109
- #
110
- # @api public
111
- # @since 0.10.0
112
- def slice_value(*keys)
113
- thread_safe_access { settings.__slice_value__(*keys) }
105
+ # @option path [String]
106
+ # @option symbolize_keys [Boolean]
107
+ # @option options [Hash<Symbol|String,Any>] Native (ruby-stdlib) ::YAML#dump attributes
108
+ # @param value_processor [Block]
109
+ # @return [void]
110
+ #
111
+ # @api public
112
+ # @since 0.11.0
113
+ def save_to_yaml(
114
+ path:,
115
+ symbolize_keys: false,
116
+ options: Qonfig::Uploaders::YAML::DEFAULT_OPTIONS,
117
+ &value_processor
118
+ )
119
+ thread_safe_access do
120
+ Qonfig::Uploaders::YAML.upload(
121
+ settings,
122
+ path: path,
123
+ options: options.merge(symbolize_keys: symbolize_keys),
124
+ &value_processor
125
+ )
114
126
  end
127
+ end
115
128
 
116
- # @return [void]
117
- #
118
- # @api public
119
- # @since 0.2.0
120
- def clear!
121
- thread_safe_access { settings.__clear__ }
122
- end
129
+ # @param key [String, Symbol]
130
+ # @return [Object]
131
+ #
132
+ # @api public
133
+ # @since 0.2.0
134
+ def [](key)
135
+ thread_safe_access { settings[key] }
136
+ end
123
137
 
124
- private
138
+ # @param keys [Array<String, Symbol>]
139
+ # @return [Object]
140
+ #
141
+ # @api public
142
+ # @since 0.2.0
143
+ def dig(*keys)
144
+ thread_safe_access { settings.__dig__(*keys) }
145
+ end
125
146
 
126
- # @return [Qonfig::Settings]
127
- #
128
- # @api private
129
- # @since 0.2.0
130
- def build_settings
131
- Qonfig::Settings::Builder.build(self.class.commands.dup)
132
- end
147
+ # @param keys [Array<String, Symbol>]
148
+ # @return [Hash]
149
+ #
150
+ # @api public
151
+ # @since 0.9.0
152
+ def slice(*keys)
153
+ thread_safe_access { settings.__slice__(*keys) }
154
+ end
133
155
 
134
- # @param options_map [Hash]
135
- # @param configurations [Proc]
136
- # @return [void]
137
- #
138
- # @api private
139
- # @since 0.2.0
140
- def load!(options_map = {}, &configurations)
141
- @settings = build_settings
142
- configure(options_map, &configurations)
143
- end
156
+ # @param keys [Array<String, Symbol>]
157
+ # @return [Hash,Any]
158
+ #
159
+ # @api public
160
+ # @since 0.10.0
161
+ def slice_value(*keys)
162
+ thread_safe_access { settings.__slice_value__(*keys) }
163
+ end
144
164
 
145
- # @param instructions [Proc]
146
- # @return [Object]
147
- #
148
- # @api private
149
- # @since 0.2.0
150
- def thread_safe_access(&instructions)
151
- @__access_lock__.synchronize(&instructions)
152
- end
165
+ # @return [void]
166
+ #
167
+ # @api public
168
+ # @since 0.2.0
169
+ def clear!
170
+ thread_safe_access { settings.__clear__ }
171
+ end
153
172
 
154
- # @param instructions [Proc]
155
- # @return [Object]
156
- #
157
- # @api private
158
- # @since 0.2.0
159
- def thread_safe_definition(&instructions)
160
- @__definition_lock__.synchronize(&instructions)
161
- end
173
+ private
174
+
175
+ # @return [Qonfig::Settings]
176
+ #
177
+ # @api private
178
+ # @since 0.2.0
179
+ def build_settings
180
+ Qonfig::Settings::Builder.build(self.class.commands.dup)
181
+ end
182
+
183
+ # @param options_map [Hash]
184
+ # @param configurations [Proc]
185
+ # @return [void]
186
+ #
187
+ # @api private
188
+ # @since 0.2.0
189
+ def load!(options_map = {}, &configurations)
190
+ @settings = build_settings
191
+ configure(options_map, &configurations)
192
+ end
193
+
194
+ # @param instructions [Proc]
195
+ # @return [Object]
196
+ #
197
+ # @api private
198
+ # @since 0.2.0
199
+ def thread_safe_access(&instructions)
200
+ @__access_lock__.synchronize(&instructions)
201
+ end
202
+
203
+ # @param instructions [Proc]
204
+ # @return [Object]
205
+ #
206
+ # @api private
207
+ # @since 0.2.0
208
+ def thread_safe_definition(&instructions)
209
+ @__definition_lock__.synchronize(&instructions)
162
210
  end
163
211
  end
data/lib/qonfig/dsl.rb CHANGED
@@ -1,124 +1,122 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Qonfig
4
- # @api private
5
- # @since 0.1.0
6
- module DSL
7
- class << self
8
- # @param child_klass [Qonfig::DataSet]
9
- # @return [void]
10
- #
11
- # @api private
12
- # @since 0.1.0
13
- def extended(child_klass)
14
- child_klass.instance_variable_set(:@commands, Qonfig::CommandSet.new)
15
-
16
- child_klass.singleton_class.prepend(Module.new do
17
- def inherited(child_klass)
18
- child_klass.instance_variable_set(:@commands, Qonfig::CommandSet.new)
19
- child_klass.commands.concat(commands)
20
- super
21
- end
22
- end)
23
- end
24
- end
25
-
26
- # @return [Qonfig::CommandSet]
3
+ # @api private
4
+ # @since 0.1.0
5
+ module Qonfig::DSL
6
+ class << self
7
+ # @param child_klass [Qonfig::DataSet]
8
+ # @return [void]
27
9
  #
28
10
  # @api private
29
11
  # @since 0.1.0
30
- def commands
31
- @commands
32
- end
12
+ def extended(child_klass)
13
+ child_klass.instance_variable_set(:@commands, Qonfig::CommandSet.new)
33
14
 
34
- # @param key [Symbol, String]
35
- # @param initial_value [Object]
36
- # @param nested_settings [Proc]
37
- # @return [void]
38
- #
39
- # @see Qonfig::Commands::AddNestedOption
40
- # @see Qonfig::Commands::AddOption
41
- #
42
- # @api public
43
- # @since 0.1.0
44
- def setting(key, initial_value = nil, &nested_settings)
45
- if block_given?
46
- commands << Qonfig::Commands::AddNestedOption.new(key, nested_settings)
47
- else
48
- commands << Qonfig::Commands::AddOption.new(key, initial_value)
49
- end
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)
50
22
  end
23
+ end
51
24
 
52
- # @param data_set_klass [Class{Qonfig::DataSet}]
53
- # @return [void]
54
- #
55
- # @see Qonfig::Comamnds::Compose
56
- #
57
- # @api private
58
- # @sine 0.1.0
59
- def compose(data_set_klass)
60
- commands << Qonfig::Commands::Compose.new(data_set_klass)
61
- end
25
+ # @return [Qonfig::CommandSet]
26
+ #
27
+ # @api private
28
+ # @since 0.1.0
29
+ def commands
30
+ @commands
31
+ end
62
32
 
63
- # @param file_path [String]
64
- # @option strict [Boolean]
65
- # @return [void]
66
- #
67
- # @see Qonfig::Commands::LoadFromYAML
68
- #
69
- # @api public
70
- # @since 0.2.0
71
- def load_from_yaml(file_path, strict: true)
72
- commands << Qonfig::Commands::LoadFromYAML.new(file_path, strict: strict)
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)
73
48
  end
49
+ end
74
50
 
75
- # @return [void]
76
- #
77
- # @see Qonfig::Commands::LoadFromSelf
78
- #
79
- # @api public
80
- # @since 0.2.0
81
- def load_from_self
82
- caller_location = caller(1, 1).first
83
- commands << Qonfig::Commands::LoadFromSelf.new(caller_location)
84
- end
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
85
61
 
86
- # @option convert_values [Boolean]
87
- # @option prefix [NilClass, String, Regexp]
88
- # @return [void]
89
- #
90
- # @see Qonfig::Commands::LoadFromENV
91
- #
92
- # @api public
93
- # @since 0.2.0
94
- def load_from_env(convert_values: false, prefix: nil, trim_prefix: false)
95
- commands << Qonfig::Commands::LoadFromENV.new(
96
- convert_values: convert_values,
97
- prefix: prefix,
98
- trim_prefix: trim_prefix
99
- )
100
- end
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
101
73
 
102
- # @param file_path [String]
103
- # @option strict [Boolean]
104
- # @return [void]
105
- #
106
- # @api public
107
- # @since 0.5.0
108
- def load_from_json(file_path, strict: true)
109
- commands << Qonfig::Commands::LoadFromJSON.new(file_path, strict: strict)
110
- end
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
111
84
 
112
- # @param file_path [String]
113
- # @option strict [Boolean]
114
- # @option via [Symbol]
115
- # @option env [Symbol, String]
116
- # @return [void]
117
- #
118
- # @api public
119
- # @since 0.7.0
120
- def expose_yaml(file_path, strict: true, via:, env:)
121
- commands << Qonfig::Commands::ExposeYAML.new(file_path, strict: strict, via: via, env: env)
122
- end
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)
123
121
  end
124
122
  end
@@ -7,7 +7,19 @@ module Qonfig
7
7
 
8
8
  # @api public
9
9
  # @since 0.1.0
10
- ArgumentError = Class.new(Error)
10
+ ArgumentError = Class.new(ArgumentError)
11
+
12
+ # @api public
13
+ # @since 0.11.0
14
+ IncorrectHashTransformationError = Class.new(ArgumentError)
15
+
16
+ # @api public
17
+ # @since 0.11.0
18
+ IncorrectKeyTransformerError = Class.new(IncorrectHashTransformationError)
19
+
20
+ # @api public
21
+ # @since 0.11.0
22
+ IncorrectValueTransformerError = Class.new(IncorrectHashTransformationError)
11
23
 
12
24
  # @see Qonfig::Settings
13
25
  #
@@ -1,40 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Qonfig
4
- module Loaders
3
+ # @api private
4
+ # @since 0.2.0
5
+ class Qonfig::Loaders::Basic
6
+ class << self
7
+ # @param data [String]
8
+ # @return [void]
9
+ #
5
10
  # @api private
6
- # @sicne 0.5.0
7
- module Basic
8
- # @param data [String]
9
- # @return [void]
10
- #
11
- # @api private
12
- # @since 0.5.0
13
- def load(data)
14
- nil
15
- end
11
+ # @since 0.5.0
12
+ def load(data)
13
+ nil # NOTE: consciously return nil (for clarity)
14
+ end
16
15
 
17
- # @return [void]
18
- #
19
- # @api private
20
- # @since 0.5.0
21
- def load_empty_data
22
- nil
23
- end
16
+ # @return [void]
17
+ #
18
+ # @api private
19
+ # @since 0.5.0
20
+ def load_empty_data
21
+ nil # NOTE: consciously return nil (for clarity)
22
+ end
24
23
 
25
- # @param file_path [String]
26
- # @option fail_on_unexist [Boolean]
27
- # @return [Object]
28
- #
29
- # @raise [Qonfig::FileNotFoundError]
30
- #
31
- # @api private
32
- # @since 0.5.0
33
- def load_file(file_path, fail_on_unexist: true)
34
- load(::File.read(file_path))
35
- rescue Errno::ENOENT => error
36
- fail_on_unexist ? (raise Qonfig::FileNotFoundError, error.message) : load_empty_data
37
- end
24
+ # @param file_path [String]
25
+ # @option fail_on_unexist [Boolean]
26
+ # @return [Object]
27
+ #
28
+ # @raise [Qonfig::FileNotFoundError]
29
+ #
30
+ # @api private
31
+ # @since 0.5.0
32
+ def load_file(file_path, fail_on_unexist: true)
33
+ load(::File.read(file_path))
34
+ rescue Errno::ENOENT => error
35
+ fail_on_unexist ? (raise Qonfig::FileNotFoundError, error.message) : load_empty_data
38
36
  end
39
37
  end
40
38
  end