envied 0.9.0 → 0.9.3

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.
data/lib/envied.rb CHANGED
@@ -2,6 +2,7 @@ require 'envied/version'
2
2
  require 'envied/cli'
3
3
  require 'envied/env_proxy'
4
4
  require 'envied/coercer'
5
+ require 'envied/coercer/envied_string'
5
6
  require 'envied/variable'
6
7
  require 'envied/configuration'
7
8
 
@@ -11,8 +12,7 @@ class ENVied
11
12
  alias_method :required?, :env
12
13
  end
13
14
 
14
- def self.require(*args)
15
- options = args.last.is_a?(Hash) ? args.pop : {}
15
+ def self.require(*args, **options)
16
16
  requested_groups = (args && !args.empty?) ? args : ENV['ENVIED_GROUPS']
17
17
  env!(requested_groups, options)
18
18
  error_on_missing_variables!(options)
@@ -21,26 +21,23 @@ class ENVied
21
21
  ensure_spring_after_fork_require(args, options)
22
22
  end
23
23
 
24
- def self.env!(requested_groups, options = {})
25
- @env = begin
26
- @config = options.fetch(:config) { Configuration.load }
27
- groups = required_groups(*requested_groups)
28
- EnvProxy.new(@config, groups: groups)
29
- 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))
30
27
  end
31
28
 
32
- def self.error_on_missing_variables!(options = {})
29
+ def self.error_on_missing_variables!(**options)
33
30
  names = env.missing_variables.map(&:name)
34
31
  if names.any?
35
- msg = "The following environment variables should be set: #{names * ', '}."
32
+ msg = "The following environment variables should be set: #{names.join(', ')}."
36
33
  msg << "\nPlease make sure to stop Spring before retrying." if spring_enabled? && !options[:via_spring]
37
34
  raise msg
38
35
  end
39
36
  end
40
37
 
41
- def self.error_on_uncoercible_variables!(options = {})
38
+ def self.error_on_uncoercible_variables!(**options)
42
39
  errors = env.uncoercible_variables.map do |v|
43
- "%{name} ('%{value}' can't be coerced to %{type})" % {name: v.name, value: env.value_to_coerce(v), type: v.type }
40
+ format("%{name} with %{value} (%{type})", name: v.name, value: env.value_to_coerce(v).inspect, type: v.type)
44
41
  end
45
42
  if errors.any?
46
43
  msg = "The following environment variables are not coercible: #{errors.join(", ")}."
@@ -55,17 +52,18 @@ class ENVied
55
52
  result.any? ? result.map(&:to_sym) : [:default]
56
53
  end
57
54
 
58
- def self.ensure_spring_after_fork_require(args, options = {})
55
+ def self.ensure_spring_after_fork_require(args, **options)
59
56
  if spring_enabled? && !options[:via_spring]
60
- Spring.after_fork { ENVied.require(args, options.merge(:via_spring => true)) }
57
+ Spring.after_fork { ENVied.require(args, options.merge(via_spring: true)) }
61
58
  end
62
59
  end
63
60
 
64
61
  def self.springify(&block)
65
62
  if defined?(ActiveSupport::Deprecation.warn) && !required?
66
- ActiveSupport::Deprecation.warn(<<-MSG)
67
- It's no longer recommended to `ENVied.require` within ENVied.springify's block. Please re-run `envied init:rails` to upgrade.
68
- MSG
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
69
67
  end
70
68
  if spring_enabled?
71
69
  Spring.after_fork(&block)
data/spec/coercer_spec.rb CHANGED
@@ -1,115 +1,273 @@
1
- require 'spec_helper'
2
-
3
- describe ENVied::Coercer do
1
+ RSpec.describe ENVied::Coercer do
4
2
  it { is_expected.to respond_to :coerce }
