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,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
|
data/lib/chamber/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION =
|
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
|