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,145 @@
1
+ require 'hashie/mash'
2
+
3
+ ###
4
+ # Internal: Gives access to the existing environment for importing/exporting
5
+ # values.
6
+ #
7
+ class Chamber
8
+ module SystemEnvironment
9
+
10
+ ###
11
+ # Internal: Allows the existing environment to be injected into the passed in
12
+ # hash. The hash that is passed in is *not* modified, instead a new hash is
13
+ # returned.
14
+ #
15
+ # Examples:
16
+ #
17
+ # ###
18
+ # # Injects the current environment variables
19
+ # #
20
+ # ENV['LEVEL_ONE_1_LEVEL_TWO_1'] = 'env value 1'
21
+ # ENV['LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_1'] = 'env value 2'
22
+ #
23
+ # SystemEnvironment.inject_into(
24
+ # level_one_1: {
25
+ # level_two_1: 'value 1',
26
+ # level_two_2: {
27
+ # level_three_1: 'value 2' } } )
28
+ #
29
+ # # => {
30
+ # 'level_one_1' => {
31
+ # 'level_two_1' => 'env value 1',
32
+ # 'level_two_2' => {
33
+ # 'level_three_1' => 'env value 2',
34
+ # }
35
+ #
36
+ # ###
37
+ # # Can inject environment variables if said variables are prefixed
38
+ # #
39
+ # ENV['PREFIX_LEVEL_TWO_1'] = 'env value 1'
40
+ # ENV['PREFIX_LEVEL_TWO_2'] = 'env value 2'
41
+ #
42
+ # SystemEnvironment.inject_into({
43
+ # level_two_1: 'value 1',
44
+ # level_two_2: 'value 2'
45
+ # },
46
+ # ['prefix'])
47
+ #
48
+ # # => {
49
+ # 'level_two_1' => 'env value 1',
50
+ # 'level_two_2' => 'env value 2',
51
+ # }
52
+ #
53
+ #
54
+ def self.inject_into(settings = {}, parent_keys = [])
55
+ with_environment(settings, parent_keys,
56
+ ->(key, value, environment_keys) do
57
+ { key => inject_into(value, environment_keys) }
58
+ end,
59
+ ->(key, value, environment_key) do
60
+ { key => convert_value(ENV[environment_key] || value) }
61
+ end)
62
+ end
63
+
64
+ ###
65
+ # Internal: Allows the environment variable-compatible variables to be
66
+ # extracted from a passed in hash.
67
+ #
68
+ # Examples:
69
+ #
70
+ # ###
71
+ # # Extracts the environment variables based on the hash keys
72
+ # #
73
+ # SystemEnvironment.extract_from(
74
+ # level_one_1: {
75
+ # level_two_1: 'value 1',
76
+ # level_two_2: {
77
+ # level_three_1: 'value 2' } } )
78
+ #
79
+ # # => {
80
+ # 'LEVEL_ONE_1_LEVEL_TWO_1' => 'env value 1',
81
+ # 'LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_1' => 'env value 2',
82
+ # }
83
+ #
84
+ # ###
85
+ # # Can extract environment variables if said variables are prefixed
86
+ # #
87
+ # SystemEnvironment.extract_from({
88
+ # level_two_1: 'value 1',
89
+ # level_two_2: 'value 2'
90
+ # },
91
+ # ['prefix'])
92
+ #
93
+ # # => {
94
+ # 'PREFIX_LEVEL_TWO_1' => 'value 1',
95
+ # 'PREFIX_LEVEL_TWO_2' => 'value 2',
96
+ # }
97
+ #
98
+ def self.extract_from(settings, parent_keys = [])
99
+ with_environment(settings, parent_keys,
100
+ ->(key, value, environment_keys) do
101
+ extract_from(value, environment_keys)
102
+ end,
103
+ ->(key, value, environment_key) do
104
+ { environment_key => value.to_s }
105
+ end)
106
+ end
107
+
108
+ private
109
+
110
+ def self.with_environment(settings, parent_keys, hash_block, value_block)
111
+ environment_hash = Hashie::Mash.new
112
+
113
+ settings.each_pair do |key, value|
114
+ environment_keys = parent_keys.dup.push(key)
115
+
116
+ if value.respond_to? :each_pair
117
+ environment_hash.merge!(hash_block.call(key, value, environment_keys))
118
+ else
119
+ environment_key = environment_keys.join('_').upcase
120
+
121
+ environment_hash.merge!(value_block.call(key, value, environment_key))
122
+ end
123
+ end
124
+
125
+ environment_hash
126
+ end
127
+
128
+ def self.convert_value(value)
129
+ return nil if value.nil?
130
+
131
+ if value.is_a? String
132
+ case value
133
+ when 'false', 'f', 'no'
134
+ false
135
+ when 'true', 't', 'yes'
136
+ true
137
+ else
138
+ value
139
+ end
140
+ else
141
+ value
142
+ end
143
+ end
144
+ end
145
+ end
@@ -1,3 +1,3 @@
1
- module Chamber
2
- VERSION = "0.0.4"
1
+ class Chamber
2
+ VERSION = '1.0.0'
3
3
  end