3
+ it { is_expected.to respond_to :coerced? }
4
+ it { is_expected.to respond_to :coercible? }
5
+ it { is_expected.to respond_to :supported_types }
6
+ it { is_expected.to respond_to :supported_type? }
7
+
8
+ describe '.supported_types' do
9
+ it 'returns a sorted set of supported types' do
10
+ expect(described_class.supported_types).to eq %i(array boolean date float hash integer string symbol time uri)
11
+ end
12
+ end
13
+
14
+ describe '.supported_type?' do
15
+ it 'returns true for supported type' do
16
+ %i(array boolean date float hash integer string symbol time uri).each do |type|
17
+ expect(described_class.supported_type?(type)).to eq true
18
+ end
19
+ end
20
+
21
+ it 'returns false for unsupported type' do
22
+ expect(described_class.supported_type?(:fixnum)).to eq false
23
+ end
24
+ end
25
+
26
+ describe '#supported_types' do
27
+ it 'calls class method implementation' do
28
+ expect(described_class).to receive(:supported_types).and_call_original
29
+ described_class.new.supported_types
30
+ end
31
+ end
32
+
33
+ describe '#supported_type?' do
34
+ it 'calls class method implementation' do
35
+ expect(described_class).to receive(:supported_type?).with(:string).and_call_original
36
+ described_class.new.supported_type?(:string)
37
+ end
38
+ end
39
+
40
+ describe '#coerced?' do
41
+ let(:coercer) { described_class.new }
42
+
43
+ it 'returns true if value has been coerced (not a string)' do
44
+ expect(coercer.coerced?(1)).to eq true
45
+ end
46
+
47
+ it 'returns false if value is not a string' do
48
+ expect(coercer.coerced?('1')).to eq false
49
+ end
50
+ end
51
+
52
+ describe '#coercible?' do
53
+ let(:coercer) { described_class.new }
54
+
55
+ it 'returns false for unsupported type' do
56
+ expect(coercer.coercible?('value', :invalid_type)).to eq false
57
+ end
58
+
59
+ it 'returns false for a failed coercion' do
60
+ expect(coercer.coercible?('value', :boolean)).to eq false
61
+ end
62
+
63
+ it 'returns true for a coercible value' do
64
+ expect(coercer.coercible?('value', :string)).to eq true
65
+ end
66
+ end
5
67
 
6
68
  describe '#coerce' do
7
69
  let(:coercer){ described_class.new }
8
70
 
9
- def coerce_to(type)
10
- ->(str){ coercer.coerce(str, type) }
71
+ def coerce(str, type)
72
+ coercer.coerce(str, type)
73
+ end
74
+
75
+ it 'fails with an invalid type' do
76
+ expect { coerce('', :fixnum) }.to raise_error(ArgumentError, "The type `:fixnum` is not supported.")
11
77
  end
12
78
 
13
- describe 'string coercion' do
14
- let(:coerce){ coerce_to(:String) }
79
+ describe 'to string' do
80
+ it 'returns the input untouched' do
81
+ expect(coerce('1', :string)).to eq '1'
82
+ expect(coerce(' 1', :string)).to eq ' 1'
83
+ end
15
84
 
