unifig 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dbc1ebd81be5210930760f06ea0c36849862425a9f0831512efed9d4cc0a81a9
4
+ data.tar.gz: 06c545484d1094f7a8d8609b48d077f602c689d0e505e01157e80d3adbedecb1
5
+ SHA512:
6
+ metadata.gz: b6c024e5b3aefe1a2bba0da9908352a489263afa1d8deb047b9a0a54b29dfe621f23a71266bf12ae1a332c7a6d62a3871b596540e12f0a52a5ed62065b3be219
7
+ data.tar.gz: 0ab9b4da14ee56d343dfbb95c6e73cdb81e924a8027686276c6c007bed49a017e0933a9023e644a4c8cd948d13c73b4963ef6d6b1bb9c6ec6ae2038120fa69f2
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # [0.1.0][] (2022-07-18)
2
+
3
+ Initial release.
4
+
5
+ [0.1.0]: https://github.com/AaronLasseigne/unifig/compare/v0.0.0...v0.1.0
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,19 @@
1
+ # Contributing
2
+
3
+ For features please open a [discussion][] and we can make sure the feature fits with the gem before working on it.
4
+
5
+ ## Steps
6
+
7
+ 1. [Fork][] the repo.
8
+ 2. Add a breaking test for your change.
9
+ 3. Make the tests pass.
10
+ 4. Push your fork.
11
+ 5. Submit a pull request.
12
+
13
+ ## Code Style
14
+
15
+ Running the tests using `rake` (with no args) will also check for style issues in the code.
16
+ If you have a failure you cannot figure out push the PR and ask for help.
17
+
18
+ [fork]: https://github.com/AaronLasseigne/unifig/fork
19
+ [discussion]: https://github.com/AaronLasseigne/unifig/discussions/categories/ideas
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Aaron Lasseigne
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # [Unifig][]
2
+
3
+ Unifig lets you load external variables from one or more providers (e.g. local file, `ENV`).
4
+
5
+ [![Version](https://img.shields.io/gem/v/unifig.svg?style=flat-square)](https://rubygems.org/gems/unifig)
6
+ [![Test](https://img.shields.io/github/workflow/status/AaronLasseigne/unifig/Test?label=Test&style=flat-square)](https://github.com/AaronLasseigne/unifig/actions?query=workflow%3ATest)
7
+
8
+ ## Installation
9
+
10
+ Add it to your Gemfile:
11
+
12
+ ``` rb
13
+ gem 'unifig', '~> 0.1.0'
14
+ ```
15
+
16
+ Or install it manually:
17
+
18
+ ``` sh
19
+ $ gem install unifig --version '~> 0.1.0'
20
+ ```
21
+
22
+ This project uses [Semantic Versioning][].
23
+ Check out [GitHub releases][] for a detailed list of changes.
24
+
25
+ ## Usage
26
+
27
+ ### Loading
28
+
29
+ Unifig loads a [YAML configuration][] which it uses to create methods allowing access to the variable values for a given environment.
30
+ Loading is handled through the `Unifig::Init` class and methods are made available on the `Unifig` class.
31
+ From `Unifig::Init` you can load the YAML as a string with `.load` or a file with `.load_file`.
32
+
33
+ All variables are converted into two methods on the `Unifig` class.
34
+ A predicate method shows whether the variable was provided and a regular method provides access to the value.
35
+
36
+ All variables are assumed to be required (not `nil` or a blank string).
37
+ They can be made optional using the `:optional` setting.
38
+ If there is a required variable without a value, Unifig will throw an error when loading.
39
+
40
+ ``` rb
41
+ Unifig::Init.load(<<~YAML, :production)
42
+ config:
43
+ envs:
44
+ development:
45
+ providers: local
46
+ production:
47
+ providers: local
48
+
49
+ HOST:
50
+ value: github.com
51
+ envs:
52
+ development:
53
+ value: localhost
54
+ PORT:
55
+ optional: true
56
+ envs:
57
+ development:
58
+ value: 8080
59
+ YAML
60
+
61
+ > Unifig.host?
62
+ # true
63
+ > Unifig.host
64
+ # => "localhost"
65
+ > Unifig.port?
66
+ # true
67
+ > Unifig.port
68
+ # => 8080
69
+ ```
70
+
71
+ If we replaced `:development` with `:production` inside the `load` call we'd get:
72
+
73
+ ``` rb
74
+ > Unifig.host?
75
+ # true
76
+ > Unifig.host
77
+ # => "github.com"
78
+ > Unifig.port?
79
+ # false
80
+ > Unifig.port
81
+ # => nil
82
+ ```
83
+
84
+ [API Documentation][]
85
+
86
+ ### YAML Configuration
87
+
88
+ The configuration YAML must contain a `config` key.
89
+ Inside that all environments to be used must be listed under the `envs` key using the environment name.
90
+ Each environment must list a provider or providers that are checked in order to find the value for each variable.
91
+ Variables are then listed by name.
92
+
93
+ The first level of settings apply to all environments.
94
+ These can be overridden by setting the `envs` key and a key with the name of then environment and then redefining any settings.
95
+
96
+ ### Providers
97
+
98
+ | Provider | Gem |
99
+ | -------- | -------- |
100
+ | Local | Built-in |
101
+
102
+ ## Contributing
103
+
104
+ If you want to contribute to Unifig, please read [our contribution guidelines][].
105
+ A [complete list of contributors][] is available on GitHub.
106
+
107
+ ## License
108
+
109
+ Unifig is licensed under [the MIT License][].
110
+
111
+ [Unifig]: https://github.com/AaronLasseigne/unifig
112
+ [Semantic Versioning]: http://semver.org/spec/v2.0.0.html
113
+ [GitHub releases]: https://github.com/AaronLasseigne/unifig/releases
114
+ [YAML configuration]: #yaml-configuration
115
+ [API Documentation]: http://rubydoc.info/github/AaronLasseigne/unifig
116
+ [our contribution guidelines]: CONTRIBUTING.md
117
+ [complete list of contributors]: https://github.com/AaronLasseigne/unifig/graphs/contributors
118
+ [the MIT License]: LICENSE.txt
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unifig
4
+ # @private
5
+ class Config
6
+ def initialize(config, env)
7
+ @config = config
8
+ @env = @config.dig(:envs, env)
9
+ end
10
+
11
+ attr_reader :env
12
+
13
+ def providers
14
+ @providers ||= Array(env[:providers]).map(&:to_sym).freeze
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unifig
4
+ # Top-level error class. All other errors subclass this.
5
+ Error = Class.new(StandardError)
6
+
7
+ # Raised if the YAML in invalid.
8
+ YAMLSyntaxError = Class.new(Error)
9
+
10
+ # Raised if there is no config at the start of the YAML.
11
+ MissingConfig = Class.new(Error)
12
+
13
+ # Raised if there is no provider that matches the one given in the config.
14
+ MissingProvider = Class.new(Error)
15
+
16
+ # Raised if a required var is blank.
17
+ MissingRequired = Class.new(Error)
18
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Unifig
6
+ # Initializes Unifig with methods based on YAML.
7
+ class Init
8
+ # Loads a string of YAML to configure Unifig.
9
+ #
10
+ # @example
11
+ # Unifig::Init.load(<<~YML, :development)
12
+ # config:
13
+ # envs:
14
+ # development:
15
+ # providers: local
16
+ #
17
+ # FOO_BAR:
18
+ # value: "baz"
19
+ # YML
20
+ #
21
+ # @param str [String] A YAML config.
22
+ # @param env [Symbol] An environment name to load.
23
+ #
24
+ # @raise [YAMLSyntaxError] - Invalid YAML
25
+ # @raise (see #initialize)
26
+ # @raise (see Unifig::Providers.list)
27
+ def self.load(str, env)
28
+ yml = Psych.load(str, symbolize_names: true)
29
+ new(yml, env).exec!
30
+ rescue Psych::SyntaxError, Psych::BadAlias => e
31
+ raise YAMLSyntaxError, e.message
32
+ end
33
+
34
+ # Loads a YAML file to configure Unifig.
35
+ #
36
+ # @example
37
+ # Unifig::Init.load_file('config.yml', :development)
38
+ #
39
+ # @param file_path [String] The path to a YAML config file.
40
+ # @param env [Symbol] An environment name to load.
41
+ #
42
+ # @raise (see Unifig::Init.load)
43
+ def self.load_file(file_path, env)
44
+ # Ruby 2.7 Psych.load_file doesn't support the :symbolize_names flag.
45
+ # After Ruby 2.7 this can be changed to Psych.load_file if that's faster.
46
+ load(File.read(file_path), env)
47
+ end
48
+
49
+ # @private
50
+ #
51
+ # @raise [MissingConfig] - No config section was provided in the YAML.
52
+ def initialize(yml, env)
53
+ @yml = yml
54
+ @env = env
55
+
56
+ config = @yml.delete(:config)
57
+ raise MissingConfig unless config
58
+
59
+ @config = Config.new(config, @env)
60
+ end
61
+
62
+ # @private
63
+ def exec!
64
+ providers = Providers.list(@config.providers)
65
+ return if providers.empty?
66
+
67
+ vars = vars_and_set_local
68
+
69
+ providers.each do |provider|
70
+ vars = fetch_and_set_methods(provider, vars)
71
+ end
72
+
73
+ required_vars, optional_vars = vars.values.partition(&:required?)
74
+ if required_vars.any?
75
+ raise MissingRequired, <<~MSG
76
+ Missing Required Vars: #{required_vars.map(&:name).join(', ')}
77
+ MSG
78
+ end
79
+
80
+ attach_optional_methods(optional_vars)
81
+ end
82
+
83
+ private
84
+
85
+ def vars_and_set_local
86
+ vars = {}
87
+ local_values = {}
88
+ @yml.each do |name, local_config|
89
+ local_config = {} if local_config.nil?
90
+
91
+ vars[name] = Var.new(name, local_config, @env)
92
+ local_values[name] = vars[name].local_value
93
+ end
94
+ Unifig::Providers::Local.load(local_values)
95
+ vars
96
+ end
97
+
98
+ def fetch_and_set_methods(provider, vars)
99
+ values = provider.retrieve(vars.keys)
100
+ values.each do |name, value|
101
+ next values.delete(name) if blank_string?(value)
102
+
103
+ attach_method(vars[name], value)
104
+ attach_predicate(vars[name], true)
105
+ end
106
+ vars.slice(*(vars.keys - values.keys)) # switch to except after 2.7
107
+ end
108
+
109
+ def blank_string?(value)
110
+ value.respond_to?(:to_str) && value.to_str.strip.empty?
111
+ end
112
+
113
+ def attach_optional_methods(vars)
114
+ vars.each do |var|
115
+ attach_method(var, nil)
116
+ attach_predicate(var, false)
117
+ end
118
+ end
119
+
120
+ def attach_method(var, value)
121
+ Unifig.define_singleton_method(var.method) do
122
+ value
123
+ end
124
+ end
125
+
126
+ def attach_predicate(var, bool)
127
+ Unifig.define_singleton_method(:"#{var.method}?") do
128
+ bool
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unifig
4
+ module Providers
5
+ # A provider to retrieve values from the unifig.yml file.
6
+ module Local
7
+ # Returns the name of the provider.
8
+ #
9
+ # @return [Symbol]
10
+ def self.name
11
+ :local
12
+ end
13
+
14
+ # @private
15
+ def self.load(data)
16
+ @data = data
17
+ end
18
+
19
+ def self.retrieve(var_names)
20
+ local_values = var_names.to_h do |name|
21
+ [name, @data[name]]
22
+ end
23
+ local_values.compact!
24
+ local_values
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unifig
4
+ # @private
5
+ module Providers
6
+ # @raise [MissingProvider] - The given provider is not in the list of available providers.
7
+ def self.list(providers = nil)
8
+ return all if providers.nil?
9
+
10
+ providers.map do |provider|
11
+ all.detect { |pp| pp.name == provider }.tap do |found|
12
+ raise MissingProvider, %("#{provider}" is not in the list of available providers) unless found
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.all
18
+ @all ||= constants(false).map { |c| const_get(c, false) }.freeze
19
+ end
20
+ end
21
+ end
data/lib/unifig/var.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unifig
4
+ # @private
5
+ class Var
6
+ def initialize(name, config, env)
7
+ @name = name
8
+ @config = config
9
+ @env = env
10
+ end
11
+
12
+ attr_reader :name, :config, :env
13
+
14
+ def method
15
+ @method ||= name.to_s.downcase.tr('-', '_').to_sym
16
+ end
17
+
18
+ def local_value
19
+ @local_value ||= env_config(:value) || config[:value]
20
+ end
21
+
22
+ def required?
23
+ return @required if defined?(@required)
24
+
25
+ optional = env_config(:optional)
26
+ optional = config[:optional] if optional.nil?
27
+ optional = false if optional.nil?
28
+ @required = !optional
29
+ end
30
+
31
+ private
32
+
33
+ def env_config(key)
34
+ config.dig(:envs, env, key)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unifig
4
+ VERSION = '0.1.0'
5
+ end
data/lib/unifig.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'unifig/version'
4
+
5
+ require_relative 'unifig/errors'
6
+ require_relative 'unifig/config'
7
+ require_relative 'unifig/var'
8
+ require_relative 'unifig/init'
9
+ require_relative 'unifig/providers'
10
+ require_relative 'unifig/providers/local'
11
+
12
+ # Handle all your configuration variables.
13
+ module Unifig
14
+ end
@@ -0,0 +1,56 @@
1
+ require 'unifig'
2
+
3
+ RSpec.configure do |config|
4
+ config.expect_with :rspec do |expectations|
5
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
6
+ expectations.on_potential_false_positives = :nothing
7
+ end
8
+
9
+ config.mock_with :rspec do |mocks|
10
+ mocks.verify_partial_doubles = true
11
+ end
12
+
13
+ config.shared_context_metadata_behavior = :apply_to_host_groups
14
+
15
+ config.filter_run_when_matching :focus
16
+
17
+ config.example_status_persistence_file_path = 'spec/examples.txt'
18
+
19
+ config.disable_monkey_patching!
20
+
21
+ config.warnings = true
22
+
23
+ config.default_formatter = 'doc' if config.files_to_run.one?
24
+
25
+ config.order = :random
26
+ Kernel.srand config.seed
27
+
28
+ # clear Unifg methods
29
+ config.before do
30
+ Unifig.methods(false).each do |name|
31
+ Unifig.singleton_class.remove_method(name)
32
+ end
33
+ end
34
+
35
+ # Add a fake Provider for testing
36
+ config.before(:suite) do
37
+ module Unifig # rubocop:disable Lint/ConstantDefinitionInBlock
38
+ module Providers
39
+ module FortyTwo
40
+ def self.name
41
+ :forty_two
42
+ end
43
+
44
+ def self.retrieve(var_names)
45
+ var_names.to_h do |var_name|
46
+ [var_name, 42]
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ config.after(:suite) do
54
+ Unifig::Providers.send(:remove_const, :FortyTwo)
55
+ end
56
+ end
@@ -0,0 +1,29 @@
1
+ RSpec.describe Unifig::Config do
2
+ subject(:config) { described_class.new(config_hash, env) }
3
+
4
+ let(:env) { :development }
5
+ let(:env_config) do
6
+ {
7
+ providers: 'local'
8
+ }
9
+ end
10
+ let(:config_hash) do
11
+ {
12
+ envs: {
13
+ "#{env}": env_config
14
+ }
15
+ }
16
+ end
17
+
18
+ describe '#env' do
19
+ it 'returns the env config' do
20
+ expect(config.env).to eql env_config
21
+ end
22
+ end
23
+
24
+ describe '#providers' do
25
+ it 'returns a list of providers for the selected env' do
26
+ expect(config.providers).to eql %i[local]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,233 @@
1
+ require 'tempfile'
2
+
3
+ RSpec.shared_examples 'basic load tests' do
4
+ context 'with invalid YAML' do
5
+ let(:str) { '`' }
6
+
7
+ it 'throws an error' do
8
+ expect { subject }.to raise_error Unifig::YAMLSyntaxError
9
+ end
10
+ end
11
+
12
+ context 'with an invalid alias' do
13
+ let(:str) { 'a: *b' }
14
+
15
+ it 'throws an error' do
16
+ expect { subject }.to raise_error Unifig::YAMLSyntaxError
17
+ end
18
+ end
19
+
20
+ context 'without a config' do
21
+ let(:str) do
22
+ <<~YML
23
+ FOO_BAR:
24
+ value: "baz"
25
+ YML
26
+ end
27
+
28
+ it 'throws an error' do
29
+ expect { subject }.to raise_error Unifig::MissingConfig
30
+ end
31
+ end
32
+
33
+ context 'with a config' do
34
+ let(:str) do
35
+ <<~YML
36
+ config:
37
+ envs:
38
+ development:
39
+ providers: local
40
+
41
+ FOO_BAR:
42
+ value: baz
43
+ YML
44
+ end
45
+
46
+ it 'loads up a string of yaml' do
47
+ subject
48
+
49
+ expect(Unifig).to respond_to(:foo_bar)
50
+ expect(Unifig.foo_bar).to eql 'baz'
51
+ end
52
+ end
53
+ end
54
+
55
+ RSpec.describe Unifig::Init do
56
+ let(:env) { :development }
57
+
58
+ describe '.load' do
59
+ subject(:load) { described_class.load(str, env) }
60
+
61
+ include_examples 'basic load tests'
62
+
63
+ context 'from multiple providers' do
64
+ let(:str) do
65
+ <<~YML
66
+ config:
67
+ envs:
68
+ development:
69
+ providers: [local, forty_two]
70
+
71
+ FOO:
72
+ BAR:
73
+ value: bar
74
+ YML
75
+ end
76
+
77
+ it 'returns the values from the providers in order' do
78
+ load
79
+
80
+ expect(Unifig.foo).to be 42
81
+ expect(Unifig.bar).to eql 'bar'
82
+ end
83
+ end
84
+
85
+ context 'with an optional var' do
86
+ before { load }
87
+
88
+ context 'that is available' do
89
+ let(:str) do
90
+ <<~YML
91
+ config:
92
+ envs:
93
+ development:
94
+ providers: local
95
+
96
+ FOO_BAR:
97
+ optional: true
98
+ value: baz
99
+ YML
100
+ end
101
+
102
+ it 'loads the var' do
103
+ expect(Unifig.foo_bar).to eql 'baz'
104
+ end
105
+
106
+ it 'sets the predicate to true' do
107
+ expect(Unifig).to be_foo_bar
108
+ end
109
+ end
110
+
111
+ context 'that is not available' do
112
+ let(:str) do
113
+ <<~YML
114
+ config:
115
+ envs:
116
+ development:
117
+ providers: local
118
+
119
+ FOO_BAR:
120
+ optional: true
121
+ YML
122
+ end
123
+
124
+ it 'makes the var nil' do
125
+ expect(Unifig.foo_bar).to be_nil
126
+ end
127
+
128
+ it 'sets the predicate to false' do
129
+ expect(Unifig).to_not be_foo_bar
130
+ end
131
+ end
132
+
133
+ context 'that is not blank' do
134
+ let(:str) do
135
+ <<~YML
136
+ config:
137
+ envs:
138
+ development:
139
+ providers: local
140
+
141
+ FOO_BAR:
142
+ optional: true
143
+ value: ' '
144
+ YML
145
+ end
146
+
147
+ it 'makes the var nil' do
148
+ expect(Unifig.foo_bar).to be_nil
149
+ end
150
+
151
+ it 'sets the predicate to false' do
152
+ expect(Unifig).to_not be_foo_bar
153
+ end
154
+ end
155
+ end
156
+
157
+ context 'with a required var' do
158
+ context 'that is available' do
159
+ before { load }
160
+
161
+ let(:str) do
162
+ <<~YML
163
+ config:
164
+ envs:
165
+ development:
166
+ providers: local
167
+
168
+ FOO_BAR:
169
+ value: baz
170
+ YML
171
+ end
172
+
173
+ it 'loads the var' do
174
+ expect(Unifig.foo_bar).to eql 'baz'
175
+ end
176
+
177
+ it 'sets the predicate to true' do
178
+ expect(Unifig).to be_foo_bar
179
+ end
180
+ end
181
+
182
+ context 'that is not available' do
183
+ let(:str) do
184
+ <<~YML
185
+ config:
186
+ envs:
187
+ development:
188
+ providers: local
189
+
190
+ FOO_BAR:
191
+ value:
192
+ YML
193
+ end
194
+
195
+ it 'throws an error' do
196
+ expect { load }.to raise_error Unifig::MissingRequired
197
+ end
198
+ end
199
+
200
+ context 'that is blank' do
201
+ let(:str) do
202
+ <<~YML
203
+ config:
204
+ envs:
205
+ development:
206
+ providers: local
207
+
208
+ FOO_BAR:
209
+ value: ' '
210
+ YML
211
+ end
212
+
213
+ it 'throws an error' do
214
+ expect { load }.to raise_error Unifig::MissingRequired
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ describe '.load_file' do
221
+ subject(:load_file) { described_class.load_file(file_path, env) }
222
+
223
+ let(:file) do
224
+ Tempfile.new(%w[test .yml]).tap do |file|
225
+ file.write(str)
226
+ file.close
227
+ end
228
+ end
229
+ let(:file_path) { file.path }
230
+
231
+ include_examples 'basic load tests'
232
+ end
233
+ end
@@ -0,0 +1,36 @@
1
+ RSpec.describe Unifig::Providers do
2
+ describe '.all' do
3
+ it 'returns all of the provider modules' do
4
+ all = described_class.all
5
+
6
+ expect(all.size).to be 2
7
+ expect(all).to include Unifig::Providers::Local
8
+ expect(all).to include Unifig::Providers::FortyTwo
9
+ end
10
+ end
11
+
12
+ describe '.list' do
13
+ it 'returns all providers' do
14
+ list = described_class.list
15
+
16
+ expect(list.size).to be 2
17
+ expect(list).to include Unifig::Providers::Local
18
+ expect(list).to include Unifig::Providers::FortyTwo
19
+ end
20
+
21
+ it 'can return a limited list' do
22
+ list = described_class.list([Unifig::Providers::Local.name])
23
+
24
+ expect(list.size).to be 1
25
+ expect(list).to include Unifig::Providers::Local
26
+ end
27
+
28
+ it 'returns them in the order requested' do
29
+ list = described_class.list([Unifig::Providers::Local.name, Unifig::Providers::FortyTwo.name])
30
+ expect(list).to eql [Unifig::Providers::Local, Unifig::Providers::FortyTwo]
31
+
32
+ list = described_class.list([Unifig::Providers::FortyTwo.name, Unifig::Providers::Local.name])
33
+ expect(list).to eql [Unifig::Providers::FortyTwo, Unifig::Providers::Local]
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,91 @@
1
+ RSpec.describe Unifig::Var do
2
+ subject(:var) { described_class.new(name, config, env) }
3
+
4
+ let(:name) { :NAME }
5
+ let(:config) { {} }
6
+ let(:env) { :development }
7
+
8
+ describe '#method' do
9
+ let(:name) { :'A-B' }
10
+
11
+ it 'lowercases and switches dashes to underscores' do
12
+ expect(var.method).to be :a_b
13
+ end
14
+ end
15
+
16
+ describe '#local_value' do
17
+ context 'with no value' do
18
+ it 'returns nil' do
19
+ expect(var.local_value).to be_nil
20
+ end
21
+ end
22
+
23
+ context 'with a top level value' do
24
+ let(:value) { 'value' }
25
+ let(:config) do
26
+ {
27
+ value: value
28
+ }
29
+ end
30
+
31
+ it 'returns the value' do
32
+ expect(var.local_value).to eql value
33
+ end
34
+
35
+ context 'with an override' do
36
+ let(:config) do
37
+ {
38
+ value: "#{value}-1",
39
+ envs: {
40
+ env => {
41
+ value: value
42
+ }
43
+ }
44
+ }
45
+ end
46
+
47
+ it 'returns the override' do
48
+ expect(var.local_value).to eql value
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ describe '#required?' do
55
+ context 'with no value' do
56
+ it 'returns true' do
57
+ expect(var).to be_required
58
+ end
59
+ end
60
+
61
+ context 'with a top level value' do
62
+ let(:value) { 'value' }
63
+ let(:config) do
64
+ {
65
+ optional: true
66
+ }
67
+ end
68
+
69
+ it 'returns the value' do
70
+ expect(var).to_not be_required
71
+ end
72
+
73
+ context 'with an override' do
74
+ let(:config) do
75
+ {
76
+ optional: false,
77
+ envs: {
78
+ env => {
79
+ optional: true
80
+ }
81
+ }
82
+ }
83
+ end
84
+
85
+ it 'returns the override' do
86
+ expect(var).to_not be_required
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unifig
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Lasseigne
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-07-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A way to load external variables from one or more sources (e.g. ENV).
14
+ email:
15
+ - aaron.lasseigne@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - CHANGELOG.md
21
+ - CONTRIBUTING.md
22
+ - LICENSE.txt
23
+ - README.md
24
+ - lib/unifig.rb
25
+ - lib/unifig/config.rb
26
+ - lib/unifig/errors.rb
27
+ - lib/unifig/init.rb
28
+ - lib/unifig/providers.rb
29
+ - lib/unifig/providers/local.rb
30
+ - lib/unifig/var.rb
31
+ - lib/unifig/version.rb
32
+ - spec/spec_helper.rb
33
+ - spec/unifig/config_spec.rb
34
+ - spec/unifig/init_spec.rb
35
+ - spec/unifig/providers_spec.rb
36
+ - spec/unifig/var_spec.rb
37
+ homepage: https://github.com/AaronLasseigne/unifig
38
+ licenses:
39
+ - MIT
40
+ metadata:
41
+ homepage_uri: https://github.com/AaronLasseigne/unifig
42
+ source_code_uri: https://github.com/AaronLasseigne/unifig
43
+ changelog_uri: https://github.com/AaronLasseigne/unifig/blob/main/CHANGELOG.md
44
+ rubygems_mfa_required: 'true'
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 2.7.0
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubygems_version: 3.3.7
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: A way to load external variables from one or more sources (e.g. ENV).
64
+ test_files:
65
+ - spec/spec_helper.rb
66
+ - spec/unifig/config_spec.rb
67
+ - spec/unifig/init_spec.rb
68
+ - spec/unifig/providers_spec.rb
69
+ - spec/unifig/var_spec.rb