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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -2
  3. data/.jrubyrc +1 -0
  4. data/.rspec +1 -1
  5. data/.rubocop.yml +15 -0
  6. data/.travis.yml +43 -4
  7. data/CHANGELOG.md +121 -0
  8. data/Gemfile +4 -2
  9. data/LICENSE.txt +1 -1
  10. data/README.md +1060 -19
  11. data/Rakefile +18 -4
  12. data/bin/console +5 -11
  13. data/bin/rspec +55 -0
  14. data/bin/setup +1 -0
  15. data/gemfiles/with_external_deps.gemfile +8 -0
  16. data/gemfiles/without_external_deps.gemfile +5 -0
  17. data/lib/qonfig.rb +22 -2
  18. data/lib/qonfig/command_set.rb +67 -0
  19. data/lib/qonfig/commands.rb +15 -0
  20. data/lib/qonfig/commands/add_nested_option.rb +45 -0
  21. data/lib/qonfig/commands/add_option.rb +41 -0
  22. data/lib/qonfig/commands/base.rb +12 -0
  23. data/lib/qonfig/commands/compose.rb +37 -0
  24. data/lib/qonfig/commands/expose_yaml.rb +159 -0
  25. data/lib/qonfig/commands/load_from_env.rb +95 -0
  26. data/lib/qonfig/commands/load_from_env/value_converter.rb +84 -0
  27. data/lib/qonfig/commands/load_from_json.rb +56 -0
  28. data/lib/qonfig/commands/load_from_self.rb +73 -0
  29. data/lib/qonfig/commands/load_from_yaml.rb +58 -0
  30. data/lib/qonfig/configurable.rb +116 -0
  31. data/lib/qonfig/data_set.rb +213 -0
  32. data/lib/qonfig/data_set/class_builder.rb +27 -0
  33. data/lib/qonfig/data_set/validator.rb +7 -0
  34. data/lib/qonfig/dsl.rb +122 -0
  35. data/lib/qonfig/errors.rb +111 -0
  36. data/lib/qonfig/loaders.rb +9 -0
  37. data/lib/qonfig/loaders/basic.rb +38 -0
  38. data/lib/qonfig/loaders/json.rb +24 -0
  39. data/lib/qonfig/loaders/yaml.rb +24 -0
  40. data/lib/qonfig/plugins.rb +65 -0
  41. data/lib/qonfig/plugins/abstract.rb +13 -0
  42. data/lib/qonfig/plugins/access_mixin.rb +38 -0
  43. data/lib/qonfig/plugins/registry.rb +125 -0
  44. data/lib/qonfig/plugins/toml.rb +26 -0
  45. data/lib/qonfig/plugins/toml/commands/expose_toml.rb +146 -0
  46. data/lib/qonfig/plugins/toml/commands/load_from_toml.rb +49 -0
  47. data/lib/qonfig/plugins/toml/data_set.rb +19 -0
  48. data/lib/qonfig/plugins/toml/dsl.rb +27 -0
  49. data/lib/qonfig/plugins/toml/loaders/toml.rb +24 -0
  50. data/lib/qonfig/plugins/toml/tomlrb_fixes.rb +92 -0
  51. data/lib/qonfig/plugins/toml/uploaders/toml.rb +25 -0
  52. data/lib/qonfig/settings.rb +457 -0
  53. data/lib/qonfig/settings/builder.rb +18 -0
  54. data/lib/qonfig/settings/key_guard.rb +71 -0
  55. data/lib/qonfig/settings/lock.rb +60 -0
  56. data/lib/qonfig/uploaders.rb +10 -0
  57. data/lib/qonfig/uploaders/base.rb +18 -0
  58. data/lib/qonfig/uploaders/file.rb +55 -0
  59. data/lib/qonfig/uploaders/json.rb +35 -0
  60. data/lib/qonfig/uploaders/yaml.rb +93 -0
  61. data/lib/qonfig/version.rb +7 -1
  62. data/qonfig.gemspec +29 -17
  63. 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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api public
4
+ # @since 0.?.0
5
+ class Qonfig::DataSet::Validator
6
+ # TODO: think about validation layer
7
+ end
@@ -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