qonfig 0.10.0 → 0.11.0

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