tty-config 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ RSpec.describe TTY::Config, '#remove' do
2
+ it "removes a value from a key" do
3
+ config = TTY::Config.new
4
+ config.set(:values) { [:foo, :bar] }
5
+ values = config.remove(:bar, from: :values)
6
+ expect(values).to eq([:foo])
7
+ expect(config.fetch(:values)).to eq([:foo])
8
+ end
9
+
10
+ it "removes multiple values from a nested key" do
11
+ config = TTY::Config.new
12
+ config.set(:foo, :bar, :baz) { [1,2,3,4] }
13
+ config.remove(2,4, from: [:foo, :bar, :baz])
14
+ expect(config.fetch(:foo, :bar, :baz)).to eq([1,3])
15
+ end
16
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe TTY::Config, '#set_from_env' do
4
+ it "fetches env variables" do
5
+ allow(ENV).to receive(:[]).with('HOST').and_return('localhost')
6
+ allow(ENV).to receive(:[]).with('PORT').and_return('7727')
7
+
8
+ config = TTY::Config.new
9
+ config.set_from_env(:host)
10
+ config.set_from_env(:port)
11
+
12
+ expect(config.fetch(:host)).to eq('localhost')
13
+ expect(config.fetch(:port)).to eq('7727')
14
+ end
15
+
16
+ it "fetches multipart env variables" do
17
+ allow(ENV).to receive(:[]).with('FOO_BAR_BAZ').and_return('1')
18
+ config = TTY::Config.new
19
+ config.set_from_env(:foo_bar_baz)
20
+
21
+ expect(config.fetch(:foo_bar_baz)).to eq('1')
22
+ end
23
+
24
+ it "prefixes env variables" do
25
+ allow(ENV).to receive(:[]).with('MYTOOL_HOST').and_return('localhost')
26
+ allow(ENV).to receive(:[]).with('MYTOOL_PORT').and_return('7727')
27
+
28
+ config = TTY::Config.new
29
+ config.env_prefix = 'mytool'
30
+ config.set_from_env(:host)
31
+ config.set_from_env(:port)
32
+
33
+ expect(config.fetch(:host)).to eq('localhost')
34
+ expect(config.fetch(:port)).to eq('7727')
35
+ end
36
+
37
+ it "allows to set env vars for deeply nested keys" do
38
+ allow(ENV).to receive(:[]).with('HOST').and_return('localhost')
39
+
40
+ config = TTY::Config.new
41
+ config.set_from_env(:foo, :bar) { "HOST" }
42
+
43
+ expect(config.fetch(:foo, :bar)).to eq('localhost')
44
+ end
45
+
46
+ it "allows to set env vars with prefix for deeply nested keys" do
47
+ allow(ENV).to receive(:[]).with('MYTOOL_HOST').and_return('localhost')
48
+
49
+ config = TTY::Config.new
50
+ config.env_prefix = 'mytool'
51
+ config.set_from_env(:foo, :bar) { "HOST" }
52
+
53
+ expect(config.fetch(:foo, :bar)).to eq('localhost')
54
+ end
55
+
56
+ it "allows to set env vars for deeply nested keys as string" do
57
+ allow(ENV).to receive(:[]).with('HOST').and_return('localhost')
58
+
59
+ config = TTY::Config.new
60
+ config.set_from_env('foo.bar') { "HOST" }
61
+
62
+ expect(config.fetch(:foo, :bar)).to eq('localhost')
63
+ end
64
+
65
+ it "fails if env var is not specified for deeply nested keys as string" do
66
+ config = TTY::Config.new
67
+ expect {
68
+ config.set_from_env('foo.bar')
69
+ }.to raise_error(ArgumentError, 'Need to set env var in block')
70
+ end
71
+
72
+ it "fails if env var is not specified for deeply nested keys" do
73
+ config = TTY::Config.new
74
+ expect {
75
+ config.set_from_env(:foo, :bar)
76
+ }.to raise_error(ArgumentError, 'Need to set env var in block')
77
+ end
78
+ end
@@ -0,0 +1,26 @@
1
+ RSpec.describe TTY::Config, '#set_if_empty' do
2
+ it "sets value for empty" do
3
+ config = TTY::Config.new
4
+ config.set_if_empty(:foo, value: :bar)
5
+ expect(config.fetch(:foo)).to eq(:bar)
6
+ end
7
+
8
+ it "sets value for empty deeply nested key" do
9
+ config = TTY::Config.new({foo: {}})
10
+ config.set_if_empty(:foo, :bar, :baz, value: 2)
11
+ expect(config.fetch(:foo, :bar, :baz)).to eq(2)
12
+ end
13
+
14
+ it "sets value for a nested key as string delimited by dot" do
15
+ config = TTY::Config.new
16
+ config.set_if_empty("foo.bar.baz", value: 1)
17
+ expect(config.fetch('foo', 'bar', 'baz')).to eq(1)
18
+ end
19
+
20
+ it "doesn't override existing value" do
21
+ config = TTY::Config.new
22
+ config.set(:foo, value: 1)
23
+ config.set_if_empty(:foo, value: 2)
24
+ expect(config.fetch(:foo)).to eq(1)
25
+ end
26
+ end
@@ -0,0 +1,62 @@
1
+ RSpec.describe TTY::Config, '#set' do
2
+ it "sets value" do
3
+ config = TTY::Config.new
4
+ config.set(:foo, value: :bar)
5
+ expect(config.fetch(:foo)).to eq(:bar)
6
+ end
7
+
8
+ it "sets value as block" do
9
+ config = TTY::Config.new
10
+ config.set(:foo) { "bar" }
11
+ expect(config.fetch(:foo)).to eq("bar")
12
+ end
13
+
14
+ it "sets value for deep ensted key" do
15
+ config = TTY::Config.new
16
+ value = config.set(:foo, :bar, :baz, value: 2)
17
+ expect(value).to eq(2)
18
+ expect(config.fetch(:foo, :bar, :baz)).to eq(2)
19
+ end
20
+
21
+ it "sets value as block for deep ensted key" do
22
+ config = TTY::Config.new
23
+ value = config.set(:foo, :bar, :baz) { 2 }
24
+ expect(value.call).to eq(2)
25
+ expect(config.fetch(:foo, :bar, :baz)).to eq(2)
26
+ end
27
+
28
+ it "sets value for deep nested string key delimited by ." do
29
+ config = TTY::Config.new
30
+ value = config.set("foo.bar.baz", value: 2)
31
+ expect(value).to eq(2)
32
+ expect(config.fetch('foo', 'bar', 'baz')).to eq(2)
33
+ end
34
+
35
+ it "sets value as block for deep nested string key delimited by ." do
36
+ config = TTY::Config.new
37
+ value = config.set("foo.bar.baz") { 2 }
38
+ expect(value.call).to eq(2)
39
+ expect(config.fetch('foo', 'bar', 'baz')).to eq(2)
40
+ end
41
+
42
+ it "overrides existing value" do
43
+ config = TTY::Config.new({foo: {bar: 1}})
44
+ expect(config.fetch(:foo, :bar)).to eq(1)
45
+ config.set(:foo, :bar) { 2 }
46
+ expect(config.fetch(:foo, :bar)).to eq(2)
47
+ end
48
+
49
+ it "raises an exception when value & block provided" do
50
+ config = TTY::Config.new
51
+ expect {
52
+ config.set(:foo, value: :bar) { :baz }
53
+ }.to raise_error(ArgumentError, "Can't set both value and block")
54
+ end
55
+
56
+ it "raises an exception when no value or block provided" do
57
+ config = TTY::Config.new
58
+ expect {
59
+ config.set(:foo, :bar, 2)
60
+ }.to raise_error(ArgumentError, "Need to set either value or block")
61
+ end
62
+ end
@@ -0,0 +1,76 @@
1
+ RSpec.describe TTY::Config, '#validate' do
2
+ let(:validation) {
3
+ -> (key, value) do
4
+ unless value.is_a?(Integer)
5
+ raise TTY::Config::ValidationError, "Failed validation for key=#{key}"
6
+ end
7
+ end
8
+ }
9
+
10
+ it "validates string value successfully" do
11
+ config = TTY::Config.new
12
+ config.validate(:foo, :bar, &validation)
13
+ config.set(:foo, :bar, value: 2)
14
+ expect(config.fetch(:foo, :bar)).to eq(2)
15
+ end
16
+
17
+ it "validates a proc value successfully" do
18
+ config = TTY::Config.new
19
+ config.validate(:foo, :bar, &validation)
20
+ config.set(:foo, :bar, value: -> { 2 })
21
+ expect(config.fetch(:foo, :bar)).to eq(2)
22
+ end
23
+
24
+ it "validates a block value successfully" do
25
+ config = TTY::Config.new
26
+ config.validate(:foo, :bar, &validation)
27
+ config.set(:foo, :bar) { 2 }
28
+ expect(config.fetch(:foo, :bar)).to eq(2)
29
+ end
30
+
31
+ it "raises an error when set value fails validation" do
32
+ config = TTY::Config.new
33
+ config.validate(:foo, :bar, &validation)
34
+ expect {
35
+ config.set(:foo, :bar, value: '2')
36
+ }.to raise_error(TTY::Config::ValidationError,
37
+ 'Failed validation for key=foo.bar')
38
+ end
39
+
40
+ it "raises an error when set value as a proc fails validation" do
41
+ config = TTY::Config.new
42
+ config.validate(:foo, :bar, &validation)
43
+ config.set(:foo, :bar, value: -> { '2' })
44
+ expect {
45
+ config.fetch(:foo, :bar)
46
+ }.to raise_error(TTY::Config::ValidationError,
47
+ 'Failed validation for key=foo.bar')
48
+ end
49
+
50
+ it "raises an eror when ste value as a block fails validation" do
51
+ config = TTY::Config.new
52
+ config.validate(:foo, :bar, &validation)
53
+ config.set(:foo, :bar) { '2' }
54
+ expect {
55
+ config.fetch(:foo, :bar)
56
+ }.to raise_error(TTY::Config::ValidationError,
57
+ 'Failed validation for key=foo.bar')
58
+ end
59
+
60
+ it "applies multiple validations" do
61
+ config = TTY::Config.new
62
+ config.validate(:foo, :bar) do |key, val|
63
+ unless val.is_a?(Integer)
64
+ raise TTY::Config::ValidationError, 'Not integer'
65
+ end
66
+ end
67
+ config.validate(:foo, :bar) do |key, val|
68
+ unless val > 100
69
+ raise TTY::Config::ValidationError , 'Value out of range'
70
+ end
71
+ end
72
+ expect {
73
+ config.set(:foo, :bar, value: 99)
74
+ }.to raise_error(TTY::Config::ValidationError, 'Value out of range')
75
+ end
76
+ end
@@ -0,0 +1,197 @@
1
+ RSpec.describe TTY::Config, '#write', type: :cli do
2
+ it "writes configuration to a specified file" do
3
+ config = TTY::Config.new
4
+ config.set(:settings, :base, value: 'USD')
5
+ config.set(:settings, :exchange, value: 'CCCAGG')
6
+ config.set(:coins, value: ['BTC', 'TRX', 'DASH'])
7
+ file = tmp_path('config.yml')
8
+
9
+ config.write(file)
10
+
11
+ expect(::File.read(file)).to eq <<-EOS
12
+ ---
13
+ settings:
14
+ base: USD
15
+ exchange: CCCAGG
16
+ coins:
17
+ - BTC
18
+ - TRX
19
+ - DASH
20
+ EOS
21
+ end
22
+
23
+ it "writes by default to the current directory" do
24
+ config = TTY::Config.new
25
+ config.set('settings', 'base', value: 'USD')
26
+ config.set('settings', 'exchange', value: 'CCCAGG')
27
+ config.set('coins', value: ['BTC', 'TRX', 'DASH'])
28
+
29
+ config.write
30
+
31
+ file = dir_path('config.yml')
32
+ expect(::File.read(file)).to eq <<-EOS
33
+ ---
34
+ settings:
35
+ base: USD
36
+ exchange: CCCAGG
37
+ coins:
38
+ - BTC
39
+ - TRX
40
+ - DASH
41
+ EOS
42
+ FileUtils.rm_rf(file)
43
+ end
44
+
45
+ it "doesn't override already existing file" do
46
+ config = TTY::Config.new
47
+ config.set('settings', 'base', value: 'USD')
48
+ file = tmp_path('config.yml')
49
+
50
+ config.write(file)
51
+
52
+ expect {
53
+ config.write(file)
54
+ }.to raise_error(TTY::Config::WriteError,
55
+ "File `#{file}` already exists. Use :force option to overwrite.")
56
+ end
57
+
58
+ it "allows to overwrite already existing file" do
59
+ config = TTY::Config.new
60
+ config.set('settings', 'base', value: 'USD')
61
+ file = tmp_path('config.yml')
62
+
63
+ config.write(file)
64
+
65
+ config.write(file, force: true)
66
+ end
67
+
68
+ it "writes json format" do
69
+ config = TTY::Config.new
70
+ config.set(:settings, :base, value: 'USD')
71
+ config.set(:settings, :exchange, value: 'CCCAGG')
72
+ config.set(:coins, value: ['BTC', 'TRX', 'DASH'])
73
+ file = tmp_path('config.json')
74
+
75
+ config.write(file)
76
+
77
+ expect(config.extname).to eq('.json')
78
+ expect(::File.read(file)).to eq <<-EOS.chomp
79
+ {
80
+ "settings": {
81
+ "base": "USD",
82
+ "exchange": "CCCAGG"
83
+ },
84
+ "coins": [
85
+ "BTC",
86
+ "TRX",
87
+ "DASH"
88
+ ]
89
+ }
90
+ EOS
91
+ end
92
+
93
+ it "writes toml format and assigns default filename and extension" do
94
+ config = TTY::Config.new
95
+ config.set(:settings, :base, value: 'USD')
96
+ config.set(:settings, :exchange, value: 'CCCAGG')
97
+ config.set(:coins, value: ['BTC', 'TRX', 'DASH'])
98
+ file = tmp_path('investments.toml')
99
+
100
+ config.write(file)
101
+
102
+ expect(config.filename).to eq('investments')
103
+ expect(config.extname).to eq('.toml')
104
+ expect(::File.read(file)).to eq <<-EOS
105
+ coins = ["BTC","TRX","DASH"]
106
+
107
+ [settings]
108
+ base = "USD"
109
+ exchange = "CCCAGG"
110
+ EOS
111
+ end
112
+
113
+ it "allows to change default file extension" do
114
+ config = TTY::Config.new
115
+ config.filename = 'investments'
116
+ config.extname = '.toml'
117
+ config.set(:settings, :base, value: 'USD')
118
+ config.set(:settings, :exchange, value: 'CCCAGG')
119
+ config.set(:coins, value: ['BTC', 'TRX', 'DASH'])
120
+
121
+ config.write
122
+
123
+ file = dir_path('investments.toml')
124
+ expect(::File.read(file)).to eq <<-EOS
125
+ coins = ["BTC","TRX","DASH"]
126
+
127
+ [settings]
128
+ base = "USD"
129
+ exchange = "CCCAGG"
130
+ EOS
131
+ FileUtils.rm_rf(file)
132
+ end
133
+
134
+ it "writes ini format and assigns default filename and extension" do
135
+ config = TTY::Config.new
136
+ config.set(:settings, :base, value: 'USD')
137
+ config.set(:settings, :exchange, value: 'CCCAGG')
138
+ config.set(:coins, value: "BTC,TRX,DASH")
139
+ file = tmp_path('investments.ini')
140
+
141
+ config.write(file)
142
+
143
+ expect(config.filename).to eq('investments')
144
+ expect(config.extname).to eq('.ini')
145
+ expect(::File.read(file)).to eq <<-EOS
146
+ coins = BTC,TRX,DASH
147
+
148
+ [settings]
149
+ base = USD
150
+ exchange = CCCAGG
151
+ EOS
152
+ end
153
+
154
+ it "writes custom format with custom file extension" do
155
+ config = TTY::Config.new
156
+ config.set(:settings, :base, value: 'USD')
157
+ config.set(:settings, :exchange, value: 'CCCAGG')
158
+ config.set(:coins, value: ['BTC', 'TRX', 'DASH'])
159
+ file = tmp_path('investments.conf')
160
+
161
+ config.write(file, format: :yaml)
162
+ expect(config.filename).to eq('investments')
163
+ expect(config.extname).to eq('.conf')
164
+ expect(::File.read(file)).to eq <<-EOS
165
+ ---
166
+ settings:
167
+ base: USD
168
+ exchange: CCCAGG
169
+ coins:
170
+ - BTC
171
+ - TRX
172
+ - DASH
173
+ EOS
174
+ end
175
+
176
+ it "cannot write unknown file format" do
177
+ config = TTY::Config.new
178
+ config.set(:settings, :base, value: 'USD')
179
+ file = tmp_path('config.txt')
180
+
181
+ expect {
182
+ config.write(file)
183
+ }.to raise_error(TTY::Config::UnsupportedExtError,
184
+ "Config file format `.txt` is not supported.")
185
+ end
186
+
187
+ it "fails to load dependency for writing file format" do
188
+ config = TTY::Config.new
189
+ file = tmp_path('investments.yml')
190
+ allow(config).to receive(:require).with('yaml').and_raise(LoadError)
191
+ stub_const("YAML", double(dump: 'ok'))
192
+
193
+ expect {
194
+ config.write(file)
195
+ }.to raise_error(TTY::Config::WriteError, "Gem `yaml` is missing. Please install it to read .yml configuration format.")
196
+ end
197
+ end