envied 0.9.1 → 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
@@ -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
- @env = begin
27
- @config = options.fetch(:config) { Configuration.load }
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} ('%{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)
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(:via_spring => true)) }
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(<<-MSG)
68
- It's no longer recommended to `ENVied.require` within ENVied.springify's block. Please re-run `envied init:rails` to upgrade.
69
- 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
70
67
  end
71
68
  if spring_enabled?
72
69
  Spring.after_fork(&block)
data/spec/coercer_spec.rb CHANGED
@@ -1,121 +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
79
  describe 'to string' do
14
- let(:coerce){ coerce_to(:string) }
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
91
  describe 'to integer' do
23
- let(:coerce){ coerce_to(:integer) }
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
24
105
 
25
- it 'converts strings to integers' do
26
- expect(coerce['1']).to eq 1
27
- expect(coerce['-1']).to eq(-1)
106
+ it 'fails with an invalid string' do
107
+ expect { coerce('non-integer', :integer) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
28
108
  end
29
109
 
30
- it 'fails for float' do
31
- expect {
32
- coerce['1.23']
33
- }.to raise_error(Coercible::UnsupportedCoercion)
110
+ it 'fails with a float' do
111
+ expect { coerce('1.23', :integer) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
34
112
  end
35
113
  end
36
114
 
37
115
  describe 'to float' do
38
- let(:coerce){ coerce_to(:float) }
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
156
+
157
+ it 'fails with an invalid string' do
158
+ expect { coerce('non-float', :float) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
159
+ end
39
160
 
40
- it 'converts strings to floats' do
41
- expect(coerce['1.05']).to eq 1.05
42
- expect(coerce['-1.234']).to eq(-1.234)
161
+ it 'fails when string starts with e' do
162
+ expect { coerce('e1', :float) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
43
163
  end
44
164
  end
45
165
 
46
166
  describe 'to boolean' do
47
- let(:coerce){ coerce_to(:boolean) }
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
48
172
 
49
- it "converts 'true' and 'false'" do
50
- expect(coerce['true']).to eq true
51
- 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
52
177
  end
53
178
 
54
- it "converts '1' and '0'" do
55
- expect(coerce['1']).to eq true
56
- 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)
57
181
  end
58
182
  end
59
183
 
60
184
  describe 'to symbol' do
61
- let(:coerce){ coerce_to(:symbol) }
62
-
63
185
  it 'converts strings to symbols' do
64
- expect(coerce['a']).to eq :a
65
- 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
66
188
  end
67
189
  end
68
190
 
69
191
  describe 'to date' do
70
- let(:coerce){ coerce_to(:date) }
192
+ it 'converts string to date' do
193
+ date = coerce('2019-03-22', :date)
194
+
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
199
+ end
71
200
 
72
- it 'converts strings to date' do
73
- expect(coerce['2014-12-25']).to eq Date.parse('2014-12-25')
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
205
+
206
+ it 'fails with an invalid string' do
207
+ expect { coerce('non-date', :date) }.to raise_error(ENVied::Coercer::UnsupportedCoercion)
74
208
  end
75
209
  end
76
210
 
77
211
  describe 'to time' do
78
- let(:coerce){ coerce_to(:time) }
212
+ it 'converts string to time without time part' do
213
+ time = coerce("2019-03-22", :time)
79
214
 
80
- it 'converts strings to time' do
81
- expect(coerce['4:00']).to eq Time.parse('4:00')
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)
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)
82
238
  end
83
239
  end
84
240
 
85
241
  describe 'to array' do
86
- let(:coerce){ coerce_to(:array) }
87
-
88
242
  it 'converts strings to array' do
89
243
  {
90
244
  'a,b' => ['a','b'],
91
245
  ' a, b' => [' a',' b'],
92
246
  'apples,and\, of course\, pears' => ['apples','and, of course, pears'],
93
- }.each do |i, o|
94
- expect(coerce[i]).to eq o
247
+ }.each do |value, array|
248
+ expect(coerce(value, :array)).to eq array
95
249
  end
96
250
  end
97
251
  end
98
252
 
99
253
  describe 'to hash' do
100
- let(:coerce){ coerce_to(:hash) }
101
-
102
254
  it 'converts strings to hashes' do
103
255
  {
104
256
  'a=1' => {'a' => '1'},
105
257
  'a=1&b=2' => {'a' => '1', 'b' => '2'},
106
258
  'a=&b=2' => {'a' => '', 'b' => '2'},
107
259
  'a&b=2' => {'a' => nil, 'b' => '2'},
108
- }.each do |i, o|
109
- expect(coerce[i]).to eq o
260
+ }.each do |value, hash|
261
+ expect(coerce(value, :hash)).to eq hash
110
262
  end
111
263
  end
112
264
  end
113
265
 
114
266
  describe 'to uri' do
115
- let(:coerce){ coerce_to(:uri) }
116
-
117
267
  it 'converts strings to uris' do
118
- 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'
119
271
  end
120
272
  end
121
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)