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,159 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Qonfig
4
- module Commands
5
- # @api private
6
- # @since 0.7.0
7
- class ExposeYAML < Base
8
- # @return [Hash]
9
- #
10
- # @api private
11
- # @since 0.7.0
12
- EXPOSERS = { file_name: :file_name, env_key: :env_key }.freeze
13
-
14
- # @return [Hash]
15
- #
16
- # @api private
17
- # @since 0.7.0
18
- EMPTY_YAML_DATA = {}.freeze
19
-
20
- # @return [String]
21
- #
22
- # @api private
23
- # @since 0.7.0
24
- attr_reader :file_path
25
-
26
- # @return [Boolean]
27
- #
28
- # @api private
29
- # @since 0.7.0
30
- attr_reader :strict
31
-
32
- # @return [Symbol]
33
- #
34
- # @api private
35
- # @since 0.7.0
36
- attr_reader :via
37
-
38
- # @return [Symbol, String]
39
- #
40
- # @api private
41
- # @since 0.7.0
42
- attr_reader :env
43
-
44
- # @param file_path [String]
45
- # @option strict [Boolean]
46
- # @option via [Symbol]
47
- # @option env [String, Symbol]
48
- #
49
- # @api private
50
- # @since 0.7.0
51
- def initialize(file_path, strict: true, via:, env:)
52
- unless env.is_a?(Symbol) || env.is_a?(String) || env.is_a?(Numeric)
53
- raise Qonfig::ArgumentError, ':env should be a string or a symbol'
54
- end
55
-
56
- raise Qonfig::ArgumentError, ':env should be provided' if env.to_s.empty?
57
- raise Qonfig::ArgumentError, 'used :via is unsupported' unless EXPOSERS.key?(via)
58
-
59
- @file_path = file_path
60
- @strict = strict
61
- @via = via
62
- @env = env
63
- end
64
-
65
- # @param settings [Qonfig::Settings]
66
- # @return [void]
67
- #
68
- # @api private
69
- # @since 0.7.0
70
- def call(settings)
71
- case via
72
- when EXPOSERS[:file_name]
73
- expose_file_name!(settings)
74
- when EXPOSERS[:env_key]
75
- expose_env_key!(settings)
76
- end
77
- end
78
-
79
- private
80
-
81
- # @param settings [Qonfig::Settings]
82
- # @return [void]
83
- #
84
- # @api private
85
- # @since 0.7.0
86
- # rubocop:disable Metrics/AbcSize
87
- def expose_file_name!(settings)
88
- # NOTE: transform file name (insert environment name into the file name)
89
- # from: path/to/file/file_name.file_extension
90
- # to: path/to/file/file_name.env_name.file_extension
91
-
92
- pathname = Pathname.new(file_path)
93
- dirname = pathname.dirname
94
- extname = pathname.extname.to_s
95
- basename = pathname.basename.to_s.sub!(extname, '')
96
- envname = [env.to_s, extname].reject(&:empty?).join('')
97
- envfile = [basename, envname].reject(&:empty?).join('.')
98
- realfile = dirname.join(envfile).to_s
99
-
100
- yaml_data = load_yaml_data(realfile)
101
- yaml_based_settings = build_data_set_class(yaml_data).new.settings
102
-
103
- settings.__append_settings__(yaml_based_settings)
104
- end
105
- # rubocop:enable Metrics/AbcSize
106
-
107
- # @param settings [Qonfig::Settings]
108
- # @return [void]
109
- #
110
- # @raise [Qonfig::ExposeError]
111
- # @raise [Qonfig::IncompatibleYAMLStructureError]
112
- #
113
- # @api private
114
- # @since 0.7.0
115
- # rubocop:disable Metrics/AbcSize
116
- def expose_env_key!(settings)
117
- yaml_data = load_yaml_data(file_path)
118
- yaml_data_slice = yaml_data[env] || yaml_data[env.to_s] || yaml_data[env.to_sym]
119
- yaml_data_slice = EMPTY_YAML_DATA.dup if yaml_data_slice.nil? && !strict
120
-
121
- raise(
122
- Qonfig::ExposeError,
123
- "#{file_path} file does not contain settings with <#{env}> environment key!"
124
- ) unless yaml_data_slice
125
-
126
- raise(
127
- Qonfig::IncompatibleYAMLStructureError,
128
- 'YAML content should have a hash-like structure'
129
- ) unless yaml_data_slice.is_a?(Hash)
130
-
131
- yaml_based_settings = build_data_set_class(yaml_data_slice).new.settings
132
-
133
- settings.__append_settings__(yaml_based_settings)
134
- end
135
- # rubocop:enable Metrics/AbcSize
136
-
137
- # @param file_path [String]
138
- # @return [Hash]
139
- #
140
- # @raise [Qonfig::IncompatibleYAMLStructureError]
141
- #
142
- # @api private
143
- # @since 0.7.0
144
- def load_yaml_data(file_path)
145
- Qonfig::Loaders::YAML.load_file(file_path, fail_on_unexist: strict).tap do |yaml_data|
146
- raise(
147
- Qonfig::IncompatibleYAMLStructureError,
148
- 'YAML content should have a hash-like structure'
149
- ) unless yaml_data.is_a?(Hash)
150
- end
151
- end
152
-
153
- # @param yaml_data [Hash]
154
- # @return [Class<Qonfig::DataSet>]
155
- #
156
- # @api private
157
- # @since 0.7.0
158
- def build_data_set_class(yaml_data)
159
- Qonfig::DataSet::ClassBuilder.build_from_hash(yaml_data)
160
- end
3
+ # @api private
4
+ # @since 0.7.0
5
+ class Qonfig::Commands::ExposeYAML < Qonfig::Commands::Base
6
+ # @return [Hash]
7
+ #
8
+ # @api private
9
+ # @since 0.7.0
10
+ EXPOSERS = { file_name: :file_name, env_key: :env_key }.freeze
11
+
12
+ # @return [Hash]
13
+ #
14
+ # @api private
15
+ # @since 0.7.0
16
+ EMPTY_YAML_DATA = {}.freeze
17
+
18
+ # @return [String]
19
+ #
20
+ # @api private
21
+ # @since 0.7.0
22
+ attr_reader :file_path
23
+
24
+ # @return [Boolean]
25
+ #
26
+ # @api private
27
+ # @since 0.7.0
28
+ attr_reader :strict
29
+
30
+ # @return [Symbol]
31
+ #
32
+ # @api private
33
+ # @since 0.7.0
34
+ attr_reader :via
35
+
36
+ # @return [Symbol, String]
37
+ #
38
+ # @api private
39
+ # @since 0.7.0
40
+ attr_reader :env
41
+
42
+ # @param file_path [String]
43
+ # @option strict [Boolean]
44
+ # @option via [Symbol]
45
+ # @option env [String, Symbol]
46
+ #
47
+ # @api private
48
+ # @since 0.7.0
49
+ def initialize(file_path, strict: true, via:, env:)
50
+ unless env.is_a?(Symbol) || env.is_a?(String) || env.is_a?(Numeric)
51
+ raise Qonfig::ArgumentError, ':env should be a string or a symbol'
161
52
  end
