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.
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TTY
2
4
  class Config
3
- VERSION = "0.2.0".freeze
5
+ VERSION = "0.3.0"
4
6
  end
5
7
  end
@@ -0,0 +1,54 @@
1
+ if ENV['COVERAGE'] || ENV['TRAVIS']
2
+ require 'simplecov'
3
+ require 'coveralls'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
9
+
10
+ SimpleCov.start do
11
+ command_name 'spec'
12
+ add_filter 'spec'
13
+ end
14
+ end
15
+
16
+ require "bundler/setup"
17
+ require "tty/config"
18
+
19
+ module TestHelpers
20
+ module Paths
21
+ def gem_root
22
+ File.expand_path(File.join(File.dirname(__FILE__), ".."))
23
+ end
24
+
25
+ def dir_path(*args)
26
+ path = File.join(gem_root, *args)
27
+ FileUtils.mkdir_p(path) unless ::File.exist?(path)
28
+ File.realpath(path)
29
+ end
30
+
31
+ def tmp_path(*args)
32
+ File.join(dir_path('tmp'), *args)
33
+ end
34
+
35
+ def fixtures_path(*args)
36
+ File.join(dir_path('spec/fixtures'), *args)
37
+ end
38
+
39
+ def within_dir(target, &block)
40
+ ::Dir.chdir(target, &block)
41
+ end
42
+ end
43
+ end
44
+
45
+ RSpec.configure do |config|
46
+ config.include(TestHelpers::Paths)
47
+ config.disable_monkey_patching!
48
+ config.after(:example, type: :cli) do
49
+ FileUtils.rm_rf(tmp_path)
50
+ end
51
+ config.expect_with :rspec do |c|
52
+ c.syntax = :expect
53
+ end
54
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe TTY::Config, '#alias_setting' do
4
+ it "aliases setting key" do
5
+ config = TTY::Config.new
6
+ config.set :foo, value: :baz
7
+
8
+ config.alias_setting :foo, to: :bar
9
+
10
+ expect(config.fetch(:foo)).to eq(:baz)
11
+ expect(config.fetch(:bar)).to eq(:baz)
12
+ end
13
+
14
+ it "aliases nested key to flat key" do
15
+ config = TTY::Config.new
16
+ config.set(:foo, :bar, :baz) { 12 }
17
+
18
+ config.alias_setting(:foo, :bar, :baz, to: :flat_foo)
19
+
20
+ expect(config.fetch(:foo, :bar, :baz)).to eq(12)
21
+ expect(config.fetch(:flat_foo)).to eq(12)
22
+ end
23
+
24
+ it "aliases nested key as a string to flat key" do
25
+ config = TTY::Config.new
26
+ config.set('foo.bar.baz') { 12 }
27
+
28
+ config.alias_setting(:foo, :bar, :baz, to: :flat_foo)
29
+
30
+ expect(config.fetch(:foo, :bar, :baz)).to eq(12)
31
+ expect(config.fetch(:flat_foo)).to eq(12)
32
+ end
33
+
34
+ it "aliases nested key to nested key" do
35
+ config = TTY::Config.new
36
+ config.set(:foo, :bar, :baz) { 12 }
37
+
38
+ config.alias_setting(:foo, :bar, :baz, to: [:bee, :bop])
39
+
40
+ expect(config.fetch(:foo, :bar, :baz)).to eq(12)
41
+ expect(config.fetch(:bee, :bop)).to eq(12)
42
+ end
43
+
44
+ it "fails to alias to already existing key" do
45
+ config = TTY::Config.new
46
+ config.set(:foo, value: 1)
47
+ config.set(:bar, value: 2)
48
+
49
+ expect {
50
+ config.alias_setting(:foo, to: :bar)
51
+ }.to raise_error(ArgumentError, "Setting already exists with an alias ':bar'")
52
+ end
53
+
54
+ it "fails to alias to already existing key" do
55
+ config = TTY::Config.new
56
+ config.set(:foo, :bar, value: 1)
57
+ config.set(:baz, :woo, value: 2)
58
+
59
+ expect {
60
+ config.alias_setting(:foo, :bar, to: [:baz, :woo])
61
+ }.to raise_error(ArgumentError, "Setting already exists with an alias ':baz, :woo'")
62
+ end
63
+
64
+ it "fails to alias to matching key" do
65
+ config = TTY::Config.new
66
+ config.set(:foo, :bar, value: 1)
67
+
68
+ expect {
69
+ config.alias_setting(:foo, :bar, to: [:foo, :bar])
70
+ }.to raise_error(ArgumentError, "Alias matches setting key")
71
+ end
72
+ end
@@ -0,0 +1,26 @@
1
+ RSpec.describe TTY::Config, '#append' do
2
+ it "returns appended values" do
3
+ config = TTY::Config.new
4
+ values = config.append(:foo, :bar, to: :values)
5
+
6
+ expect(values).to eq([:foo, :bar])
7
+ expect(config.fetch(:values)).to eq([:foo, :bar])
8
+ end
9
+
10
+ it "appends values to already existing key" do
11
+ config = TTY::Config.new
12
+ config.set(:values) { :foo }
13
+ values = config.append(:bar, :baz, to: :values)
14
+
15
+ expect(config.fetch(:values)).to eq([:foo, :bar, :baz])
16
+ expect(values).to eq([:foo, :bar, :baz])
17
+ end
18
+
19
+ it "appends values to nested key" do
20
+ config = TTY::Config.new
21
+ config.set(:foo, :bar) { 1 }
22
+ values = config.append(2,3, to: [:foo, :bar])
23
+ expect(values).to eq([1,2,3])
24
+ expect(config.fetch(:foo, :bar)).to eq([1,2,3])
25
+ end
26
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe TTY::Config, '#autoload_env' do
4
+ it "autoloads env variables" do
5
+ allow(ENV).to receive(:[]).with('HOST').and_return('localhost')
6
+ config = TTY::Config.new
7
+ expect(config.fetch(:host)).to eq(nil)
8
+
9
+ config.autoload_env
10
+
11
+ expect(config.fetch(:host)).to eq('localhost')
12
+ end
13
+
14
+ it "autoloads env variables with prefix" do
15
+ allow(ENV).to receive(:[]).with('MYTOOL_HOST').and_return('localhost')
16
+ config = TTY::Config.new
17
+ config.env_prefix = 'mytool'
18
+
19
+ expect(config.fetch(:host)).to eq(nil)
20
+
21
+ config.autoload_env
22
+
23
+ expect(config.fetch(:host)).to eq('localhost')
24
+ end
25
+
26
+ it "prioritises set over env vars" do
27
+ allow(ENV).to receive(:[]).with('HOST').and_return('localhost')
28
+ config = TTY::Config.new
29
+ config.autoload_env
30
+
31
+ config.set(:host, value: 'myhost')
32
+
33
+ expect(config.fetch(:host)).to eq('myhost')
34
+ end
35
+
36
+ it "prioritises env vars over defaults when a keyword" do
37
+ allow(ENV).to receive(:[]).with('PORT').and_return('7727')
38
+ config = TTY::Config.new
39
+
40
+ config.autoload_env
41
+
42
+ expect(config.fetch(:port, default: '3000')).to eq('7727')
43
+ end
44
+
45
+ it "prioritises env vars over defaults when block" do
46
+ allow(ENV).to receive(:[]).with('PORT').and_return('7727')
47
+ config = TTY::Config.new
48
+
49
+ config.autoload_env
50
+
51
+ expect(config.fetch(:port) {'3000' }).to eq('7727')
52
+ end
53
+
54
+ it "prioritises present configuration over env vars" do
55
+ allow(ENV).to receive(:[]).with('PORT').and_return('7727')
56
+ config = TTY::Config.new(port: '3000')
57
+
58
+ config.autoload_env
59
+
60
+ expect(config.fetch(:port)).to eq('3000')
61
+ end
62
+ end
@@ -0,0 +1,22 @@
1
+ RSpec.describe TTY::Config, '#delete' do
2
+ it "deletes the value" do
3
+ config = TTY::Config.new
4
+ config.set(:foo, value: 2)
5
+ expect(config.delete(:foo)).to eq(2)
6
+ expect(config.fetch(:foo)).to eq(nil)
7
+ end
8
+
9
+ it "deletes the value under deeply nested key" do
10
+ config = TTY::Config.new
11
+ config.set(:foo, :bar, :baz) { 2 }
12
+ expect(config.delete(:foo, :bar, :baz).call).to eq(2)
13
+ expect(config.fetch(:foo, :bar, :baz)).to eq(nil)
14
+ end
15
+
16
+ it "deletes innermost key with array value" do
17
+ config = TTY::Config.new
18
+ config.set(:foo, :bar, value: [1,2,3])
19
+ expect(config.delete(:foo, :bar)).to eq([1,2,3])
20
+ expect(config.fetch(:foo, :bar)).to eq(nil)
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ RSpec.describe TTY::Config, '#exist?', type: :cli do
2
+ it "checks if configuration file exists" do
3
+ config = TTY::Config.new
4
+ config.append_path(tmp_path)
5
+
6
+ expect(config.exist?).to eq(false)
7
+
8
+ config.write(tmp_path('investments.yml'))
9
+
10
+ expect(config.exist?).to eq(true)
11
+ end
12
+
13
+ it "checks if a file without extension is present" do
14
+ config = TTY::Config.new
15
+ config.append_path tmp_path
16
+
17
+ expect(config.exist?).to eq(false)
18
+
19
+ config.write(tmp_path('investments'), format: :yml)
20
+
21
+ expect(config.exist?).to eq(true)
22
+ expect(config.persisted?).to eq(true)
23
+ end
24
+ end
@@ -0,0 +1,45 @@
1
+ RSpec.describe TTY::Config do
2
+ it "fetches default if no value" do
3
+ config = TTY::Config.new
4
+ expect(config.fetch(:foo, default: :bar)).to eq(:bar)
5
+ end
6
+
7
+ it "fetches default proc value" do
8
+ config = TTY::Config.new
9
+ expect(config.fetch(:foo, default: -> { :bar })).to eq(:bar)
10
+ end
11
+
12
+ it "fetches deeply nested proc value" do
13
+ config = TTY::Config.new
14
+ expect(config.fetch(:foo, default: -> { -> { :bar }})).to eq(:bar)
15
+ end
16
+
17
+ it "fetches default as block" do
18
+ config = TTY::Config.new
19
+ expect(config.fetch(:foo) { :bar }).to eq(:bar)
20
+ end
21
+
22
+ it "fetches default as block for deeply nested missing key" do
23
+ config = TTY::Config.new
24
+ expect(config.fetch(:foo, :bar, :baz) { 2 }).to eq(2)
25
+ end
26
+
27
+ it "fetches value for deeply nested key" do
28
+ config = TTY::Config.new
29
+ config.set(:foo, :bar, :baz, value: 2)
30
+ expect(config.fetch(:foo, :bar, :baz)).to eq(2)
31
+ end
32
+
33
+ it "fetches value as string delimited by . for deeply nested key" do
34
+ config = TTY::Config.new
35
+ config.set('foo', 'bar', 'baz') { 2 }
36
+ expect(config.fetch("foo.bar.baz")).to eq(2)
37
+ end
38
+
39
+ it "fetches key with indifferent access" do
40
+ config = TTY::Config.new
41
+ config.set(:foo, :bar, :baz, value: 2)
42
+
43
+ expect(config.fetch('foo', :bar, 'baz')).to eq(2)
44
+ end
45
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe TTY::Config, '#generate' do
4
+ it "generate config content" do
5
+ conf = {
6
+ 'int' => 1,
7
+ 'false' => false,
8
+ 'str' => 'hello',
9
+ 'array' => [1,2,3],
10
+ 'deep_array' => [
11
+ {foo: 1},
12
+ {bar: 2}
13
+ ],
14
+ 'section' => {
15
+ 'value' => 1,
16
+ 'empty' => nil,
17
+ 'array' => [1,2,3]
18
+ },
19
+ "empty" => { },
20
+ 'nil' => nil,
21
+ }
22
+
23
+ content = TTY::Config.generate(conf)
24
+
25
+ expect(content).to eq <<-EOS
26
+ array = 1,2,3
27
+ false = false
28
+ int = 1
29
+ str = hello
30
+
31
+ [deep_array]
32
+ foo = 1
33
+ bar = 2
34
+
35
+ [section]
36
+ value = 1
37
+ array = 1,2,3
38
+ EOS
39
+ end
40
+
41
+ it "generate config content with custom separator" do
42
+ conf = {
43
+ 'str' => 'hello',
44
+ 'array' => [1,2,3],
45
+ 'deep_array' => [
46
+ {foo: 1},
47
+ {bar: 2}
48
+ ],
49
+ 'section' => {
50
+ 'value' => 1,
51
+ 'array' => [1,2,3]
52
+ }
53
+ }
54
+
55
+ content = TTY::Config.generate(conf, separator: ':')
56
+
57
+ expect(content).to eq <<-EOS
58
+ array : 1,2,3
59
+ str : hello
60
+
61
+ [deep_array]
62
+ foo : 1
63
+ bar : 2
64
+
65
+ [section]
66
+ value : 1
67
+ array : 1,2,3
68
+ EOS
69
+ end
70
+ end
@@ -0,0 +1,13 @@
1
+ RSpec.describe TTY::Config, '#merge' do
2
+ it "merges nested hash" do
3
+ config = TTY::Config.new
4
+ config.set(:a, :b, value: 1)
5
+ config.set(:a, :c, value: 2)
6
+
7
+ config.merge({'a' => {'c' => 3, 'd' => 4}})
8
+
9
+ expect(config.fetch(:a, :b)).to eq(1)
10
+ expect(config.fetch(:a, :c)).to eq(3)
11
+ expect(config.fetch(:a, :d)).to eq(4)
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ RSpec.describe TTY::Config, '#new' do
2
+ it "sets settings through initialization" do
3
+ config = TTY::Config.new(foo: "bar")
4
+ expect(config.fetch(:foo)).to eq("bar")
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ RSpec.describe TTY::Config, '#normalize_hash' do
2
+ it "normalizes keys via method to symbols" do
3
+ hash = {
4
+ "settings" => {
5
+ "base" => "USD",
6
+ "color" => true,
7
+ "exchange" => "CCCAGG"
8
+ },
9
+ "coins" => ["BTC", "ETH", "TRX", "DASH"]
10
+ }
11
+
12
+ expect(TTY::Config.normalize_hash(hash)).to eq({
13
+ settings: {
14
+ base: "USD",
15
+ color: true,
16
+ exchange: "CCCAGG"
17
+ },
18
+ coins: ["BTC", "ETH", "TRX", "DASH"]
19
+ })
20
+ end
21
+ end
@@ -0,0 +1,109 @@
1
+ RSpec.describe TTY::Config, '#read' do
2
+ it "reads from a specified file" do
3
+ file = fixtures_path('investments.yml')
4
+ config = TTY::Config.new
5
+
6
+ config.read(file)
7
+
8
+ expect(config.fetch(:settings, :base)).to eq('USD')
9
+ end
10
+
11
+ it "searched for a file to read from" do
12
+ config = TTY::Config.new
13
+ config.filename = 'investments'
14
+ config.append_path(fixtures_path)
15
+
16
+ config.read
17
+
18
+ expect(config.fetch(:settings, :base)).to eq('USD')
19
+ end
20
+
21
+ it "reads from a specified file and merges with defaults" do
22
+ file = fixtures_path('investments.yml')
23
+ config = TTY::Config.new
24
+ config.set(:settings, :base, value: 'EUR')
25
+ config.set(:settings, :top, value: 50)
26
+
27
+ config.read(file)
28
+
29
+ expect(config.fetch(:settings, :base)).to eq('USD')
30
+ expect(config.fetch(:settings, :exchange)).to eq('CCCAGG')
31
+ expect(config.fetch(:settings, :top)).to eq(50)
32
+ end
33
+
34
+ it "reads a json format" do
35
+ file = fixtures_path('investments.json')
36
+ config = TTY::Config.new
37
+
38
+ config.read(file)
39
+
40
+ expect(config.filename).to eq('investments')
41
+ expect(config.extname).to eq('.json')
42
+ expect(config.fetch(:settings, :base)).to eq('USD')
43
+ expect(config.fetch(:coins)).to eq(["BTC", "ETH", "TRX", "DASH"])
44
+ end
45
+
46
+ it "reads a toml format" do
47
+ file = fixtures_path('investments.toml')
48
+ config = TTY::Config.new
49
+
50
+ config.read(file)
51
+
52
+ expect(config.filename).to eq('investments')
53
+ expect(config.extname).to eq('.toml')
54
+ expect(config.fetch(:settings, :base)).to eq('USD')
55
+ expect(config.fetch(:coins)).to eq(["BTC", "ETH", "TRX", "DASH"])
56
+ end
57
+
58
+ it "reads an ini format" do
59
+ file = fixtures_path('investments.ini')
60
+ config = TTY::Config.new
61
+
62
+ config.read(file)
63
+
64
+ expect(config.filename).to eq('investments')
65
+ expect(config.extname).to eq('.ini')
66
+ expect(config.fetch(:settings, :base)).to eq('USD')
67
+ expect(config.fetch(:coins).split(',')).to eq(["BTC", "ETH", "TRX", "DASH"])
68
+ end
69
+
70
+ it "reads custom format" do
71
+ file = fixtures_path('.env')
72
+ config = TTY::Config.new
73
+
74
+ config.read(file, format: :ini)
75
+
76
+ expect(config.filename).to eq('.env')
77
+ expect(config.extname).to eq('')
78
+ expect(config.fetch(:settings, :base)).to eq('USD')
79
+ expect(config.fetch(:coins).split(',')).to eq(["BTC", "ETH", "TRX", "DASH"])
80
+ end
81
+
82
+ it "fails to find a file to read" do
83
+ config = TTY::Config.new
84
+ expect {
85
+ config.read
86
+ }.to raise_error(TTY::Config::ReadError,
87
+ "No file found to read configuration from!")
88
+ end
89
+
90
+ it "fails to read a file" do
91
+ config = TTY::Config.new
92
+ file = fixtures_path('unknown.yml')
93
+
94
+ expect {
95
+ config.read(file)
96
+ }.to raise_error(TTY::Config::ReadError,
97
+ "Configuration file `#{file}` does not exist!")
98
+ end
99
+
100
+ it "fails to load dependency for reading file format" do
101
+ config = TTY::Config.new
102
+ file = fixtures_path('investments.yml')
103
+ allow(config).to receive(:require).with('yaml').and_raise(LoadError)
104
+
105
+ expect {
106
+ config.read(file)
107
+ }.to raise_error(TTY::Config::ReadError, "Gem `yaml` is missing. Please install it to read .yml configuration format.")
108
+ end
109
+ end