@@ -0,0 +1,212 @@
1
+ require 'rspectacular'
2
+ require 'chamber/file_set'
3
+ require 'fileutils'
4
+
5
+
6
+ class Chamber
7
+ describe FileSet do
8
+ before(:each) { FileUtils.mkdir '/tmp/settings' unless ::File.exist? '/tmp/settings' }
9
+ after(:each) { FileUtils.rm_rf '/tmp/settings' if ::File.exist? '/tmp/settings' }
10
+
11
+ it 'can consider directories containing YAML files' do
12
+ ::File.new('/tmp/settings/some_settings_file.yml', 'w+')
13
+ ::File.new('/tmp/settings/another_settings_file.yml', 'w+')
14
+
15
+ file_set = FileSet.new files: '/tmp/settings'
16
+
17
+ expect(file_set.filenames).to eql [
18
+ '/tmp/settings/another_settings_file.yml',
19
+ '/tmp/settings/some_settings_file.yml',
20
+ ]
21
+
22
+ ::FileUtils.rm_rf('/tmp/settings')
23
+ end
24
+
25
+ it 'can consider globs of files' do
26
+ ::File.new('/tmp/settings/some_settings_file.yml', 'w+')
27
+ ::File.new('/tmp/settings/another_settings_file.yml', 'w+')
28
+
29
+ file_set = FileSet.new files: '/tmp/settings/*.yml'
30
+
31
+ expect(file_set.filenames).to eql [
32
+ '/tmp/settings/another_settings_file.yml',
33
+ '/tmp/settings/some_settings_file.yml',
34
+ ]
35
+
36
+ ::FileUtils.rm_rf('/tmp/settings')
37
+ end
38
+
39
+ it 'can consider namespaced files' do
40
+ ::File.new('/tmp/settings/settings-blue.yml', 'w+')
41
+
42
+ file_set = FileSet.new files: '/tmp/settings/settings-blue.yml',
43
+ namespaces: ['blue']
44
+
45
+ expect(file_set.filenames).to eql [
46
+ '/tmp/settings/settings-blue.yml'
47
+ ]
48
+
49
+ ::FileUtils.rm_f('/tmp/settings/settings-blue.yml')
50
+ end
51
+
52
+ it 'does not consider namespaced files which are not relevant' do
53
+ ::File.new('/tmp/settings/settings-blue.yml', 'w+')
54
+
55
+ file_set = FileSet.new files: '/tmp/settings/settings-blue.yml',
56
+ namespaces: ['green']
57
+
58
+ expect(file_set.filenames).to be_empty
59
+
60
+ ::FileUtils.rm_f('/tmp/settings/settings-blue.yml')
61
+ end
62
+
63
+ it 'removes any duplicate files' do
64
+ ::File.new('/tmp/settings.yml', 'w+')
65
+
66
+ file_set = FileSet.new files: [
67
+ '/tmp/settings.yml',
68
+ '/tmp/settings.yml',
69
+ ]
70
+
71
+ expect(file_set.filenames).to eql [
72
+ '/tmp/settings.yml'
73
+ ]
74
+
75
+ ::FileUtils.rm_f('/tmp/settings.yml')
76
+ end
77
+
78
+ it 'can consider multiple file paths' do
79
+ ::File.new('/tmp/settings.yml', 'w+')
80
+ ::File.new('/tmp/settings/new_file.yml', 'w+')
81
+
82
+ file_set = FileSet.new files: [
83
+ '/tmp/settings.yml',
84
+ '/tmp/settings/*.yml',
85
+ ]
86
+
87
+ expect(file_set.filenames).to eql [
88
+ '/tmp/settings.yml',
89
+ '/tmp/settings/new_file.yml',
90
+ ]
91
+
92
+ ::FileUtils.rm_rf('/tmp/settings*')
93
+ end
94
+
95
+ it 'can consider file paths as Pathnames' do
96
+ ::File.new('/tmp/settings.yml', 'w+')
97
+
98
+ file_set = FileSet.new files: '/tmp/settings.yml'
99
+
100
+ expect(file_set.filenames).to eql [
101
+ '/tmp/settings.yml',
102
+ ]
103
+
104
+ ::FileUtils.rm_rf('/tmp/settings*')
105
+ end
106
+
107
+ it 'can consider multiple namespaced files in the order the namespaces are specified' do
108
+ ::File.new('/tmp/settings/settings-blue.yml', 'w+')
109
+ ::File.new('/tmp/settings/settings-green.yml', 'w+')
110
+
111
+ file_set = FileSet.new files: '/tmp/settings/settings*.yml',
112
+ namespaces: ['blue', 'green']
113
+
114
+ expect(file_set.filenames).to eql [
115
+ '/tmp/settings/settings-blue.yml',
116
+ '/tmp/settings/settings-green.yml',
117
+ ]
118
+
119
+ file_set = FileSet.new files: '/tmp/settings/settings*.yml',
120
+ namespaces: ['green', 'blue']
121
+
122
+ expect(file_set.filenames).to eql [
123
+ '/tmp/settings/settings-green.yml',
124
+ '/tmp/settings/settings-blue.yml',
125
+ ]
126
+
127
+ ::FileUtils.rm_f('/tmp/settings/settings*.yml')
128
+ end
129
+
130
+ it 'considers the generic file prior to any namespaced files' do
131
+ ::File.new('/tmp/settings.yml', 'w+')
132
+ ::File.new('/tmp/settings-blue.yml', 'w+')
133
+
134
+ file_set = FileSet.new files: '/tmp/settings*.yml',
135
+ namespaces: ['blue']
136
+
137
+ expect(file_set.filenames).to eql [
138
+ '/tmp/settings.yml',
139
+ '/tmp/settings-blue.yml',
140
+ ]
141
+
142
+ ::FileUtils.rm_f('/tmp/settings*.yml')
143
+ end
144
+
145
+ it 'considers the generic file prior to any namespaced files' do
146
+ ::File.new('/tmp/settings.yml', 'w+')
147
+ ::File.new('/tmp/settings-blue.yml', 'w+')
148
+
149
+ file_set = FileSet.new files: '/tmp/settings*.yml',
150
+ namespaces: ['blue']
151
+
152
+ expect(file_set.filenames).to eql [
153
+ '/tmp/settings.yml',
154
+ '/tmp/settings-blue.yml',
155
+ ]
156
+
157
+ ::FileUtils.rm_f('/tmp/settings*.yml')
158
+ end
159
+
160
+ it 'considers each glob independently, placing non-namespaced and namespaced versions of the globs files above those in subsequent globs' do
161
+ ::File.new('/tmp/settings/credentials-development.yml', 'w+')
162
+ ::File.new('/tmp/settings/settings.yml', 'w+')
163
+
164
+ file_set = FileSet.new files: ['/tmp/settings/credentials*.yml',
165
+ '/tmp/settings/settings*.yml'],
166
+ namespaces: ['development']
167
+
168
+ expect(file_set.filenames).to eql [
169
+ '/tmp/settings/credentials-development.yml',
170
+ '/tmp/settings/settings.yml',
171
+ ]
172
+
173
+ ::FileUtils.rm_rf('/tmp/settings')
174
+ end
175
+
176
+ it 'can display the filenames which were considered for the settings values' do
177
+ ::File.new('/tmp/settings/some_settings_file.yml', 'w+')
178
+ ::File.new('/tmp/settings/another_settings_file.yml', 'w+')
179
+
180
+ file_set = FileSet.new files: '/tmp/settings/*.yml'
181
+
182
+ expect(file_set.filenames).to eql [
183
+ '/tmp/settings/another_settings_file.yml',
184
+ '/tmp/settings/some_settings_file.yml',
185
+ ]
186
+
187
+ ::FileUtils.rm_rf('/tmp/settings')
188
+ end
189
+
190
+ it "can yield each file's settings when converting" do
191
+ ::File.new('/tmp/settings.yml', 'w+')
192
+
193
+ file_set = FileSet.new files: '/tmp/settings.yml'
194
+
195
+ file_set.to_settings do |settings|
196
+ expect(settings).to eql Settings.new
197
+ end
198
+
199
+ ::FileUtils.rm_f('/tmp/settings.yml')
200
+ end
201
+
202
+ it 'can convert settings without yielding to the block by using an intermediate settings object' do
203
+ ::File.new('/tmp/settings.yml', 'w+')
204
+
205
+ file_set = FileSet.new files: '/tmp/settings.yml'
206
+
207
+ expect(file_set.to_settings).to eql Settings.new
208
+
209
+ ::FileUtils.rm_f('/tmp/settings.yml')
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,94 @@
1
+ require 'rspectacular'
2
+ require 'chamber/file'
3
+ require 'chamber/settings'
4
+ require 'tempfile'
5
+
6
+ def create_tempfile_with_content(content)
7
+ tempfile = Tempfile.new('settings')
8
+ tempfile.puts content
9
+ tempfile.rewind
10
+ tempfile
11
+ end
12
+
13
+ class Chamber
14
+ describe File do
15
+ it 'can convert file contents to settings' do
16
+ tempfile = create_tempfile_with_content %Q({ test: settings })
17
+ settings_file = File.new path: tempfile.path
18
+
19
+ allow(Settings).to receive(:new).
20
+ and_return :settings
21
+
22
+ file_settings = settings_file.to_settings
23
+
24
+ expect(file_settings).to eql :settings
25
+ expect(Settings).to have_received(:new).
26
+ with(settings: {'test' => 'settings'},
27
+ namespaces: {})
28
+ end
29
+
30
+ it 'can convert a file whose contents are empty' do
31
+ tempfile = create_tempfile_with_content ''
32
+ settings_file = File.new path: tempfile.path
33
+
34
+ allow(Settings).to receive(:new).
35
+ and_return :settings
36
+
37
+ file_settings = settings_file.to_settings
38
+
39
+ expect(file_settings).to eql :settings
40
+ expect(Settings).to have_received(:new).
41
+ with(settings: {},
42
+ namespaces: {})
43
+ end
44
+
45
+ it 'throws an error when the file contents are malformed' do
46
+ tempfile = create_tempfile_with_content %Q({ test : )
47
+ settings_file = File.new path: tempfile.path
48
+
49
+ expect { settings_file.to_settings }.to raise_error
50
+ end
51
+
52
+ it 'passes any namespaces through to the settings' do
53
+ tempfile = create_tempfile_with_content %Q({ test: settings })
54
+ settings_file = File.new path: tempfile.path,
55
+ namespaces: {
56
+ environment: :development }
57
+
58
+ allow(Settings).to receive(:new)
59
+
60
+ settings_file.to_settings
61
+
62
+ expect(Settings).to have_received(:new).
63
+ with( settings: {'test' => 'settings'},
64
+ namespaces: {
65
+ environment: :development })
66
+ end
67
+
68
+ it 'can handle files which contain ERB markup' do
69
+ tempfile = create_tempfile_with_content %Q({ test: <%= 1 + 1 %> })
70
+ settings_file = File.new path: tempfile.path
71
+
72
+ allow(Settings).to receive(:new)
73
+
74
+ settings_file.to_settings
75
+ expect(Settings).to have_received(:new).
76
+ with( settings: {'test' => 2},
77
+ namespaces: {} )
78
+ end
79
+
80
+ it 'does not throw an error when attempting to convert a file which does not exist' do
81
+ settings_file = File.new path: 'no/path'
82
+
83
+ allow(Settings).to receive(:new).
84
+ and_return :settings
85
+
86
+ file_settings = settings_file.to_settings
87
+
88
+ expect(file_settings).to eql :settings
89
+ expect(Settings).to have_received(:new).
90
+ with(settings: {},
91
+ namespaces: {})
92
+ end
93
+ end
94
+ end