53
+
54
+ raise Qonfig::ArgumentError, ':env should be provided' if env.to_s.empty?
55
+ raise Qonfig::ArgumentError, 'used :via is unsupported' unless EXPOSERS.key?(via)
56
+
57
+ @file_path = file_path
58
+ @strict = strict
59
+ @via = via
60
+ @env = env
61
+ end
62
+
63
+ # @param settings [Qonfig::Settings]
64
+ # @return [void]
65
+ #
66
+ # @api private
67
+ # @since 0.7.0
68
+ def call(settings)
69
+ case via
70
+ when EXPOSERS[:file_name]
71
+ expose_file_name!(settings)
72
+ when EXPOSERS[:env_key]
73
+ expose_env_key!(settings)
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ # @param settings [Qonfig::Settings]
80
+ # @return [void]
81
+ #
82
+ # @api private
83
+ # @since 0.7.0
84
+ # rubocop:disable Metrics/AbcSize
85
+ def expose_file_name!(settings)
86
+ # NOTE: transform file name (insert environment name into the file name)
87
+ # from: path/to/file/file_name.file_extension
88
+ # to: path/to/file/file_name.env_name.file_extension
89
+
90
+ pathname = Pathname.new(file_path)
91
+ dirname = pathname.dirname
92
+ extname = pathname.extname.to_s
93
+ basename = pathname.basename.to_s.sub!(extname, '')
94
+ envname = [env.to_s, extname].reject(&:empty?).join('')
95
+ envfile = [basename, envname].reject(&:empty?).join('.')
96
+ realfile = dirname.join(envfile).to_s
97
+
98
+ yaml_data = load_yaml_data(realfile)
99
+ yaml_based_settings = build_data_set_class(yaml_data).new.settings
100
+
101
+ settings.__append_settings__(yaml_based_settings)
102
+ end
103
+ # rubocop:enable Metrics/AbcSize
104
+
105
+ # @param settings [Qonfig::Settings]
106
+ # @return [void]
107
+ #
108
+ # @raise [Qonfig::ExposeError]
109
+ # @raise [Qonfig::IncompatibleYAMLStructureError]
110
+ #
111
+ # @api private
112
+ # @since 0.7.0
113
+ # rubocop:disable Metrics/AbcSize
114
+ def expose_env_key!(settings)
115
+ yaml_data = load_yaml_data(file_path)
116
+ yaml_data_slice = yaml_data[env] || yaml_data[env.to_s] || yaml_data[env.to_sym]
117
+ yaml_data_slice = EMPTY_YAML_DATA.dup if yaml_data_slice.nil? && !strict
118
+
119
+ raise(
120
+ Qonfig::ExposeError,
121
+ "#{file_path} file does not contain settings with <#{env}> environment key!"
122
+ ) unless yaml_data_slice
123
+
124
+ raise(
125
+ Qonfig::IncompatibleYAMLStructureError,
126
+ 'YAML content should have a hash-like structure'
127
+ ) unless yaml_data_slice.is_a?(Hash)
128
+
129
+ yaml_based_settings = build_data_set_class(yaml_data_slice).new.settings
130
+
131
+ settings.__append_settings__(yaml_based_settings)
132
+ end
133
+ # rubocop:enable Metrics/AbcSize
134
+
135
+ # @param file_path [String]
136
+ # @return [Hash]
137
+ #
138
+ # @raise [Qonfig::IncompatibleYAMLStructureError]
139
+ #
140
+ # @api private
141
+ # @since 0.7.0
142
+ def load_yaml_data(file_path)
143
+ Qonfig::Loaders::YAML.load_file(file_path, fail_on_unexist: strict).tap do |yaml_data|
144
+ raise(
145
+ Qonfig::IncompatibleYAMLStructureError,
146
+ 'YAML content should have a hash-like structure'
147
+ ) unless yaml_data.is_a?(Hash)
148
+ end
149
+ end
150
+
151
+ # @param yaml_data [Hash]
152
+ # @return [Class<Qonfig::DataSet>]
153
+ #
154
+ # @api private
155
+ # @since 0.7.0
156
+ def build_data_set_class(yaml_data)
157
+ Qonfig::DataSet::ClassBuilder.build_from_hash(yaml_data)
162
158
  end
