unifig 0.1.0

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