envied 0.9.1 → 0.9.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,8 +2,9 @@ class ENVied
2
2
  # Responsible for anything related to the ENV.
3
3
  class EnvProxy
4
4
  attr_reader :config, :coercer, :groups
5
+ private :config, :coercer, :groups
5
6
 
6
- def initialize(config, options = {})
7
+ def initialize(config, **options)
7
8
  @config = config
8
9
  @coercer = options.fetch(:coercer, ENVied::Coercer.new)
9
10
  @groups = options.fetch(:groups, [])
@@ -17,16 +18,6 @@ class ENVied
17
18
  variables.reject(&method(:coerced?)).reject(&method(:coercible?))
18
19
  end
19
20
 
20
- def variables
21
- @variables ||= begin
22
- config.variables.select {|v| groups.include?(v.group) }
23
- end
24
- end
25
-
26
- def variables_by_name
27
- Hash[variables.map {|v| [v.name, v] }]
28
- end
29
-
30
21
  def [](name)
31
22
  coerce(variables_by_name[name.to_sym])
32
23
  end
@@ -35,35 +26,45 @@ class ENVied
35
26
  variables_by_name[name.to_sym]
36
27
  end
37
28
 
38
- def env_value_of(var)
39
- ENV[var.name.to_s]
40
- end
41
-
42
- def default_value_of(var)
43
- var.default_value(ENVied, var)
44
- end
45
-
46
29
  def value_to_coerce(var)
47
30
  return env_value_of(var) unless env_value_of(var).nil?
48
31
  config.defaults_enabled? ? default_value_of(var) : nil
49
32
  end
50
33
 
34
+ private
35
+
51
36
  def coerce(var)
52
37
  coerced?(var) ?
53
38
  value_to_coerce(var) :
54
39
  coercer.coerce(value_to_coerce(var), var.type)
55
40
  end
56
41
 
42
+ def coerced?(var)
43
+ coercer.coerced?(value_to_coerce(var))
44
+ end
45
+
57
46
  def coercible?(var)
58
47
  coercer.coercible?(value_to_coerce(var), var.type)
59
48
  end
60
49
 
50
+ def default_value_of(var)
51
+ var.default_value(ENVied, var)
52
+ end
53
+
54
+ def env_value_of(var)
55
+ ENV[var.name.to_s]
56
+ end
57
+
61
58
  def missing?(var)
62
59
  value_to_coerce(var).nil?
63
60
  end
64
61
 
65
- def coerced?(var)
66
- coercer.coerced?(value_to_coerce(var))
62
+ def variables
63
+ @variables ||= config.variables.select {|v| groups.include?(v.group) }
64
+ end
65
+
66
+ def variables_by_name
67
+ @variables_by_name ||= variables.map {|v| [v.name, v] }.to_h
67
68
  end
68
69
  end
69
70
  end
@@ -15,12 +15,12 @@ class ENVied
15
15
 
16
16
  attr_reader :globs, :extensions
17
17
 
18
- def initialize(options = {})
18
+ def initialize(**options)
19
19
  @globs = options.fetch(:globs, self.defaults[:globs])
20
20
  @extensions = options.fetch(:extensions, self.defaults[:extensions])
21
21
  end
22
22
 
23
- def self.extract_from(globs, options = {})
23
+ def self.extract_from(globs, **options)
24
24
  new(options.merge(globs: Array(globs))).extract
25
25
  end
26
26
 
@@ -1,7 +1,7 @@
1
1
  class ENVied::Variable
2
2
  attr_reader :name, :type, :group, :default
3
3
 
4
- def initialize(name, type, options = {})
4
+ def initialize(name, type, **options)
5
5
  @name = name.to_sym
6
6
  @type = type.to_sym
7
7
  @group = options.fetch(:group, :default).to_sym
@@ -1,3 +1,3 @@
1
1
  class ENVied
2
- VERSION = '0.9.1'
2
+ VERSION = '0.9.2.rc1'
3
3
  end
@@ -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