163
159
  end
@@ -1,97 +1,95 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Qonfig
4
- module Commands
5
- # @api private
6
- # @since 0.2.0
7
- class LoadFromENV < Base
8
- # @return [Boolean]
9
- #
10
- # @api private
11
- # @since 0.2.0
12
- attr_reader :convert_values
3
+ # @api private
4
+ # @since 0.2.0
5
+ class Qonfig::Commands::LoadFromENV < Qonfig::Commands::Base
6
+ require_relative 'load_from_env/value_converter'
13
7
 
14
- # @return [Regexp]
15
- #
16
- # @api private
17
- # @since 0.2.0
18
- attr_reader :prefix_pattern
8
+ # @return [Boolean]
9
+ #
10
+ # @api private
11
+ # @since 0.2.0
12
+ attr_reader :convert_values
19
13
 
20
- # @return [Boolean]
21
- #
22
- # @api private
23
- # @since 0.2.0
24
- attr_reader :trim_prefix
14
+ # @return [Regexp]
15
+ #
16
+ # @api private
17
+ # @since 0.2.0
18
+ attr_reader :prefix_pattern
25
19
 
26
- # @return [Regexp]
27
- #
28
- # @api private
29
- # @since 0.2.0
30
- attr_reader :trim_pattern
20
+ # @return [Boolean]
21
+ #
22
+ # @api private
23
+ # @since 0.2.0
24
+ attr_reader :trim_prefix
31
25
 