16
- it 'yields the input untouched' do
17
- expect(coerce['1']).to eq '1'
18
- expect(coerce[' 1']).to eq ' 1'
85
+ it 'fails when the value does not respond to #to_str' do
86
+ value = Object.new
87
+ expect { coerce(value, :string) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
19
88
  end
20
89
  end
21
90
 
22
- describe 'integer coercion' do
23
- let(:coerce){ coerce_to(:Integer) }
91
+ describe 'to integer' do
92
+ {
93
+ '1' => 1,
94
+ '+1' => 1,
95
+ '-1' => -1,
96
+ '10' => 10,
97
+ '100_00' => 100_00,
98
+ '1_000_00' => 1_000_00
99
+ }.each do |value, integer|
100
+ it "converts #{value.inspect} to an integer" do
101
+ expect(coerce(value, :integer)).to be_kind_of(Integer)
102
+ expect(coerce(value, :integer)).to eq integer
103
+ end
104
+ end
105
+
106
+ it 'fails with an invalid string' do
107
+ expect { coerce('non-integer', :integer) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
108
+ end
24
109
 
25
- it 'converts strings to integers' do
26
- expect(coerce['1']).to eq 1
27
- expect(coerce['-1']).to eq(-1)
110
+ it 'fails with a float' do
111
+ expect { coerce('1.23', :integer) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
28
112
  end
29
113
  end
30
114
 
31
- describe 'float coercion' do
32
- let(:coerce){ coerce_to(:Float) }
115
+ describe 'to float' do
116
+ {
117
+ '1' => 1.0,
118
+ '+1' => 1.0,
119
+ '-1' => -1.0,
120
+ '1_000.0' => 1_000.0,
121
+ '10_000.23' => 10_000.23,
122
+ '1_000_000.0' => 1_000_000.0,
123
+ '1.0' => 1.0,
124
+ '1.234' => 1.234,
125
+ '1.0e+1' => 10.0,
126
+ '1.0e-1' => 0.1,
127
+ '1.0E+1' => 10.0,
128
+ '1.0E-1' => 0.1,
129
+ '+1.0' => 1.0,
130
+ '+1.0e+1' => 10.0,
131
+ '+1.0e-1' => 0.1,
132
+ '+1.0E+1' => 10.0,
133
+ '+1.0E-1' => 0.1,
134
+ '-1.0' => -1.0,
135
+ '-1.234' => -1.234,
136
+ '-1.0e+1' => -10.0,
137
+ '-1.0e-1' => -0.1,
138
+ '-1.0E+1' => -10.0,
139
+ '-1.0E-1' => -0.1,
140
+ '.1' => 0.1,
141
+ '.1e+1' => 1.0,
142
+ '.1e-1' => 0.01,
143
+ '.1E+1' => 1.0,
144
+ '.1E-1' => 0.01,
145
+ '1e1' => 10.0,
146
+ '1E+1' => 10.0,
147
+ '+1e-1' => 0.1,
148
+ '-1E1' => -10.0,
149
+ '-1e-1' => -0.1,
150
+ }.each do |value, float|
151
+ it "converts #{value.inspect} to a float" do
152
+ expect(coerce(value, :float)).to be_kind_of(Float)
153
+ expect(coerce(value, :float)).to eq float
154
+ end
155
+ end
33
156
 
34
- it 'converts strings to floats' do
35
- expect(coerce['1.05']).to eq 1.05
36
- expect(coerce['-1.234']).to eq(-1.234)
157
+ it 'fails with an invalid string' do
158
+ expect { coerce('non-float', :float) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
159
+ end
160
+
161
+ it 'fails when string starts with e' do
162
+ expect { coerce('e1', :float) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
37
163
  end
38
164
  end
39
165
 
40
- describe 'boolean coercion' do
41
- let(:coerce){ coerce_to(:Boolean) }
166
+ describe 'to boolean' do
167
+ %w[ 1 on ON t true T TRUE y yes Y YES ].each do |value|
168
+ it "converts #{value.inspect} to `true`" do
169
+ expect(coerce(value, :boolean)).to eq true
170
+ end
171
+ end
42
172
 
43
- it "converts 'true' and 'false'" do
44
- expect(coerce['true']).to eq true
45
- expect(coerce['false']).to eq false
173
+ %w[ 0 off OFF f false F FALSE n no N NO ].each do |value|
174
+ it "converts #{value.inspect} to `false`" do
175
+ expect(coerce(value, :boolean)).to eq false
176
+ end
46
177
  end
47
178
 
48
- it "converts '1' and '0'" do
49
- expect(coerce['1']).to eq true
50
- expect(coerce['0']).to eq false
179
+ it 'fails with an invalid boolean string' do
180
+ expect { coerce('non-boolean', :boolean) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
51
181
  end
52
182
  end
53
183
 
54
- describe 'symbol coercion' do
55
- let(:coerce){ coerce_to(:Symbol) }
56
-
184
+ describe 'to symbol' do
57
185
  it 'converts strings to symbols' do
58
- expect(coerce['a']).to eq :a
59
- expect(coerce['nice_symbol']).to eq :nice_symbol
186
+ expect(coerce('a', :symbol)).to eq :a
187
+ expect(coerce('nice_symbol', :symbol)).to eq :nice_symbol
60
188
  end
61
189
  end
62
190
 
63
- describe 'date coercion' do
64
- let(:coerce){ coerce_to(:Date) }
191
+ describe 'to date' do
192
+ it 'converts string to date' do
193
+ date = coerce('2019-03-22', :date)
65
194
 
66
- it 'converts strings to date' do
67
- expect(coerce['2014-12-25']).to eq Date.parse('2014-12-25')
195
+ expect(date).to be_instance_of(Date)
196
+ expect(date.year).to eq 2019
197
+ expect(date.month).to eq 3
198
+ expect(date.day).to eq 22
68
199
  end
69
- end
70
200
 
71
- describe 'time coercion' do
72
- let(:coerce){ coerce_to(:Time) }
201
+ it 'converts other string formats to date' do
202
+ expect(coerce('March 22nd, 2019', :date)).to eq Date.parse('2019-03-22')
203
+ expect(coerce('Sat, March 23rd, 2019', :date)).to eq Date.parse('2019-03-23')
204
+ end
73
205
 
74
- it 'converts strings to time' do
75
- expect(coerce['4:00']).to eq Time.parse('4:00')
206
+ it 'fails with an invalid string' do
207
+ expect { coerce('non-date', :date) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
76
208
  end
77
209
  end
78
210
 
79
- describe 'array coercion' do
80
- let(:coerce){ coerce_to(:Array) }
211
+ describe 'to time' do
212
+ it 'converts string to time without time part' do
213
+ time = coerce("2019-03-22", :time)
214
+
215
+ expect(time).to be_instance_of(Time)
216
+ expect(time.year).to eq 2019
217
+ expect(time.month).to eq 3
218
+ expect(time.day).to eq 22
219
+ expect(time.hour).to eq 0
220
+ expect(time.min).to eq 0
221
+ expect(time.sec).to eq 0
222
+ end
223
+
224
+ it 'converts string to time with time portion' do
225
+ time = coerce("March 22nd, 2019 9:30:55", :time)
81
226
 
227
+ expect(time).to be_instance_of(Time)
228
+ expect(time.year).to eq 2019
229
+ expect(time.month).to eq 3
230
+ expect(time.day).to eq 22
231
+ expect(time.hour).to eq 9
232
+ expect(time.min).to eq 30
233
+ expect(time.sec).to eq 55
234
+ end
235
+
236
+ it 'fails with an invalid string' do
237
+ expect { coerce('2999', :time) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
238
+ end
239
+ end
240
+
241
+ describe 'to array' do
82
242
  it 'converts strings to array' do
83
243
  {
84
244
  'a,b' => ['a','b'],
85
245
  ' a, b' => [' a',' b'],
86
246
  'apples,and\, of course\, pears' => ['apples','and, of course, pears'],
87
- }.each do |i, o|
88
- expect(coerce[i]).to eq o
247
+ }.each do |value, array|
248
+ expect(coerce(value, :array)).to eq array
89
249
  end
90
250
  end
91
251
  end
92
252
 
93
- describe 'hash coercion' do
94
- let(:coerce){ coerce_to(:Hash) }
95
-
253
+ describe 'to hash' do
96
254
  it 'converts strings to hashes' do
97
255
  {
98
256
  'a=1' => {'a' => '1'},
99
257
  'a=1&b=2' => {'a' => '1', 'b' => '2'},
100
258
  'a=&b=2' => {'a' => '', 'b' => '2'},
101
259
  'a&b=2' => {'a' => nil, 'b' => '2'},
102
- }.each do |i, o|
103
- expect(coerce[i]).to eq o
260
+ }.each do |value, hash|
261
+ expect(coerce(value, :hash)).to eq hash
104
262
  end
105
263
  end
106
264
  end
107
265
 
108
- describe 'uri coercion' do
109
- let(:coerce){ coerce_to(:Uri) }
110
-
266
+ describe 'to uri' do
111
267
  it 'converts strings to uris' do
112
- expect(coerce['http://www.google.com']).to be_a(URI)
268
+ expect(coerce('https://www.google.com', :uri)).to be_a(URI)
269
+ expect(coerce('https://www.google.com', :uri).scheme).to eq 'https'
270
+ expect(coerce('https://www.google.com', :uri).host).to eq 'www.google.com'
113
271
  end
114
272
  end
115
273
  end
@@ -1,49 +1,112 @@
1
- require 'spec_helper'
2
-
3
- describe ENVied::Configuration do
1
+ RSpec.describe ENVied::Configuration do
4
2
  it { is_expected.to respond_to :variable }
3
+ it { is_expected.to respond_to :group }
5
4
  it { is_expected.to respond_to :enable_defaults! }
6
5
  it { is_expected.to respond_to :defaults_enabled? }
7
6
 
8
- describe '#variable' do
9
- def with_envfile(&block)
10
- @config = described_class.new(&block)
11
- end
12
- attr_reader :config
7
+ def with_envfile(**options, &block)
8
+ @config = ENVied::Configuration.new(options, &block)
9
+ end
10
+ attr_reader :config
13
11
 
12
+ describe 'variables' do
14
13
  it 'results in an added variable' do
15
14
  with_envfile do
16
15
  variable :foo, :boolean
17
16
  end
18
17
 
19
- expect(config.variables).to include ENVied::Variable.new(:foo, :boolean)
18
+ expect(config.variables).to include ENVied::Variable.new(:foo, :boolean, group: :default)
19
+ end
20
+
21
+ it 'sets string as default type when no type is given' do
22
+ with_envfile do
23
+ variable :bar
24
+ end
25
+
26
+ expect(config.variables).to include ENVied::Variable.new(:bar, :string, default: nil, group: :default)
20
27
  end
21
28
 
22
- it 'sets string as type when no type is given' do
29
+ it 'sets a default value when specified' do
23
30
  with_envfile do
24
31
  variable :bar, default: 'bar'
25
32
  end
26
33
 
27
- expect(config.variables).to include ENVied::Variable.new(:bar, :string, default: 'bar')
34
+ expect(config.variables).to include ENVied::Variable.new(:bar, :string, default: 'bar', group: :default)
35
+ end
36
+
37
+ it 'sets specific group for variable' do
38
+ with_envfile do
39
+ group :production do
40
+ variable :SECRET_KEY_BASE
41
+ end
42
+ end
43
+
44
+ expect(config.variables).to include ENVied::Variable.new(:SECRET_KEY_BASE, :string, group: :production)
45
+ end
46
+
47
+ it 'sets the same variable for multiple groups' do
48
+ with_envfile do
49
+ group :development, :test do
50
+ variable :DISABLE_PRY, :boolean, default: 'false'
51
+ end
52
+ end
53
+
54
+ expect(config.variables).to eq [
55
+ ENVied::Variable.new(:DISABLE_PRY, :boolean, default: 'false', group: :development),
56
+ ENVied::Variable.new(:DISABLE_PRY, :boolean, default: 'false', group: :test)
57
+ ]
28
58
  end
29
59
  end
30
60
 
31
61
  describe 'defaults' do
32
62
  it 'is disabled by default' do
33
- expect(subject.defaults_enabled?).to_not be
63
+ expect(subject.defaults_enabled?).to eq false
64
+ end
65
+
66
+ it 'can be enabled with an ENV variable' do
67
+ allow(ENV).to receive(:[]).with("ENVIED_ENABLE_DEFAULTS").and_return("true")
68
+ expect(subject.defaults_enabled?).to eq true
69
+ end
70
+
71
+ it 'can be enabled through a config option' do
72
+ with_envfile(enable_defaults: true) { }
73
+
74
+ expect(config.defaults_enabled?).to eq true
34
75
  end
35
76
 
36
77
  describe '#enable_defaults!' do
37
- it 'can be passed a value' do
78
+ it 'can be enabled in a block by calling `enable_defaults!`' do
79
+ with_envfile do
80
+ enable_defaults!
81
+ end
82
+
83
+ expect(config.defaults_enabled?).to eq true
84
+ end
85
+
86
+ it 'can be enabled by calling `enable_defaults!` with a Proc' do
87
+ with_envfile do
88
+ enable_defaults! { true }
89
+ end
90
+
91
+ expect(config.defaults_enabled?).to eq true
92
+ end
93
+
94
+ it 'defaults to true with no arguments' do
95
+ expect {
96
+ subject.enable_defaults!
97
+ }.to change { subject.defaults_enabled? }.from(false).to(true)
98
+ end
99
+
100
+ it 'can be passed a boolean value' do
38
101
  expect {
39
102
  subject.enable_defaults!(true)
40
- }.to change { subject.defaults_enabled? }
103
+ }.to change { subject.defaults_enabled? }.from(false).to(true)
41
104
  end
42
105
 
43
106
  it 'can be passed a block' do
44
107
  expect {
45
108
  subject.enable_defaults! { true }
46
- }.to change { subject.defaults_enabled? }.to(true)
109
+ }.to change { subject.defaults_enabled? }.from(false).to(true)
47
110
  end
48
111
  end
49
112
  end
@@ -1,6 +1,4 @@
1
- require 'spec_helper'
2
-
3
- describe ENVied::EnvVarExtractor do
1
+ RSpec.describe ENVied::EnvVarExtractor do
4
2
 
5
3
  describe "#capture_variables" do
6
4
  def capture_variables(text)