chamber 0.0.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,250 +1,286 @@
1
- require 'spec_helper'
1
+ require 'rspectacular'
2
+ require 'chamber'
3
+ require 'fileutils'
2
4
 
3
- require 'tempfile'
5
+ FileUtils.mkdir_p '/tmp/chamber/settings' unless File.exist? '/tmp/chamber/settings'
4
6
 
5
- class Settings
6
- extend Chamber
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
- describe Chamber do
10
- before do
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
- foo: bar test
100
- CONTENT
101
- end
102
-
103
- before do
104
- file.write(content)
105
- file.close
106
- end
107
-
108
- after do
109
- file.unlink
110
- end
111
-
112
- context 'and no options are specified' do
113
- before { Settings.source(filename) }
114
-
115
- let(:expected) do
116
- {
117
- 'secret' => {
118
- 'environment' => 'CHAMBER_TEST'
119
- },
120
- 'development' => {
121
- 'foo' => 'bar dev'
122
- },
123
- 'test' => {
124
- 'foo' => 'bar test'
125
- }
126
- }
127
- end
128
-
129
- it 'loads all settings' do
130
- Settings.load!
131
- expect(Settings.instance.to_hash).to eq expected
132
- end
133
-
134
- it 'provides access to all settings without the instance root' do
135
- Settings.load!
136
- expect(Settings.to_hash).to eq expected
137
- end
138
- end
139
-
140
- context 'and the :namespace option is specified' do
141
- before { Settings.source(filename, namespace: namespace) }
142
-
143
- context 'and it is valid' do
144
- let(:namespace) { 'development' }
145
- let(:expected) do
146
- {
147
- 'foo' => 'bar dev'
148
- }
149
- end
150
-
151
- it 'loads settings for the specified namespace' do
152
- Settings.load!
153
- expect(Settings.instance.to_hash).to eq expected
154
- end
155
-
156
- it 'provides access to all settings without the instance root' do
157
- Settings.load!
158
- expect(Settings.to_hash).to eq expected
159
- end
160
- end
161
-
162
- context 'and it is not valid' do
163
- let(:namespace) { 'staging' }
164
-
165
- it 'raises a KeyError' do
166
- expect { Settings.load! }.to raise_error(KeyError)
167
- end
168
- end
169
- end
170
-
171
- context 'and the :override_from_environment option is specified' do
172
- before { Settings.source(filename, override_from_environment: true) }
173
-
174
- context 'and the environment variable is present' do
175
- before { ENV['CHAMBER_TEST'] = 'value' }
176
-
177
- it 'overrides the settings from the environment' do
178
- Settings.load!
179
- expect(Settings.instance.secret).to eq 'value'
180
- end
181
-
182
- it 'provides access to all settings without the instance root' do
183
- Settings.load!
184
- expect(Settings.secret).to eq 'value'
185
- end
186
- end
187
-
188
- context 'and the environment variable is not present' do
189
- before { ENV.delete('CHAMBER_TEST') }
190
-
191
- it 'sets the value to nil' do
192
- Settings.load!
193
- expect(Settings.instance.secret).to be_nil
194
- end
195
-
196
- it 'provides acccess to all settings without the instance root' do
197
- Settings.load!
198
- expect(Settings.secret).to be_nil
199
- end
200
- end
201
- end
202
- end
203
- end
204
-
205
- describe '.reload!' do
206
- context 'when a filename is changed after it is sourced and loaded' do
207
- let(:file) { Tempfile.new('test') }
208
- let!(:filename) { file.path }
209
- let(:content) do
210
- <<-CONTENT
211
- initial: value
212
- CONTENT
213
- end
214
- let(:modified) do
215
- <<-MODIFIED
216
- modified: changed
217
- MODIFIED
218
- end
219
-
220
- before do
221
- file.write(content)
222
- file.close
223
- Settings.source(filename)
224
- Settings.load!
225
- end
226
-
227
- after do
228
- file.unlink
229
- end
230
-
231
- it 'reloads the settings' do
232
- File.open(filename, 'w') { |writer| writer.write(modified) }
233
-
234
- expect { Settings.reload! }.to change { Settings.instance.to_hash }.from({ 'initial' => 'value' }).to({ 'modified' => 'changed' })
235
- end
236
- end
237
- end
238
-
239
- describe '.instance' do
240
- it 'is a Hashie::Mash' do
241
- expect(Settings.instance).to be_a(Hashie::Mash)
242
- end
243
- end
244
-
245
- describe '.env' do
246
- it 'is aliased to :instance' do
247
- expect(Settings.method(:env)).to eq Settings.method(:instance)
248
- end
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