32
- # @option convert_values [Boolean]
33
- # @opion prefix [NilClass, String, Regexp]
34
- #
35
- # @raise [Qonfig::ArgumentError]
36
- #
37
- # @api private
38
- # @since 0.2.0
39
- def initialize(convert_values: false, prefix: nil, trim_prefix: false)
40
- unless convert_values.is_a?(FalseClass) || convert_values.is_a?(TrueClass)
41
- raise Qonfig::ArgumentError, ':convert_values option should be a boolean'
42
- end
26
+ # @return [Regexp]
27
+ #
28
+ # @api private
29
+ # @since 0.2.0
30
+ attr_reader :trim_pattern
43
31
 
44
- unless prefix.is_a?(NilClass) || prefix.is_a?(String) || prefix.is_a?(Regexp)
45
- raise Qonfig::ArgumentError, ':prefix option should be a nil / string / regexp'
46
- end
32
+ # @option convert_values [Boolean]
33
+ # @opion prefix [NilClass, String, Regexp]
34
+ #
35
+ # @raise [Qonfig::ArgumentError]
36
+ #
37
+ # @api private
38
+ # @since 0.2.0
39
+ def initialize(convert_values: false, prefix: nil, trim_prefix: false)
40
+ unless convert_values.is_a?(FalseClass) || convert_values.is_a?(TrueClass)
41
+ raise Qonfig::ArgumentError, ':convert_values option should be a boolean'
42
+ end
47
43
 
48
- unless trim_prefix.is_a?(FalseClass) || trim_prefix.is_a?(TrueClass)
49
- raise Qonfig::ArgumentError, ':trim_refix options should be a boolean'
50
- end
44
+ unless prefix.is_a?(NilClass) || prefix.is_a?(String) || prefix.is_a?(Regexp)
45
+ raise Qonfig::ArgumentError, ':prefix option should be a nil / string / regexp'
46
+ end
51
47
 
