qonfig 0.0.0 → 0.12.0

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