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,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