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 +7 -0
- data/CHANGELOG.md +5 -0
- data/CONTRIBUTING.md +19 -0
- data/LICENSE.txt +21 -0
- data/README.md +118 -0
- data/lib/unifig/config.rb +17 -0
- data/lib/unifig/errors.rb +18 -0
- data/lib/unifig/init.rb +132 -0
- data/lib/unifig/providers/local.rb +28 -0
- data/lib/unifig/providers.rb +21 -0
- data/lib/unifig/var.rb +37 -0
- data/lib/unifig/version.rb +5 -0
- data/lib/unifig.rb +14 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/unifig/config_spec.rb +29 -0
- data/spec/unifig/init_spec.rb +233 -0
- data/spec/unifig/providers_spec.rb +36 -0
- data/spec/unifig/var_spec.rb +91 -0
- metadata +69 -0
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
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
|
+
[](https://rubygems.org/gems/unifig)
|
6
|
+
[](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
|
data/lib/unifig/init.rb
ADDED
@@ -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
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|