tty-config 0.2.0 → 0.3.0
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/CHANGELOG.md +17 -0
- data/README.md +332 -38
- data/lib/tty/config.rb +269 -67
- data/lib/tty/config/version.rb +3 -1
- data/spec/spec_helper.rb +54 -0
- data/spec/unit/alias_setting_spec.rb +72 -0
- data/spec/unit/append_spec.rb +26 -0
- data/spec/unit/autoload_env_spec.rb +62 -0
- data/spec/unit/delete_spec.rb +22 -0
- data/spec/unit/exist_spec.rb +24 -0
- data/spec/unit/fetch_spec.rb +45 -0
- data/spec/unit/generate_spec.rb +70 -0
- data/spec/unit/merge_spec.rb +13 -0
- data/spec/unit/new_spec.rb +6 -0
- data/spec/unit/normalize_hash_spec.rb +21 -0
- data/spec/unit/read_spec.rb +109 -0
- data/spec/unit/remove_spec.rb +16 -0
- data/spec/unit/set_from_env_spec.rb +78 -0
- data/spec/unit/set_if_empty_spec.rb +26 -0
- data/spec/unit/set_spec.rb +62 -0
- data/spec/unit/validate_spec.rb +76 -0
- data/spec/unit/write_spec.rb +197 -0
- data/tty-config.gemspec +4 -3
- metadata +35 -9
- data/.gitignore +0 -12
- data/.rspec +0 -3
- data/.travis.yml +0 -23
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -16
- data/appveyor.yml +0 -23
@@ -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
|