52
- @convert_values = convert_values
53
- @prefix_pattern = prefix.is_a?(Regexp) ? prefix : /\A#{Regexp.escape(prefix.to_s)}.*\z/m
54
- @trim_prefix = trim_prefix
55
- @trim_pattern = prefix.is_a?(Regexp) ? prefix : /\A(#{Regexp.escape(prefix.to_s)})/m
56
- end
48
+ unless trim_prefix.is_a?(FalseClass) || trim_prefix.is_a?(TrueClass)
49
+ raise Qonfig::ArgumentError, ':trim_refix options should be a boolean'
50
+ end
57
51
 
58
- # @param settings [Qonfig::Settings]
59
- # @return [void]
60
- #
61
- # @api private
62
- # @since 0.2.0
63
- def call(settings)
64
- env_data = extract_env_data
52
+ @convert_values = convert_values
53
+ @prefix_pattern = prefix.is_a?(Regexp) ? prefix : /\A#{Regexp.escape(prefix.to_s)}.*\z/m
54
+ @trim_prefix = trim_prefix
55
+ @trim_pattern = prefix.is_a?(Regexp) ? prefix : /\A(#{Regexp.escape(prefix.to_s)})/m
56
+ end
65
57
 
66
- env_based_settings = build_data_set_class(env_data).new.settings
58
+ # @param settings [Qonfig::Settings]
59
+ # @return [void]
60
+ #
61
+ # @api private
62
+ # @since 0.2.0
63
+ def call(settings)
64
+ env_data = extract_env_data
67
65
 
68
- settings.__append_settings__(env_based_settings)
69
- end
66
+ env_based_settings = build_data_set_class(env_data).new.settings
70
67
 
71
- private
68
+ settings.__append_settings__(env_based_settings)
69
+ end
72
70
 
73
- # @return [Hash]
74
- #
75
- # @api private
76
- # @since 0.2.0
77
- def extract_env_data
78
- ENV.each_with_object({}) do |(key, value), env_data|
79
- next unless key.match(prefix_pattern)
80
- key = key.sub(trim_pattern, '') if trim_prefix
81
- env_data[key] = value
82
- end.tap do |env_data|
83
- ValueConverter.convert_values!(env_data) if convert_values
84
- end
85
- end
71
+ private
86
72
 
87
- # @param env_data [Hash]
88
- # @return [Class<Qonfig::DataSet>]
89
- #
90
- # @api private
91
- # @since 0.2.0
92
- def build_data_set_class(env_data)
93
- Qonfig::DataSet::ClassBuilder.build_from_hash(env_data)
94
- end
73
+ # @return [Hash]
74
+ #
75
+ # @api private
76
+ # @since 0.2.0
77
+ def extract_env_data
78
+ ENV.each_with_object({}) do |(key, value), env_data|
79
+ next unless key.match(prefix_pattern)
80
+ key = key.sub(trim_pattern, '') if trim_prefix
81
+ env_data[key] = value
82
+ end.tap do |env_data|
83
+ ValueConverter.convert_values!(env_data) if convert_values
95
84
  end
96
85
  end
86
+
87
+ # @param env_data [Hash]
88
+ # @return [Class<Qonfig::DataSet>]
89
+ #
90
+ # @api private
91
+ # @since 0.2.0
92
+ def build_data_set_class(env_data)
93
+ Qonfig::DataSet::ClassBuilder.build_from_hash(env_data)
94
+ end
97
95
  end
@@ -1,60 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Qonfig
4
- module Commands
5
- # @api private
6
- # @since 0.5.0
7
- class LoadFromJSON < Base
8
- # @return [String]
9
- #
10
- # @api private
11
- # @since 0.5.0
12
- attr_reader :file_path
13
-
14
- # @return [Boolean]
15
- #
16
- # @api private
17
- # @sicne 0.5.0
18
- attr_reader :strict
19
-
20
- # @param file_path [String]
21
- # @option strict [Boolean]
22
- #
23
- # @api private
24
- # @since 0.5.0
25
- def initialize(file_path, strict: true)
26
- @file_path = file_path
27
- @strict = strict
28
- end
29
-
30
- # @param settings [Qonfig::Settings]
31
- # @return [void]
32
- #
33
- # @api private
34
- # @since 0.5.0
35
- def call(settings)
36
- json_data = Qonfig::Loaders::JSON.load_file(file_path, fail_on_unexist: strict)
37
-
38
- raise(
39
- Qonfig::IncompatibleJSONStructureError,
40
- 'JSON object should have a hash-like structure'
41
- ) unless json_data.is_a?(Hash)
42
-
43
- json_based_settings = build_data_set_class(json_data).new.settings
44
-
45
- settings.__append_settings__(json_based_settings)
46
- end
47
-
48
- private
49
-
50
- # @param json_data [Hash]
51
- # @return [Class<Qonfig::DataSet>]
52
- #
53
- # @api private
54
- # @since 0.5.0
55
- def build_data_set_class(json_data)
56
- Qonfig::DataSet::ClassBuilder.build_from_hash(json_data)
57
- end
58
- end
3
+ # @api private
4
+ # @since 0.5.0
5
+ class Qonfig::Commands::LoadFromJSON < Qonfig::Commands::Base
6
+ # @return [String]
7
+ #
8
+ # @api private
9
+ # @since 0.5.0
10
+ attr_reader :file_path
11
+
12
+ # @return [Boolean]
13
+ #
14
+ # @api private
15
+ # @sicne 0.5.0
16
+ attr_reader :strict
17
+
18
+ # @param file_path [String]
19
+ # @option strict [Boolean]
20
+ #
21
+ # @api private
22
+ # @since 0.5.0
23
+ def initialize(file_path, strict: true)
24
+ @file_path = file_path
25
+ @strict = strict
26
+ end
27
+
28
+ # @param settings [Qonfig::Settings]
29
+ # @return [void]
30
+ #
31
+ # @api private
32
+ # @since 0.5.0
33
+ def call(settings)
34
+ json_data = Qonfig::Loaders::JSON.load_file(file_path, fail_on_unexist: strict)
35
+
36
+ raise(
37
+ Qonfig::IncompatibleJSONStructureError,
38
+ 'JSON object should have a hash-like structure'
39
+ ) unless json_data.is_a?(Hash)
40
+
41
+ json_based_settings = build_data_set_class(json_data).new.settings
42
+
43
+ settings.__append_settings__(json_based_settings)
44
+ end
45
+
46
+ private
47
+
48
+ # @param json_data [Hash]
49
+ # @return [Class<Qonfig::DataSet>]
50
+ #
51
+ # @api private
52
+ # @since 0.5.0
53
+ def build_data_set_class(json_data)
54
+ Qonfig::DataSet::ClassBuilder.build_from_hash(json_data)
59
55
  end
60
56
  end