envied 0.9.1 → 0.9.2.rc1
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 +5 -5
- data/.gitignore +13 -17
- data/.rspec +1 -2
- data/.travis.yml +5 -6
- data/CHANGELOG.md +5 -0
- data/Gemfile +0 -3
- data/README.md +5 -6
- data/Rakefile +2 -6
- data/bin/console +14 -0
- data/bin/envied +4 -0
- data/bin/setup +8 -0
- data/envied.gemspec +5 -4
- data/lib/envied.rb +14 -17
- data/lib/envied/cli.rb +4 -11
- data/lib/envied/coercer.rb +7 -11
- data/lib/envied/coercer/envied_string.rb +51 -2
- data/lib/envied/configuration.rb +14 -17
- data/lib/envied/env_proxy.rb +22 -21
- data/lib/envied/env_var_extractor.rb +2 -2
- data/lib/envied/variable.rb +1 -1
- data/lib/envied/version.rb +1 -1
- data/spec/coercer_spec.rb +201 -49
- data/spec/configuration_spec.rb +78 -15
- data/spec/env_var_extractor_spec.rb +1 -3
- data/spec/envied_spec.rb +129 -170
- data/spec/gemspec_spec.rb +17 -0
- data/spec/spec_helper.rb +73 -5
- data/spec/variable_spec.rb +17 -7
- metadata +18 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 13dbc94c996f024f24f78d106973b051c90c83231a79a5f78e190f17c31c431b
|
4
|
+
data.tar.gz: '0836c6bb61eb65228beaa6ee0100d9229f7557b52d663a68914b641528d37d6f'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85fc118a868a8b78e22c9e1160fe842e2a4a173f86c39f740cdaa8f26cc3ba2d383f4ab2c6606ee422a60e914b30a8b6f5db109532051831430be52b8e1f030c
|
7
|
+
data.tar.gz: 96f37d724c1d660e1530360782c01d704562ded183c4f19fc3e9821c2a8ac6bab319434210d15c9e7f40d8e80c686bbdd61f73df20c66af549b109ec3a31f9f3
|
data/.gitignore
CHANGED
@@ -1,18 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/_yardoc/
|
4
|
+
/coverage/
|
5
|
+
/doc/
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/tmp/
|
10
|
+
|
11
|
+
# rspec failure tracking
|
12
|
+
.rspec_status
|
13
|
+
|
6
14
|
Gemfile.lock
|
7
|
-
InstalledFiles
|
8
|
-
_yardoc
|
9
|
-
coverage
|
10
|
-
doc/
|
11
|
-
lib/bundler/man
|
12
|
-
pkg
|
13
|
-
rdoc
|
14
|
-
spec/reports
|
15
|
-
test/tmp
|
16
|
-
test/version_tmp
|
17
|
-
tmp
|
18
|
-
bin
|
data/.rspec
CHANGED
data/.travis.yml
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
language: ruby
|
2
2
|
|
3
3
|
before_install:
|
4
|
+
- "echo 'gem: --no-document' > ~/.gemrc"
|
4
5
|
- gem update --system
|
5
6
|
- gem install bundler
|
6
7
|
|
7
8
|
rvm:
|
8
|
-
- 2.
|
9
|
-
- 2.
|
10
|
-
- 2.
|
11
|
-
- 2.
|
12
|
-
- jruby-9.1.12.0
|
13
|
-
- jruby-head
|
9
|
+
- 2.4.5
|
10
|
+
- 2.5.5
|
11
|
+
- 2.6.2
|
12
|
+
- jruby-9.2.6.0
|
14
13
|
|
15
14
|
cache: bundler
|
16
15
|
sudo: false
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## master / unreleased
|
2
|
+
|
3
|
+
* Now requiring Ruby 2.4+ [#48], [#51]
|
4
|
+
* Removed `coercible` dependency as now all coercion functionality is implemented locally. This is a backwards compatible change. [#49]
|
5
|
+
|
1
6
|
## 0.9.1 / 2017-07-06
|
2
7
|
|
3
8
|
* Updates `envied check:heroku` to support multiline ENV variables [#42](../../pull/42)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# ENVied [](https://travis-ci.org/eval/envied)
|
1
|
+
# ENVied [](https://travis-ci.org/eval/envied) [](https://envied-rb.zulipchat.com/)
|
2
2
|
|
3
3
|
### TL;DR ensure presence and type of your app's ENV-variables.
|
4
4
|
|
@@ -19,7 +19,6 @@ For the rationale behind this project, see this [blogpost](http://www.gertgoet.c
|
|
19
19
|
* [Groups](#groups)
|
20
20
|
* [Defaults](#defaults)
|
21
21
|
* [More examples](#more-examples)
|
22
|
-
* [Rails](#rails--spring)
|
23
22
|
* [Command-line interface](#command-line-interface)
|
24
23
|
* [How do I...?](#how-do-i)
|
25
24
|
* [Testing](#testing)
|
@@ -91,11 +90,11 @@ The following types are supported:
|
|
91
90
|
* `:time` (e.g. '14:00')
|
92
91
|
* `:hash` (e.g. 'a=1&b=2' becomes `{'a' => '1', 'b' => '2'}`)
|
93
92
|
* `:array` (e.g. 'tag1,tag2' becomes `['tag1', 'tag2']`)
|
94
|
-
* `:uri` (e.g. 'http://www.google.com' becomes `URI.parse('http://www.google.com')`
|
93
|
+
* `:uri` (e.g. 'http://www.google.com' becomes result of `URI.parse('http://www.google.com')`)
|
95
94
|
|
96
95
|
### Groups
|
97
96
|
|
98
|
-
Groups give you more flexibility to define when variables are needed.
|
97
|
+
Groups give you more flexibility to define when variables are needed.
|
99
98
|
It's similar to groups in a Gemfile:
|
100
99
|
|
101
100
|
```ruby
|
@@ -147,7 +146,7 @@ variable :FORCE_SSL, :boolean, default: 'false'
|
|
147
146
|
variable :PORT, :integer, default: proc {|envied| envied.FORCE_SSL ? 443 : 80 }
|
148
147
|
```
|
149
148
|
|
150
|
-
Please remember that ENVied only **reads** from ENV; it doesn't mutate ENV.
|
149
|
+
Please remember that ENVied only **reads** from ENV; it doesn't mutate ENV.
|
151
150
|
Don't let setting a default for, say `RAILS_ENV`, give you the impression that `ENV['RAILS_ENV']` is set.
|
152
151
|
As a rule of thumb you should only use defaults:
|
153
152
|
* for local development
|
@@ -220,7 +219,7 @@ bundle exec rspec
|
|
220
219
|
## Developing
|
221
220
|
|
222
221
|
```bash
|
223
|
-
|
222
|
+
bin/console
|
224
223
|
```
|
225
224
|
|
226
225
|
## Contributing
|
data/Rakefile
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
-
require
|
2
|
+
require "rspec/core/rake_task"
|
3
3
|
|
4
|
-
RSpec::Core::RakeTask.new(:spec)
|
5
|
-
s.ruby_opts = %w(-w)
|
6
|
-
s.rspec_opts = '--format progress'
|
7
|
-
end
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
5
|
|
9
|
-
desc "Run the specs"
|
10
6
|
task default: :spec
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "envied"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/envied
ADDED
data/bin/setup
ADDED
data/envied.gemspec
CHANGED
@@ -18,10 +18,11 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.required_ruby_version = ">= 2.
|
22
|
-
|
21
|
+
spec.required_ruby_version = ">= 2.4"
|
22
|
+
|
23
23
|
spec.add_dependency "thor", "~> 0.15"
|
24
|
-
|
25
|
-
spec.add_development_dependency "
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
26
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
26
27
|
spec.add_development_dependency "rspec", "~> 3.0"
|
27
28
|
end
|
data/lib/envied.rb
CHANGED
@@ -12,8 +12,7 @@ class ENVied
|
|
12
12
|
alias_method :required?, :env
|
13
13
|
end
|
14
14
|
|
15
|
-
def self.require(*args)
|
16
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
15
|
+
def self.require(*args, **options)
|
17
16
|
requested_groups = (args && !args.empty?) ? args : ENV['ENVIED_GROUPS']
|
18
17
|
env!(requested_groups, options)
|
19
18
|
error_on_missing_variables!(options)
|
@@ -22,26 +21,23 @@ class ENVied
|
|
22
21
|
ensure_spring_after_fork_require(args, options)
|
23
22
|
end
|
24
23
|
|
25
|
-
def self.env!(requested_groups, options
|
26
|
-
@
|
27
|
-
|
28
|
-
groups = required_groups(*requested_groups)
|
29
|
-
EnvProxy.new(@config, groups: groups)
|
30
|
-
end
|
24
|
+
def self.env!(requested_groups, **options)
|
25
|
+
@config = options.fetch(:config) { Configuration.load }
|
26
|
+
@env = EnvProxy.new(@config, groups: required_groups(*requested_groups))
|
31
27
|
end
|
32
28
|
|
33
|
-
def self.error_on_missing_variables!(options
|
29
|
+
def self.error_on_missing_variables!(**options)
|
34
30
|
names = env.missing_variables.map(&:name)
|
35
31
|
if names.any?
|
36
|
-
msg = "The following environment variables should be set: #{names
|
32
|
+
msg = "The following environment variables should be set: #{names.join(', ')}."
|
37
33
|
msg << "\nPlease make sure to stop Spring before retrying." if spring_enabled? && !options[:via_spring]
|
38
34
|
raise msg
|
39
35
|
end
|
40
36
|
end
|
41
37
|
|
42
|
-
def self.error_on_uncoercible_variables!(options
|
38
|
+
def self.error_on_uncoercible_variables!(**options)
|
43
39
|
errors = env.uncoercible_variables.map do |v|
|
44
|
-
"%{name}
|
40
|
+
format("%{name} with %{value} (%{type})", name: v.name, value: env.value_to_coerce(v).inspect, type: v.type)
|
45
41
|
end
|
46
42
|
if errors.any?
|
47
43
|
msg = "The following environment variables are not coercible: #{errors.join(", ")}."
|
@@ -56,17 +52,18 @@ class ENVied
|
|
56
52
|
result.any? ? result.map(&:to_sym) : [:default]
|
57
53
|
end
|
58
54
|
|
59
|
-
def self.ensure_spring_after_fork_require(args, options
|
55
|
+
def self.ensure_spring_after_fork_require(args, **options)
|
60
56
|
if spring_enabled? && !options[:via_spring]
|
61
|
-
Spring.after_fork { ENVied.require(args, options.merge(:
|
57
|
+
Spring.after_fork { ENVied.require(args, options.merge(via_spring: true)) }
|
62
58
|
end
|
63
59
|
end
|
64
60
|
|
65
61
|
def self.springify(&block)
|
66
62
|
if defined?(ActiveSupport::Deprecation.warn) && !required?
|
67
|
-
ActiveSupport::Deprecation.warn(
|
68
|
-
It's no longer recommended to `ENVied.require` within ENVied.springify's
|
69
|
-
|
63
|
+
ActiveSupport::Deprecation.warn(<<~MSG)
|
64
|
+
It's no longer recommended to `ENVied.require` within ENVied.springify's
|
65
|
+
block. Please re-run `envied init:rails` to upgrade.
|
66
|
+
MSG
|
70
67
|
end
|
71
68
|
if spring_enabled?
|
72
69
|
Spring.after_fork(&block)
|
data/lib/envied/cli.rb
CHANGED
@@ -43,10 +43,8 @@ class ENVied
|
|
43
43
|
puts "Writing Envfile to #{File.expand_path('Envfile')}"
|
44
44
|
template("Envfile.tt")
|
45
45
|
|
46
|
-
puts
|
47
|
-
|
48
|
-
ENVied.require(*ENV['ENVIED_GROUPS'] || [:default, ENV['RACK_ENV']])
|
49
|
-
INIT
|
46
|
+
puts "Add the following snippet (or similar) to your app's initialization:"
|
47
|
+
puts "ENVied.require(*ENV['ENVIED_GROUPS'] || [:default, ENV['RACK_ENV']])"
|
50
48
|
end
|
51
49
|
|
52
50
|
desc "init:rails", "Generate all files needed for a Rails project"
|
@@ -79,7 +77,6 @@ INIT
|
|
79
77
|
end
|
80
78
|
|
81
79
|
desc "check:heroku", "Checks whether a Heroku config contains required variables"
|
82
|
-
|
83
80
|
long_desc <<-LONG
|
84
81
|
Checks the config of your Heroku app against the local Envfile.
|
85
82
|
|
@@ -96,14 +93,11 @@ INIT
|
|
96
93
|
option :quiet, type: :boolean, desc: 'Communicate success of the check only via the exit status.'
|
97
94
|
define_method "check:heroku" do
|
98
95
|
if STDIN.tty?
|
99
|
-
error
|
100
|
-
Please pipe the contents of `heroku config --json` to this task.
|
101
|
-
I.e. `heroku config --json | bundle exec envied check:heroku`"
|
102
|
-
ERR
|
96
|
+
error "Please pipe to this task i.e. `heroku config --json | bundle exec envied check:heroku`"
|
103
97
|
exit 1
|
104
98
|
end
|
105
99
|
heroku_env = JSON.parse(STDIN.read)
|
106
|
-
ENV.replace(
|
100
|
+
ENV.replace(heroku_env)
|
107
101
|
|
108
102
|
requested_groups = ENV['ENVIED_GROUPS'] || options[:groups]
|
109
103
|
ENVied.require(*requested_groups)
|
@@ -117,7 +111,6 @@ ERR
|
|
117
111
|
Generates a shell script to check the Heroku config against the local Envfile.
|
118
112
|
|
119
113
|
The same as the check:heroku-task, but all in one script (no need to pipe `heroku config --json` to it etc.).
|
120
|
-
|
121
114
|
LONG
|
122
115
|
option :dest, banner: "where to put the script", desc: "Default: bin/<app>-env-check or bin/heroku-env-check"
|
123
116
|
option :app, banner: "name of Heroku app", desc: "uses ENV['HEROKU_APP'] as default if present", default: ENV['HEROKU_APP']
|
data/lib/envied/coercer.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
require 'coercible'
|
2
|
-
|
3
1
|
# Responsible for all string to type coercions.
|
4
2
|
class ENVied::Coercer
|
3
|
+
|
4
|
+
UnsupportedCoercion = Class.new(StandardError)
|
5
|
+
|
5
6
|
# Coerce strings to specific type.
|
6
7
|
#
|
7
8
|
# @param string [String] the string to be coerced
|
@@ -14,14 +15,9 @@ class ENVied::Coercer
|
|
14
15
|
# @return [type] the coerced string.
|
15
16
|
def coerce(string, type)
|
16
17
|
unless supported_type?(type)
|
17
|
-
raise ArgumentError, "
|
18
|
+
raise ArgumentError, "The type `#{type.inspect}` is not supported."
|
18
19
|
end
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def coerce_method_for(type)
|
23
|
-
return nil unless supported_type?(type)
|
24
|
-
coercer.method("to_#{type.downcase}")
|
20
|
+
coercer.public_send("to_#{type.downcase}", string)
|
25
21
|
end
|
26
22
|
|
27
23
|
def self.supported_types
|
@@ -52,7 +48,7 @@ class ENVied::Coercer
|
|
52
48
|
end
|
53
49
|
|
54
50
|
def coercer
|
55
|
-
@coercer ||=
|
51
|
+
@coercer ||= ENViedString.new
|
56
52
|
end
|
57
53
|
|
58
54
|
def coerced?(value)
|
@@ -63,7 +59,7 @@ class ENVied::Coercer
|
|
63
59
|
return false unless supported_type?(type)
|
64
60
|
coerce(string, type)
|
65
61
|
true
|
66
|
-
rescue
|
62
|
+
rescue UnsupportedCoercion
|
67
63
|
false
|
68
64
|
end
|
69
65
|
end
|
@@ -1,15 +1,55 @@
|
|
1
|
-
|
1
|
+
class ENVied::Coercer::ENViedString
|
2
|
+
TRUE_VALUES = %w[1 on t true y yes].freeze
|
3
|
+
FALSE_VALUES = %w[0 off f false n no].freeze
|
4
|
+
BOOLEAN_MAP = (TRUE_VALUES.product([ true ]) + FALSE_VALUES.product([ false ])).to_h.freeze
|
2
5
|
|
3
|
-
class ENVied::Coercer::ENViedString < Coercible::Coercer::String
|
4
6
|
def to_array(str)
|
5
7
|
str.split(/(?<!\\),/).map{|i| i.gsub(/\\,/,',') }
|
6
8
|
end
|
7
9
|
|
10
|
+
def to_boolean(str)
|
11
|
+
BOOLEAN_MAP.fetch(str&.downcase) do
|
12
|
+
raise_unsupported_coercion(str, __method__)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_date(str)
|
17
|
+
require 'date'
|
18
|
+
::Date.parse(str)
|
19
|
+
rescue ArgumentError
|
20
|
+
raise_unsupported_coercion(str, __method__)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_float(str)
|
24
|
+
Float(str)
|
25
|
+
rescue ArgumentError
|
26
|
+
raise_unsupported_coercion(str, __method__)
|
27
|
+
end
|
28
|
+
|
8
29
|
def to_hash(str)
|
9
30
|
require 'cgi'
|
10
31
|
::CGI.parse(str).map { |key, values| [key, values[0]] }.to_h
|
11
32
|
end
|
12
33
|
|
34
|
+
def to_string(str)
|
35
|
+
if str.respond_to?(:to_str)
|
36
|
+
str.public_send(:to_str)
|
37
|
+
else
|
38
|
+
raise_unsupported_coercion(str, __method__)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_symbol(str)
|
43
|
+
str.to_sym
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_time(str)
|
47
|
+
require 'time'
|
48
|
+
::Time.parse(str)
|
49
|
+
rescue ArgumentError
|
50
|
+
raise_unsupported_coercion(str, __method__)
|
51
|
+
end
|
52
|
+
|
13
53
|
def to_uri(str)
|
14
54
|
require 'uri'
|
15
55
|
::URI.parse(str)
|
@@ -20,4 +60,13 @@ class ENVied::Coercer::ENViedString < Coercible::Coercer::String
|
|
20
60
|
rescue ArgumentError
|
21
61
|
raise_unsupported_coercion(str, __method__)
|
22
62
|
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def raise_unsupported_coercion(value, method)
|
67
|
+
raise(
|
68
|
+
ENVied::Coercer::UnsupportedCoercion,
|
69
|
+
"#{self.class}##{method} doesn't know how to coerce #{value.inspect}"
|
70
|
+
)
|
71
|
+
end
|
23
72
|
end
|
data/lib/envied/configuration.rb
CHANGED
@@ -2,21 +2,13 @@ class ENVied
|
|
2
2
|
class Configuration
|
3
3
|
attr_reader :current_group, :defaults_enabled, :coercer
|
4
4
|
|
5
|
-
def initialize(options
|
5
|
+
def initialize(**options, &block)
|
6
6
|
@coercer = options.fetch(:coercer, Coercer.new)
|
7
7
|
@defaults_enabled = options.fetch(:enable_defaults, defaults_enabled_default)
|
8
8
|
instance_eval(&block) if block_given?
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
if ENV['ENVIED_ENABLE_DEFAULTS'].nil?
|
13
|
-
false
|
14
|
-
else
|
15
|
-
@coercer.coerce(ENV['ENVIED_ENABLE_DEFAULTS'], :boolean)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.load(options = {})
|
11
|
+
def self.load(**options)
|
20
12
|
envfile = File.expand_path('Envfile')
|
21
13
|
new(options).tap do |v|
|
22
14
|
v.instance_eval(File.read(envfile), envfile)
|
@@ -33,13 +25,9 @@ class ENVied
|
|
33
25
|
@defaults_enabled
|
34
26
|
end
|
35
27
|
|
36
|
-
def variable(name,
|
37
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
38
|
-
type = args.first || :string
|
39
|
-
|
28
|
+
def variable(name, type = :string, **options)
|
40
29
|
unless coercer.supported_type?(type)
|
41
|
-
raise ArgumentError,
|
42
|
-
"Variable type (of #{name}) should be one of #{coercer.supported_types}"
|
30
|
+
raise ArgumentError, "#{type.inspect} is not a supported type. Should be one of #{coercer.supported_types}"
|
43
31
|
end
|
44
32
|
options[:group] = current_group if current_group
|
45
33
|
variables << ENVied::Variable.new(name, type, options)
|
@@ -57,6 +45,15 @@ class ENVied
|
|
57
45
|
def variables
|
58
46
|
@variables ||= []
|
59
47
|
end
|
60
|
-
end
|
61
48
|
|
49
|
+
private
|
50
|
+
|
51
|
+
def defaults_enabled_default
|
52
|
+
if ENV['ENVIED_ENABLE_DEFAULTS'].nil?
|
53
|
+
false
|
54
|
+
else
|
55
|
+
@coercer.coerce(ENV['ENVIED_ENABLE_DEFAULTS'], :boolean)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
62
59
|
end
|