really-confy 0.1.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 +7 -0
- data/lib/really_confy.rb +235 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c4a5653e5cc890738cf6afce11f7da262f814e78
|
4
|
+
data.tar.gz: 38f63ff32abbff0de095ebe6335d909ad8d935a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5af1c19f0ca561a5fe9dcd52fe019ad5e2bc5a20611934156f60ccc3b078daf3e8f5af37cc451b85b9d11c5d93e762f1d899c9f1ce012e78b736ee72856baa50
|
7
|
+
data.tar.gz: 99c36e189f874ede4a7226943a7fcd1530cc3235d3b08ac1234b8a81644fdca1af16d4766e7b987c61c7bc31d7b41febad54c3f9cef8560525a77f8a6ba5160c
|
data/lib/really_confy.rb
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'rainbow'
|
3
|
+
require 'active_support/core_ext/hash/deep_merge'
|
4
|
+
|
5
|
+
class ReallyConfy
|
6
|
+
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
# look for config files under this directory
|
9
|
+
config_path: './config',
|
10
|
+
# load and reucrisvely merge the config from these files, in the given order (duplicate keys
|
11
|
+
# in later files will override those in earlier files)
|
12
|
+
config_files: [
|
13
|
+
'config.yml',
|
14
|
+
'config.secret.yml',
|
15
|
+
'config.local.yml'
|
16
|
+
],
|
17
|
+
# load will raise a ConfigError if these files are in the git repo
|
18
|
+
local_config_files: [
|
19
|
+
'config.secret.yml',
|
20
|
+
'config.local.yml'
|
21
|
+
],
|
22
|
+
# load will raise a ConfigError if these files are missing
|
23
|
+
required_config_files: [
|
24
|
+
'config.yml',
|
25
|
+
'config.secret.yml'
|
26
|
+
],
|
27
|
+
# load will print a warning if these files are missing
|
28
|
+
suggested_config_files: [
|
29
|
+
'config.secret.yml'
|
30
|
+
],
|
31
|
+
# the environment key will be selected based on this ENV variable
|
32
|
+
env_var_name: 'CONFY_ENV',
|
33
|
+
# use Symbols instead of Strings for all keys in the config Hash
|
34
|
+
symbol_keys: false,
|
35
|
+
# load will return an ActiveSupport::HashWithIndifferentAccess instead of a Hash
|
36
|
+
indifferent_keys: false,
|
37
|
+
# suppress output to stdout/stderr
|
38
|
+
quiet: false,
|
39
|
+
# enable colorized output; nil means 'auto', which enables color by default unless the
|
40
|
+
# terminal doesn't support it
|
41
|
+
color: nil,
|
42
|
+
# force the ruby interpreter to exit if ReallyConfy encounters an error during load
|
43
|
+
# ... not a good idea to use this with quiet:true unless you know exactly what you're doing
|
44
|
+
exit_on_error: false
|
45
|
+
}
|
46
|
+
|
47
|
+
attr_accessor :config_files
|
48
|
+
attr_accessor :config_path
|
49
|
+
attr_accessor :local_config_files
|
50
|
+
attr_accessor :required_config_files
|
51
|
+
attr_accessor :suggested_config_files
|
52
|
+
attr_accessor :env_var_name
|
53
|
+
attr_accessor :env
|
54
|
+
|
55
|
+
def initialize(opts = {})
|
56
|
+
setup(opts)
|
57
|
+
end
|
58
|
+
|
59
|
+
def setup(opts)
|
60
|
+
read_opts_into_instance_vars(opts, DEFAULT_OPTIONS.keys)
|
61
|
+
@env_var_name = @env_var_name.to_s if @env_var_name # ENV keys are always strings
|
62
|
+
@env = opts[:env] if opts.has_key? :env
|
63
|
+
|
64
|
+
@rainbow = Rainbow.new
|
65
|
+
@rainbow.enabled = @color unless @color.nil?
|
66
|
+
|
67
|
+
ensure_required_config_files_exist
|
68
|
+
check_suggested_config_files_exist
|
69
|
+
ensure_local_config_files_are_not_in_git
|
70
|
+
|
71
|
+
if @symbol_keys && @indifferent_keys
|
72
|
+
fail ArgumentError,
|
73
|
+
":symbol_keys and :indifferent_keys options cannot be used together!"
|
74
|
+
end
|
75
|
+
|
76
|
+
require 'active_support/core_ext/hash/keys' if @symbol_keys
|
77
|
+
require 'active_support/core_ext/hash/indifferent_access' if @indifferent_keys
|
78
|
+
end
|
79
|
+
|
80
|
+
def load
|
81
|
+
existing_config_files =
|
82
|
+
config_files.select{|file| File.exists? full_path_to_config_file(file) }
|
83
|
+
|
84
|
+
print_info "Loading config from #{existing_config_files.inspect} for #{env.inspect}"+
|
85
|
+
" environment..."
|
86
|
+
|
87
|
+
multi_env_configs =
|
88
|
+
existing_config_files.map{|file| load_config_file(file) }
|
89
|
+
|
90
|
+
unless multi_env_configs.any?{|config| config.is_a?(Hash) && config.has_key?(env) }
|
91
|
+
fail ConfigError, "#{env.inspect} is not a valid environment! None of the loaded configs"+
|
92
|
+
" had a top-level #{env.inspect} key. All configurations should be nested under top"+
|
93
|
+
" level keys corresponding to environment names (e.g. 'test', 'development', ...)"
|
94
|
+
end
|
95
|
+
|
96
|
+
env_configs = multi_env_configs.map{|multi_env_config|
|
97
|
+
multi_env_config.fetch('DEFAULTS', {}).deep_merge multi_env_config.fetch(env, {})
|
98
|
+
}
|
99
|
+
merged_config = env_configs.reduce{|merged_config, config| merged_config.deep_merge(config) }
|
100
|
+
|
101
|
+
merged_config['env'] ||= env
|
102
|
+
|
103
|
+
merged_config.deep_symbolize_keys! if @symbol_keys
|
104
|
+
merged_config = merged_config.with_indifferent_access if @indifferent_keys
|
105
|
+
|
106
|
+
merged_config
|
107
|
+
rescue => e
|
108
|
+
header = "!!! Couldn't load config for #{env.inspect} environment! !!!"
|
109
|
+
print_error ""
|
110
|
+
print_error "!"*header.length
|
111
|
+
print_error "#{header}"
|
112
|
+
print_error "!"*header.length
|
113
|
+
print_error ""
|
114
|
+
print_error "#{e}"
|
115
|
+
print_error ""
|
116
|
+
print_error "!"*header.length
|
117
|
+
print_error ""
|
118
|
+
if @exit_on_error
|
119
|
+
print_error "Aborting because the :exit_on_error option is true!"
|
120
|
+
exit 666
|
121
|
+
end
|
122
|
+
raise e
|
123
|
+
end
|
124
|
+
|
125
|
+
def load_config_file(file)
|
126
|
+
full_path = full_path_to_config_file(file)
|
127
|
+
multi_env_config = (YAML.load_file full_path)
|
128
|
+
|
129
|
+
# YAML.load_file will return false if given an empty file to load
|
130
|
+
return {} if multi_env_config == false
|
131
|
+
|
132
|
+
unless multi_env_config.is_a? Hash
|
133
|
+
fail ConfigError, "Config file #{file.inspect} must contain a YAML-encoded Hash, but"+
|
134
|
+
" it seems to contain a #{multi_env_config.class}"
|
135
|
+
end
|
136
|
+
|
137
|
+
multi_env_config
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def full_path_to_config_file(file)
|
143
|
+
File.absolute_path(relative_path_to_config_file(file))
|
144
|
+
end
|
145
|
+
|
146
|
+
def relative_path_to_config_file(file)
|
147
|
+
File.join(config_path,file)
|
148
|
+
end
|
149
|
+
|
150
|
+
def ensure_required_config_files_exist
|
151
|
+
required_config_files.each do |file|
|
152
|
+
full_path = full_path_to_config_file(file)
|
153
|
+
unless File.exists? full_path
|
154
|
+
fail ConfigError, "Required config file #{file.inspect} does not exist under"+
|
155
|
+
" #{full_path.inspect}!"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def check_suggested_config_files_exist
|
161
|
+
(suggested_config_files - required_config_files).each do |file|
|
162
|
+
full_path = full_path_to_config_file(file)
|
163
|
+
unless File.exists? full_path
|
164
|
+
print_warning "WARNING: Config file #{file.inspect} does not exist!"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def ensure_local_config_files_are_not_in_git
|
170
|
+
return unless git_available?
|
171
|
+
return unless we_are_in_a_git_repo?
|
172
|
+
|
173
|
+
local_config_files.each do |file|
|
174
|
+
relative_path = relative_path_to_config_file(file)
|
175
|
+
if file_is_in_git? relative_path
|
176
|
+
fail ConfigError, "Local config file #{relative_path.inspect} exists in the git repo!"
|
177
|
+
" Remove this file from your git repo and add it to your .gitignore"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def git_available?
|
183
|
+
begin
|
184
|
+
`git`
|
185
|
+
true
|
186
|
+
rescue Errno::ENOENT => e
|
187
|
+
false
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def we_are_in_a_git_repo?
|
192
|
+
File.exists? '.git'
|
193
|
+
end
|
194
|
+
|
195
|
+
def file_is_in_git?(file)
|
196
|
+
git_cmd = "git ls-tree HEAD #{file}"
|
197
|
+
git_output = `#{git_cmd}`.strip
|
198
|
+
|
199
|
+
not git_output.empty?
|
200
|
+
end
|
201
|
+
|
202
|
+
def print_error(err)
|
203
|
+
$stderr.puts @rainbow.wrap(err).red.bright
|
204
|
+
end
|
205
|
+
|
206
|
+
def print_warning(warning)
|
207
|
+
$stderr.puts @rainbow.wrap(warning).yellow.bright
|
208
|
+
end
|
209
|
+
|
210
|
+
def print_info(info)
|
211
|
+
$stdout.puts @rainbow.wrap(info).cyan
|
212
|
+
end
|
213
|
+
|
214
|
+
def env
|
215
|
+
if @env
|
216
|
+
return @env
|
217
|
+
elsif ENV.has_key? env_var_name
|
218
|
+
ENV.fetch(env_var_name)
|
219
|
+
else
|
220
|
+
fail ConfigError, "Configuration environment couldn't be determined --"+
|
221
|
+
" ENV[#{env_var_name.inspect}] is not set! Try running with"+
|
222
|
+
" `#{env_var_name.inspect}=yourenvname ...`"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def read_opts_into_instance_vars(opts, instance_var_keys)
|
227
|
+
instance_var_keys.each do |key|
|
228
|
+
instance_variable_set(:"@#{key}", opts.fetch(key, ReallyConfy::DEFAULT_OPTIONS.fetch(key)))
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class ConfigError < StandardError
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: really-confy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Zukowski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rainbow
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
description:
|
42
|
+
email: mzukowski@adknowledge.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- lib/really_confy.rb
|
48
|
+
homepage:
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
metadata: {}
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 2.4.8
|
69
|
+
signing_key:
|
70
|
+
specification_version: 4
|
71
|
+
summary: Simple YAML configuration loader
|
72
|
+
test_files: []
|
73
|
+
has_rdoc:
|