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.
@@ -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