chamber 0.0.4 → 1.0.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 +4 -4
- data/{LICENSE.txt → LICENSE} +0 -0
- data/README.md +718 -127
- data/bin/chamber +277 -0
- data/lib/chamber.rb +63 -65
- data/lib/chamber/file.rb +84 -0
- data/lib/chamber/file_set.rb +265 -0
- data/lib/chamber/namespace_set.rb +141 -0
- data/lib/chamber/rails.rb +3 -0
- data/lib/chamber/rails/railtie.rb +11 -0
- data/lib/chamber/settings.rb +152 -0
- data/lib/chamber/system_environment.rb +145 -0
- data/lib/chamber/version.rb +2 -2
- data/spec/lib/chamber/file_set_spec.rb +212 -0
- data/spec/lib/chamber/file_spec.rb +94 -0
- data/spec/lib/chamber/namespace_set_spec.rb +81 -0
- data/spec/lib/chamber/settings_spec.rb +135 -0
- data/spec/lib/chamber/system_environment_spec.rb +121 -0
- data/spec/lib/chamber_spec.rb +279 -243
- metadata +58 -59
- data/.gitignore +0 -17
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -5
- data/Gemfile +0 -4
- data/Rakefile +0 -6
- data/chamber.gemspec +0 -36
- data/spec/spec_helper.rb +0 -12
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'chamber/namespace_set'
|
3
|
+
|
4
|
+
class Chamber
|
5
|
+
describe NamespaceSet do
|
6
|
+
it 'can create a set from from a hash' do
|
7
|
+
namespace_set = NamespaceSet.new( environment: :development,
|
8
|
+
hostname: 'my host')
|
9
|
+
|
10
|
+
expect(namespace_set).to eq [:development, 'my host']
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'can create a set from an array' do
|
14
|
+
namespace_set = NamespaceSet.new([:development,
|
15
|
+
'my host'])
|
16
|
+
|
17
|
+
expect(namespace_set).to eq [:development, 'my host']
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can create a set from a set' do
|
21
|
+
original_set = Set[:development, 'my host']
|
22
|
+
namespace_set = NamespaceSet.new(original_set)
|
23
|
+
|
24
|
+
expect(namespace_set).to eq [:development, 'my host']
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'can turn itself into an array' do
|
28
|
+
namespace_set = NamespaceSet.new([:development, 'my host'])
|
29
|
+
|
30
|
+
expect(namespace_set.to_ary).to eq [:development, 'my host']
|
31
|
+
expect(namespace_set.to_a).to eq [:development, 'my host']
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'can combine itself with an array' do
|
35
|
+
namespace_set = NamespaceSet.new([:development, 'my host'])
|
36
|
+
other_set = Set['other value', 3]
|
37
|
+
combined_set = namespace_set + other_set
|
38
|
+
|
39
|
+
expect(combined_set).to eq [:development, 'my host', 'other value', 3]
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does not modify the set in place if combining with another array' do
|
43
|
+
namespace_set = NamespaceSet.new([:development, 'my host'])
|
44
|
+
other_set = Set['other value', 3]
|
45
|
+
combined_set = namespace_set + other_set
|
46
|
+
|
47
|
+
expect(combined_set.object_id).not_to eq namespace_set.object_id
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'can combine itself with something that can be converted to an array' do
|
51
|
+
namespace_set = NamespaceSet.new([:development, 'my host'])
|
52
|
+
other_set = (1..3)
|
53
|
+
combined_set = namespace_set + other_set
|
54
|
+
|
55
|
+
expect(combined_set).to eq [:development, 'my host', 1, 2, 3]
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'does not allow duplicate items' do
|
59
|
+
namespace_set = NamespaceSet.new([:development, :development])
|
60
|
+
|
61
|
+
expect(namespace_set).to eq [:development]
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'will process a value by executing it if it is a callable' do
|
65
|
+
namespace_set = NamespaceSet.new([ -> { 'callable' } ])
|
66
|
+
|
67
|
+
expect(namespace_set).to eq ['callable']
|
68
|
+
|
69
|
+
namespace_set = NamespaceSet.new({ :my_namespace => -> { 'callable' } })
|
70
|
+
|
71
|
+
expect(namespace_set).to eq ['callable']
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'can compare itself to another NamespaceSet' do
|
75
|
+
namespace_set = NamespaceSet.new([:development, :development])
|
76
|
+
other_namespace_set = NamespaceSet.new([:development, :development])
|
77
|
+
|
78
|
+
expect(namespace_set).to eql other_namespace_set
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'chamber/settings'
|
3
|
+
|
4
|
+
class Chamber
|
5
|
+
describe Settings do
|
6
|
+
it 'can verify that it is equal to another Settings object' do
|
7
|
+
settings = Settings.new( settings: {setting: 'value'},
|
8
|
+
namespaces: NamespaceSet.new(['good']))
|
9
|
+
other_settings = Settings.new( settings: {setting: 'value'},
|
10
|
+
namespaces: NamespaceSet.new(['good']))
|
11
|
+
|
12
|
+
expect(settings).to eql other_settings
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'does not consider itself equal if the namespaces are not equal' do
|
16
|
+
settings = Settings.new( settings: {setting: 'value'},
|
17
|
+
namespaces: NamespaceSet.new(['good']))
|
18
|
+
other_settings = Settings.new( settings: {setting: 'value'},
|
19
|
+
namespaces: NamespaceSet.new(['bad']))
|
20
|
+
|
21
|
+
expect(settings).not_to eql other_settings
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'does not consider itself equal if the settings are not equal' do
|
25
|
+
settings = Settings.new( settings: {setting: 'value'},
|
26
|
+
namespaces: NamespaceSet.new(['good']))
|
27
|
+
other_settings = Settings.new( settings: {setting: 'value 1'},
|
28
|
+
namespaces: NamespaceSet.new(['good']))
|
29
|
+
|
30
|
+
expect(settings).not_to eql other_settings
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'knows how to convert itself into an environment hash' do
|
34
|
+
allow(SystemEnvironment).to receive(:extract_from).
|
35
|
+
and_return(:environment => :development)
|
36
|
+
|
37
|
+
settings = Settings.new(settings: {setting: 'value'})
|
38
|
+
|
39
|
+
expect(settings.to_environment).to eql(:environment => :development)
|
40
|
+
expect(SystemEnvironment).to have_received(:extract_from).
|
41
|
+
with(Hashie::Mash.new setting: 'value')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'sorts environment variables by name when converted to an environment hash so that they are easier to parse for humans' do
|
45
|
+
allow(SystemEnvironment).to receive(:extract_from).
|
46
|
+
and_return('C' => 'value',
|
47
|
+
'D' => 'value',
|
48
|
+
'A' => 'value',
|
49
|
+
'E' => 'value',
|
50
|
+
'B' => 'value',)
|
51
|
+
|
52
|
+
settings = Settings.new(settings: { setting: 'value' })
|
53
|
+
|
54
|
+
expect(settings.to_environment.to_a).to eql([['A', 'value'],
|
55
|
+
['B', 'value'],
|
56
|
+
['C', 'value'],
|
57
|
+
['D', 'value'],
|
58
|
+
['E', 'value']])
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'can convert itself into a string' do
|
62
|
+
allow(SystemEnvironment).to receive(:extract_from).
|
63
|
+
and_return('C' => 'cv',
|
64
|
+
'D' => 'dv',
|
65
|
+
'A' => 'av',
|
66
|
+
'E' => 'ev',
|
67
|
+
'B' => 'bv',)
|
68
|
+
|
69
|
+
settings = Settings.new(settings: { setting: 'value' })
|
70
|
+
|
71
|
+
expect(settings.to_s).to eql %Q{A="av" B="bv" C="cv" D="dv" E="ev"}
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'can merge itself with a hash' do
|
75
|
+
settings = Settings.new(settings: {setting: 'value'})
|
76
|
+
settings.merge!(other_setting: 'another value')
|
77
|
+
|
78
|
+
expect(settings.to_hash).to eql Hashie::Mash.new( setting: 'value',
|
79
|
+
other_setting: 'another value')
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'can merge itself with Settings' do
|
83
|
+
settings = Settings.new(settings: {setting: 'value'},
|
84
|
+
namespaces: NamespaceSet.new(['good']))
|
85
|
+
|
86
|
+
other_settings = Settings.new(settings: {other_setting: 'another value'},
|
87
|
+
namespaces: NamespaceSet.new(['bad']))
|
88
|
+
|
89
|
+
settings.merge!(other_settings)
|
90
|
+
|
91
|
+
expect(settings).to eql Settings.new( settings: {
|
92
|
+
setting: 'value',
|
93
|
+
other_setting: 'another value' },
|
94
|
+
namespaces: NamespaceSet.new(['good', 'bad']))
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'can convert itself into a hash' do
|
98
|
+
settings = Settings.new(settings: {setting: 'value'})
|
99
|
+
|
100
|
+
expect(settings.to_hash).to eql('setting' => 'value')
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'allows messages to be passed through to the underlying data' do
|
104
|
+
settings = Settings.new(settings: {setting: 'value'})
|
105
|
+
|
106
|
+
expect(settings.setting).to eql 'value'
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'will still raise an error if the underlying data does not respond to it' do
|
110
|
+
settings = Settings.new(settings: {setting: 'value'})
|
111
|
+
|
112
|
+
expect { settings.unknown }.to raise_error NoMethodError
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'can notify properly whether it responds to messages if the underlying data does' do
|
116
|
+
settings = Settings.new(settings: {setting: 'value'})
|
117
|
+
|
118
|
+
expect(settings.respond_to?(:setting)).to be_a TrueClass
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'only includes namespaced data if any exists' do
|
122
|
+
settings = Settings.new(settings: {
|
123
|
+
namespace_value: {
|
124
|
+
namespace_setting: 'value' },
|
125
|
+
other_namespace_value: {
|
126
|
+
other_namespace_setting: 'value' },
|
127
|
+
non_namespace_setting: 'other value'
|
128
|
+
},
|
129
|
+
namespaces: NamespaceSet.new(['namespace_value', 'other_namespace_value']))
|
130
|
+
|
131
|
+
expect(settings.to_hash).to eql('namespace_setting' => 'value',
|
132
|
+
'other_namespace_setting' => 'value')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'chamber/system_environment'
|
3
|
+
|
4
|
+
class Chamber
|
5
|
+
describe SystemEnvironment do
|
6
|
+
it 'can extract environment variables based on a hash that is passed in' do
|
7
|
+
source_hash = {
|
8
|
+
level_one_1: {
|
9
|
+
level_two_1: 'value 1',
|
10
|
+
level_two_2: {
|
11
|
+
level_three_1: 'value 2',
|
12
|
+
level_three_2: 'value 3',
|
13
|
+
},
|
14
|
+
level_two_3: 'value 4',
|
15
|
+
level_one_2: 'value 5' }
|
16
|
+
}
|
17
|
+
|
18
|
+
expect(SystemEnvironment.extract_from(source_hash)).to eql({
|
19
|
+
'LEVEL_ONE_1_LEVEL_TWO_1' => 'value 1',
|
20
|
+
'LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_1' => 'value 2',
|
21
|
+
'LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_2' => 'value 3',
|
22
|
+
'LEVEL_ONE_1_LEVEL_TWO_3' => 'value 4',
|
23
|
+
'LEVEL_ONE_1_LEVEL_ONE_2' => 'value 5',
|
24
|
+
})
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'converts all items to strings so that they are usable as an environment variable' do
|
28
|
+
source_hash = {
|
29
|
+
value_one: Time.utc(2013, 10, 8, 18, 0, 1),
|
30
|
+
value_two: 3,
|
31
|
+
}
|
32
|
+
|
33
|
+
expect(SystemEnvironment.extract_from(source_hash)).to eql({
|
34
|
+
'VALUE_ONE' => '2013-10-08 18:00:01 UTC',
|
35
|
+
'VALUE_TWO' => '3',
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'allows injected environment variables to be prefixed for compatibility' do
|
40
|
+
ENV['PREFIX_VALUE_ONE'] = 'value 1'
|
41
|
+
|
42
|
+
source_hash = {
|
43
|
+
value_one: 'value',
|
44
|
+
}
|
45
|
+
|
46
|
+
environment_hash = SystemEnvironment.inject_into(source_hash, [:prefix])
|
47
|
+
|
48
|
+
expect(environment_hash).to eql({
|
49
|
+
'value_one' => 'value 1',
|
50
|
+
})
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'allows extracted environment variables to be prefixed for compatibility' do
|
54
|
+
source_hash = {
|
55
|
+
value_one: 'value',
|
56
|
+
}
|
57
|
+
|
58
|
+
environment_hash = SystemEnvironment.extract_from(source_hash, [:prefix])
|
59
|
+
|
60
|
+
expect(environment_hash).to eql({
|
61
|
+
'PREFIX_VALUE_ONE' => 'value',
|
62
|
+
})
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'can inject existing environment variable values into a hash' do
|
66
|
+
ENV['LEVEL_ONE_1_LEVEL_TWO_1'] = 'value 1'
|
67
|
+
ENV['LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_1'] = 'value 2'
|
68
|
+
ENV['LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_2'] = 'value 3'
|
69
|
+
ENV['LEVEL_ONE_1_LEVEL_TWO_3'] = 'value 4'
|
70
|
+
ENV['LEVEL_ONE_1_LEVEL_ONE_2'] = 'value 5'
|
71
|
+
|
72
|
+
source_hash = {
|
73
|
+
level_one_1: {
|
74
|
+
level_two_1: nil,
|
75
|
+
level_two_2: {
|
76
|
+
level_three_1: nil,
|
77
|
+
level_three_2: '',
|
78
|
+
},
|
79
|
+
level_two_3: '',
|
80
|
+
level_one_2: '' }
|
81
|
+
}
|
82
|
+
|
83
|
+
expect(SystemEnvironment.inject_into(source_hash)).to eql({
|
84
|
+
'level_one_1' => {
|
85
|
+
'level_two_1' => 'value 1',
|
86
|
+
'level_two_2' => {
|
87
|
+
'level_three_1' => 'value 2',
|
88
|
+
'level_three_2' => 'value 3',
|
89
|
+
},
|
90
|
+
'level_two_3' => 'value 4',
|
91
|
+
'level_one_2' => 'value 5' }
|
92
|
+
})
|
93
|
+
|
94
|
+
ENV.delete('LEVEL_ONE_1_LEVEL_TWO_1')
|
95
|
+
ENV.delete('LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_1')
|
96
|
+
ENV.delete('LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_2')
|
97
|
+
ENV.delete('LEVEL_ONE_1_LEVEL_TWO_3')
|
98
|
+
ENV.delete('LEVEL_ONE_1_LEVEL_ONE_2')
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'can convert simple boolean text values to their Boolean equivalents' do
|
102
|
+
source_hash = {
|
103
|
+
true_text: 'true',
|
104
|
+
true_single: 't',
|
105
|
+
true_yes: 'yes',
|
106
|
+
false_text: 'false',
|
107
|
+
false_single: 'f',
|
108
|
+
false_no: 'no',
|
109
|
+
}
|
110
|
+
|
111
|
+
environment_hash = SystemEnvironment.inject_into(source_hash)
|
112
|
+
|
113
|
+
expect(environment_hash['true_text']).to be_a TrueClass
|
114
|
+
expect(environment_hash['true_single']).to be_a TrueClass
|
115
|
+
expect(environment_hash['true_yes']).to be_a TrueClass
|
116
|
+
expect(environment_hash['false_text']).to be_a FalseClass
|
117
|
+
expect(environment_hash['false_single']).to be_a FalseClass
|
118
|
+
expect(environment_hash['false_no']).to be_a FalseClass
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/spec/lib/chamber_spec.rb
CHANGED
@@ -1,250 +1,286 @@
|
|
1
|
-
require '
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'chamber'
|
3
|
+
require 'fileutils'
|
2
4
|
|
3
|
-
|
5
|
+
FileUtils.mkdir_p '/tmp/chamber/settings' unless File.exist? '/tmp/chamber/settings'
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
+
File.open('/tmp/chamber/settings.yml', 'w+') do |file|
|
8
|
+
file.puts <<-HEREDOC
|
9
|
+
test:
|
10
|
+
my_setting: my_value
|
11
|
+
my_boolean: "false"
|
12
|
+
my_dynamic_setting: <%= 1 + 1 %>
|
13
|
+
my_ftp_url: ftp://<%= Chamber[:test][:my_username] %>:<%= Chamber[:test][:my_password] %>@127.0.0.1
|
14
|
+
another_level:
|
15
|
+
setting_one: 1
|
16
|
+
setting_two: 2
|
17
|
+
level_three:
|
18
|
+
an_array:
|
19
|
+
- item 1
|
20
|
+
- item 2
|
21
|
+
- item 3
|
22
|
+
a_scalar: 'hello'
|
23
|
+
HEREDOC
|
24
|
+
end
|
25
|
+
|
26
|
+
File.open('/tmp/chamber/credentials.yml', 'w+') do |file|
|
27
|
+
file.puts <<-HEREDOC
|
28
|
+
test:
|
29
|
+
my_username: username
|
30
|
+
my_password: password
|
31
|
+
HEREDOC
|
7
32
|
end
|
8
33
|
|
9
|
-
|
10
|
-
|
11
|
-
Settings.clear!
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '.source' do
|
15
|
-
context 'when an invalid option is specified' do
|
16
|
-
let(:options) do
|
17
|
-
{ foo: 'bar' }
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'raises ChamberInvalidOptionError' do
|
21
|
-
expect { Settings.source('filename', options) }.to raise_error(Chamber::ChamberInvalidOptionError)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'when no options are specified' do
|
26
|
-
it 'does not raise an error' do
|
27
|
-
expect { Settings.source('filename') }.not_to raise_error
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'when valid options are specified' do
|
32
|
-
context 'and options only contains :namespace' do
|
33
|
-
let(:options) do
|
34
|
-
{ namespace: 'bar' }
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'does not raise an error' do
|
38
|
-
expect { Settings.source('filename', options) }.not_to raise_error
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context 'and options only contains :override_from_environment' do
|
43
|
-
let(:options) do
|
44
|
-
{ override_from_environment: 'bar' }
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'does not raise an error' do
|
48
|
-
expect { Settings.source('filename', options) }.not_to raise_error
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context 'and options contains both :namespace and :override_from_environment' do
|
53
|
-
let(:options) do
|
54
|
-
{
|
55
|
-
namespace: 'bar',
|
56
|
-
override_from_environment: 'bar'
|
57
|
-
}
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'does not raise an error' do
|
61
|
-
expect { Settings.source('filename', options) }.not_to raise_error
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe '.load!' do
|
68
|
-
context 'when a non-existent file is specified' do
|
69
|
-
let(:file) { Tempfile.new('test') }
|
70
|
-
let!(:filename) { file.path }
|
71
|
-
|
72
|
-
before do
|
73
|
-
file.close
|
74
|
-
file.unlink
|
75
|
-
expect(File.exists?(filename)).to be_false
|
76
|
-
Settings.source filename
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'does not raise an error' do
|
80
|
-
expect { Settings.load! }.not_to raise_error
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'leaves the instance empty' do
|
84
|
-
Settings.load!
|
85
|
-
expect(Settings.instance).to be_empty
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
context 'when an existing file is specified' do
|
90
|
-
let(:file) { Tempfile.new('test') }
|
91
|
-
let(:filename) { file.path }
|
92
|
-
let(:content) do
|
93
|
-
<<-CONTENT
|
94
|
-
secret:
|
95
|
-
environment: CHAMBER_TEST
|
96
|
-
development:
|
97
|
-
foo: bar dev
|
34
|
+
File.open('/tmp/chamber/settings-blue.yml', 'w+') do |file|
|
35
|
+
file.puts <<-HEREDOC
|
98
36
|
test:
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
end
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
37
|
+
my_other_setting: my_other_value
|
38
|
+
another_level:
|
39
|
+
setting_one: 3
|
40
|
+
other:
|
41
|
+
everything: works
|
42
|
+
HEREDOC
|
43
|
+
end
|
44
|
+
|
45
|
+
File.open('/tmp/chamber/settings/some_settings_file.yml', 'w+') do |file|
|
46
|
+
file.puts <<-HEREDOC
|
47
|
+
blue:
|
48
|
+
my_settings_for_inline_namespace: my_value_for_inline_namespace
|
49
|
+
my_non_inline_namespaced_setting: my_value_for_non_inline_namespace
|
50
|
+
HEREDOC
|
51
|
+
end
|
52
|
+
|
53
|
+
File.open('/tmp/chamber/settings/sub_settings.yml', 'w+') do |file|
|
54
|
+
file.puts <<-HEREDOC
|
55
|
+
sub_settings:
|
56
|
+
my_sub_setting: my_sub_setting_value
|
57
|
+
HEREDOC
|
58
|
+
end
|
59
|
+
|
60
|
+
File.open('/tmp/chamber/settings/sub_settings-blue.yml', 'w+') do |file|
|
61
|
+
file.puts <<-HEREDOC
|
62
|
+
sub_settings:
|
63
|
+
my_namespaced_sub_setting: my_namespaced_sub_setting_value
|
64
|
+
HEREDOC
|
65
|
+
end
|
66
|
+
|
67
|
+
File.open('/tmp/chamber/settings/only_namespaced_settings-blue.yml', 'w+') do |file|
|
68
|
+
file.puts <<-HEREDOC
|
69
|
+
only_namespaced_sub_settings:
|
70
|
+
another_sub_setting: namespaced
|
71
|
+
HEREDOC
|
72
|
+
end
|
73
|
+
|
74
|
+
describe Chamber, :singletons => [Chamber] do
|
75
|
+
before(:each) { Chamber.load(:basepath => '/tmp/chamber') }
|
76
|
+
|
77
|
+
it 'knows how to load itself with a path string' do
|
78
|
+
Chamber.load(:basepath => '/tmp/chamber')
|
79
|
+
|
80
|
+
expect(Chamber.basepath.to_s).to eql '/tmp/chamber'
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'knows how to load itself with a path object' do
|
84
|
+
Chamber.load(:basepath => Pathname.new('/tmp/chamber'))
|
85
|
+
|
86
|
+
expect(Chamber.basepath.to_s).to eql '/tmp/chamber'
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'processes settings files through ERB before YAML' do
|
90
|
+
expect(Chamber[:test][:my_dynamic_setting]).to eql 2
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'can access settings through a hash-like syntax' do
|
94
|
+
expect(Chamber[:test][:my_setting]).to eql 'my_value'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'can access the settings through method-based access' do
|
98
|
+
expect(Chamber.instance.test.my_setting).to eql 'my_value'
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'can access the instance via "env"' do
|
102
|
+
expect(Chamber.instance.test.my_setting).to eql 'my_value'
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'prefers values stored in environment variables over those in the YAML files' do
|
106
|
+
ENV['TEST_MY_SETTING'] = 'some_other_value'
|
107
|
+
ENV['TEST_ANOTHER_LEVEL_LEVEL_THREE_AN_ARRAY'] = 'something'
|
108
|
+
|
109
|
+
Chamber.load(:basepath => '/tmp/chamber')
|
110
|
+
expect(Chamber.instance.test.my_setting).to eql 'some_other_value'
|
111
|
+
expect(Chamber.instance.test.another_level.level_three.an_array).to eql 'something'
|
112
|
+
expect(Chamber.instance.test.my_dynamic_setting).to eql 2
|
113
|
+
|
114
|
+
ENV.delete 'TEST_MY_SETTING'
|
115
|
+
ENV.delete 'TEST_ANOTHER_LEVEL_LEVEL_THREE_AN_ARRAY'
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'can load files based on the namespace passed in' do
|
119
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
120
|
+
:namespaces => {
|
121
|
+
:my_namespace => -> { 'blue' } } )
|
122
|
+
|
123
|
+
expect(Chamber.instance.other.everything).to eql 'works'
|
124
|
+
expect(Chamber.instance.test.my_dynamic_setting).to eql 2
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'loads multiple namespaces if it is called twice' do
|
128
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
129
|
+
:namespaces => {
|
130
|
+
:first_namespace_call => -> { :first },
|
131
|
+
:second_namespace_call => -> { :second }, } )
|
132
|
+
|
133
|
+
expect(Chamber.instance.namespaces.to_a).to eql [:first, :second]
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'does not load the same namespace twice' do
|
137
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
138
|
+
:namespaces => {
|
139
|
+
:first_namespace_call => -> { :first },
|
140
|
+
:first_namespace_call => -> { :first }, } )
|
141
|
+
|
142
|
+
expect(Chamber.instance.namespaces.to_a).to eql [:first]
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'will load settings files which are only namespaced' do
|
146
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
147
|
+
:namespaces => {
|
148
|
+
:my_namespace => -> { 'blue' } } )
|
149
|
+
|
150
|
+
expect(Chamber[:only_namespaced_sub_settings][:another_sub_setting]).to eql 'namespaced'
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'clears all settings each time the settings are loaded' do
|
154
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
155
|
+
:namespaces => {
|
156
|
+
:my_namespace => -> { 'blue' } } )
|
157
|
+
|
158
|
+
expect(Chamber[:only_namespaced_sub_settings][:another_sub_setting]).to eql 'namespaced'
|
159
|
+
|
160
|
+
Chamber.load(:basepath => '/tmp/chamber')
|
161
|
+
|
162
|
+
expect(Chamber[:only_namespaced_sub_settings]).to be_nil
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'still raises an error if you try to send a message which the settings hash does not understand' do
|
166
|
+
expect{ Chamber.instance.i_do_not_know }.to raise_error NoMethodError
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'does not raise an exception if a namespaced file does not exist' do
|
170
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
171
|
+
:namespaces => {
|
172
|
+
:non_existant_namespace => -> { false } } )
|
173
|
+
|
174
|
+
expect { Chamber.load(:basepath => '/tmp/chamber') }.not_to raise_error
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'merges (not overrides) subsequent settings' do
|
178
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
179
|
+
:namespaces => {
|
180
|
+
:my_namespace => -> { 'blue' } } )
|
181
|
+
|
182
|
+
expect(Chamber.instance.test.my_setting).to eql 'my_value'
|
183
|
+
expect(Chamber.instance.test.my_other_setting).to eql 'my_other_value'
|
184
|
+
expect(Chamber.instance.test.another_level.setting_one).to eql 3
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'loads YAML files from the "settings" directory under the base directory if any exist' do
|
188
|
+
expect(Chamber.instance.sub_settings.my_sub_setting).to eql 'my_sub_setting_value'
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'does not load YAML files from the "settings" directory if it is namespaced' do
|
192
|
+
expect(Chamber['sub_settings-namespaced']).to be_nil
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'loads namespaced YAML files in the "settings" directory if they correspond to a value namespace' do
|
196
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
197
|
+
:namespaces => {
|
198
|
+
:my_namespace => -> { 'blue' } } )
|
199
|
+
|
200
|
+
expect(Chamber['sub_settings']['my_namespaced_sub_setting']).to eql 'my_namespaced_sub_setting_value'
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'loads namespaced settings if they are inline in a non-namespaced filename' do
|
204
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
205
|
+
:namespaces => {
|
206
|
+
:my_namespace => -> { 'blue' } } )
|
207
|
+
|
208
|
+
expect(Chamber['my_settings_for_inline_namespace']).to eql 'my_value_for_inline_namespace'
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'does not load non-namespaced data from a file if inline namespaces are found' do
|
212
|
+
Chamber.load( :basepath => '/tmp/chamber',
|
213
|
+
:namespaces => {
|
214
|
+
:my_namespace => -> { 'blue' } } )
|
215
|
+
|
216
|
+
expect(Chamber['my_non_inline_namespaced_setting']).not_to eql 'my_value_for_non_inline_namespace'
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'loads the entire inline namespaced file if no namespaces are passed in since it does not know they are namespaced' do
|
220
|
+
Chamber.load(:basepath => '/tmp/chamber')
|
221
|
+
|
222
|
+
expect(Chamber['blue']['my_settings_for_inline_namespace']).to eql 'my_value_for_inline_namespace'
|
223
|
+
expect(Chamber['my_non_inline_namespaced_setting']).to eql 'my_value_for_non_inline_namespace'
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'can convert the settings to their environment variable versions' do
|
227
|
+
Chamber.load(:basepath => '/tmp/chamber')
|
228
|
+
|
229
|
+
expect(Chamber.to_environment).to eql(
|
230
|
+
'SUB_SETTINGS_MY_SUB_SETTING' => 'my_sub_setting_value',
|
231
|
+
'TEST_ANOTHER_LEVEL_LEVEL_THREE_AN_ARRAY' => '["item 1", "item 2", "item 3"]',
|
232
|
+
'TEST_ANOTHER_LEVEL_LEVEL_THREE_A_SCALAR' => 'hello',
|
233
|
+
'TEST_ANOTHER_LEVEL_SETTING_ONE' => '1',
|
234
|
+
'TEST_ANOTHER_LEVEL_SETTING_TWO' => '2',
|
235
|
+
'TEST_MY_DYNAMIC_SETTING' => '2',
|
236
|
+
'TEST_MY_SETTING' => 'my_value',
|
237
|
+
'TEST_MY_FTP_URL' => 'ftp://username:password@127.0.0.1',
|
238
|
+
'TEST_MY_PASSWORD' => 'password',
|
239
|
+
'TEST_MY_SETTING' => 'my_value',
|
240
|
+
'TEST_MY_USERNAME' => 'username',
|
241
|
+
'TEST_MY_BOOLEAN' => 'false',
|
242
|
+
'BLUE_MY_SETTINGS_FOR_INLINE_NAMESPACE' => 'my_value_for_inline_namespace',
|
243
|
+
'MY_NON_INLINE_NAMESPACED_SETTING' => 'my_value_for_non_inline_namespace',
|
244
|
+
)
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'can convert boolean-like strings to actual booleans' do
|
248
|
+
expect(Chamber[:test][:my_boolean]).to be_a FalseClass
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'can use data from credentials in subsequently loaded files' do
|
252
|
+
expect(Chamber[:test][:my_ftp_url]).to eql 'ftp://username:password@127.0.0.1'
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'can notify properly whether it responds to messages if the underlying settings does' do
|
256
|
+
expect(Chamber.env.respond_to?(:sub_settings)).to be_a TrueClass
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'can explicitly specify files without specifying a basepath' do
|
260
|
+
Chamber.load files: ['/tmp/chamber/credentials.yml']
|
261
|
+
|
262
|
+
expect(Chamber.filenames).to eql ['/tmp/chamber/credentials.yml']
|
263
|
+
expect(Chamber.settings.to_hash).to eql('test' => {
|
264
|
+
'my_username' => 'username',
|
265
|
+
'my_password' => 'password', } )
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'ignores the basepath if file patterns are explicitly passed in' do
|
269
|
+
Chamber.load basepath: '/tmp/chamber',
|
270
|
+
files: 'credentials.yml'
|
271
|
+
|
272
|
+
expect(Chamber.filenames).to be_empty
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'can render itself as a string even if it has not been loaded' do
|
276
|
+
Singleton.__init__(Chamber)
|
277
|
+
|
278
|
+
expect(Chamber.to_s).to eql ''
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'can determine settings even if it has not been loaded' do
|
282
|
+
Singleton.__init__(Chamber)
|
283
|
+
|
284
|
+
expect(Chamber.settings.to_hash).to eql({})
|
249
285
|
end
|
250
286
|
end
|