chalk-config 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,3 @@
1
+ --markup markdown
2
+ --output yardoc/
3
+ -M redcarpet
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # Execute bundler hook if present
2
+ ['~/.', '/etc/'].any? do |file|
3
+ File.lstat(path = File.expand_path(file + 'bundle-gemfile-hook')) rescue next
4
+ eval(File.read(path), binding, path); break true
5
+ end || source('https://rubygems.org/')
6
+
7
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Stripe
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,175 @@
1
+ # Chalk::Config
2
+
3
+ Maps on-disk config files into a loaded global
4
+ [configatron](https://github.com/markbates/configatron) instance,
5
+ taking into account your current environment.
6
+
7
+ `configatron` is used within many Chalk gems to control their
8
+ behavior, and is also great for configuration within your application.
9
+
10
+ ## Usage
11
+
12
+ ### Environment
13
+
14
+ `Chalk::Config` relies on describing your environment as an opaque
15
+ string (`production`, `qa`, etc). You can set it like so:
16
+
17
+ ```ruby
18
+ Chalk::Config.environment = 'production'
19
+ ```
20
+
21
+ At that point, the global `configatron` will be cleared and all
22
+ registered config reapplied, meaning you don't have to worry about
23
+ setting the environment prior to registering files.
24
+
25
+ `environment` defaults to the value `'default'`.
26
+
27
+ ### Registering config files
28
+
29
+ Additional configuration files are registered using
30
+ {Chalk::Config.register}. The most basic usage looks like
31
+
32
+ ```ruby
33
+ Chalk::Config.register('/path/to/file')
34
+ ```
35
+
36
+ to register a YAML configuration file. You must provide an absolute
37
+ path, in order to ensure you're not relying on the present working
38
+ directory. The following is a pretty good idiom:
39
+
40
+ ```ruby
41
+ Chalk::Config.register(File.expand_path('../config.yaml', __FILE__))
42
+ ```
43
+
44
+ By default, YAML configuration files should have a top-level key for
45
+ each environment.
46
+
47
+ A good convention is to have most configuration in a dummy `default`
48
+ environment and use YAML's native merging to keep your file somewhat
49
+ DRY (WARNING: there exists at least one gem which changes Ruby's YAML
50
+ parser in the presence of multiple merge operators on a single key, so
51
+ be wary of two `<<` calls at once.) However, it's also fine to repeat
52
+ yourself to make the file more human readable.
53
+
54
+ ```yaml
55
+ # /path/to/config.yaml
56
+
57
+ default: &default
58
+ my_feature:
59
+ enable: true
60
+ shards: 2
61
+
62
+ my_service:
63
+ host: localhost
64
+ port: 2800
65
+
66
+ production:
67
+ <<: *default
68
+ my_service:
69
+ host: appserver1
70
+ port: 2800
71
+
72
+ send_emails: true
73
+
74
+ development:
75
+ <<: *default
76
+ send_emails: false
77
+ ```
78
+
79
+ The configuration from the currently active environment will then be
80
+ added to the global `configatron` when you register the file:
81
+
82
+ ```ruby
83
+ Chalk::Config.register('/path/to/config.yaml')
84
+
85
+ Chalk::Config.environment = 'production'
86
+ configatron.my_service.host
87
+ #=> 'appserver1'
88
+
89
+ Chalk::Config.environment = 'development'
90
+ configatron.my_service.host
91
+ #=> 'localhost'
92
+ ```
93
+
94
+ Keys present in multiple files will be deep merged:
95
+
96
+ ```yaml
97
+ # /path/to/site.yaml
98
+
99
+ production:
100
+ my_service:
101
+ host: otherappserver
102
+
103
+ development: {}
104
+ ```
105
+
106
+ ```ruby
107
+ Chalk::Config.register('/path/to/config.yaml')
108
+ Chalk::Config.register('/path/to/site.yaml')
109
+
110
+ Chalk::Config.environment = 'production'
111
+ configatron.my_service.host
112
+ #=> 'otherappserver'
113
+ configatron.my_service.port
114
+ #=> 2800
115
+ ```
116
+
117
+ You can explicitly nest a config file (only a single level of nesting
118
+ is current supported) using the `:nested` option. You can also
119
+ indicate a file has no environment keys and should be applied directly
120
+ via `:raw`:
121
+
122
+
123
+ ```yaml
124
+ # /path/to/cookies.yaml
125
+
126
+ tasty: yes
127
+ ```
128
+
129
+ ```ruby
130
+ Chalk::Config.register('/path/to/cookies.yaml', nested: 'cookies', raw: true)
131
+ configatron.cookies.tasty
132
+ #=> 'yes'
133
+ ```
134
+
135
+ ## Best practices
136
+
137
+ ### Config keys for everything
138
+
139
+ Writing code that switches off the environment is usually indicative
140
+ of the antipattern:
141
+
142
+ ```ruby
143
+ # BAD!
144
+ if Chalk::Config.environment == 'production'
145
+ email.send!
146
+ else
147
+ puts email
148
+ end
149
+ ```
150
+
151
+ Instead, you should create a fresh config key for all of these cases:
152
+
153
+ ```ruby
154
+ if configatron.send_emails
155
+ email.send!
156
+ else
157
+ puts email
158
+ end
159
+ ```
160
+
161
+ This means your code doesn't need to know anything about the set of
162
+ environment names, making adding a new environment easy. As well, it's
163
+ much easier to have fine-grained control and visibility over exactly
164
+ how your application behaves.
165
+
166
+ It's totally fine (and expected) to have many config keys that are
167
+ used only once.
168
+
169
+ # Contributors
170
+
171
+ - Greg Brockman
172
+ - Evan Broder
173
+ - Michelle Bu
174
+ - Nelson Elhage
175
+ - Jeremy Hoon
@@ -0,0 +1,13 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'bundler/setup'
3
+ require 'chalk-rake/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs = ['lib']
8
+ # t.warning = true
9
+ t.verbose = true
10
+ t.test_files = FileList['test/**/*.rb'].reject do |file|
11
+ file.end_with?('_lib.rb') || file.include?('/_lib/')
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chalk-config/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'chalk-config'
8
+ gem.version = Chalk::Config::VERSION
9
+ gem.authors = ['Stripe']
10
+ gem.email = ['oss@stripe.com']
11
+ gem.description = %q{Layer over configatron with conventions for environment-based and site-specific config}
12
+ gem.summary = %q{chalk-configatron uses a config_schema.yaml file to figure out how to configure your app}
13
+ gem.homepage = 'https://github.com/stripe/chalk-config'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+ gem.add_dependency 'configatron', '~> 4.4'
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'minitest'
22
+ gem.add_development_dependency 'mocha'
23
+ gem.add_development_dependency 'chalk-rake'
24
+ end
@@ -0,0 +1,8 @@
1
+ Chalk::Config.environment = 'local'
2
+
3
+ Chalk::Config.register('config.yaml')
4
+ Chalk::Config.register('secrets.yaml')
5
+
6
+ Chalk::Config.register('ops/ops-secrets.yaml', nested: 'ops')
7
+
8
+ Chalk::Config.environment = 'production'
@@ -0,0 +1,317 @@
1
+ require 'set'
2
+ require 'yaml'
3
+ require 'chalk-config/version'
4
+ require 'chalk-config/errors'
5
+
6
+ # We don't necessarily need to keep this, but it's preferable to make
7
+ # sure no one defines any keys outside of Chalk::Config
8
+ if defined?(configatron)
9
+ raise "Someone already loaded 'configatron'. You should let chalk-config load it itself."
10
+ end
11
+
12
+ require 'configatron'
13
+ configatron.lock!
14
+
15
+ # The main class powering Chalk's configuration.
16
+ #
17
+ # This is written using a wrapped Singleton, which makes testing
18
+ # possible (just stub `Chalk::Config.instance` to return a fresh
19
+ # instance) and helps hide implementation.
20
+ class Chalk::Config
21
+ include Singleton
22
+
23
+ # Sets the current environment. All configuration is then reapplied
24
+ # in the order it was {.register}ed. This means you don't have to
25
+ # worry about setting your environment prior to registering config
26
+ # files.
27
+ #
28
+ # @return [String] The current environment.
29
+ def self.environment=(name)
30
+ instance.send(:environment=, name)
31
+ end
32
+
33
+ # You should generally not take any action directly off this
34
+ # value. All codepath switches should be triggered off configuration
35
+ # keys, possibly with environment assertions to ensure safety.
36
+ #
37
+ # @return [String] The current environment (default: `'default'`)
38
+ def self.environment
39
+ instance.send(:environment)
40
+ end
41
+
42
+ # Specify the list of environments every configuration file must
43
+ # include.
44
+ #
45
+ # It's generally recommended to set this in a wrapper library, and
46
+ # use that wrapper library in all your projects. This way you can be
47
+ # defensive, and have certainty no config file slips through without
48
+ # the requisite environment keys.
49
+ #
50
+ # @param environments [Enumerable<String>] The list of required environments.
51
+ def self.required_environments=(environments)
52
+ instance.send(:required_environments=, environments)
53
+ end
54
+
55
+ # Access the environments registered by {.required_environments=}.
56
+ #
57
+ # @return [Enumerable] The registered environments list (by default, nil)
58
+ def self.required_environments
59
+ instance.send(:required_environments)
60
+ end
61
+
62
+ # Register a given YAML file to be included in the global
63
+ # configuration.
64
+ #
65
+ # The config will be loaded once (cached in memory) and be
66
+ # immediately deep-merged onto configatron. If you later run
67
+ # {.environment=}, then all registered configs will be reapplied in
68
+ # the order they were loaded.
69
+ #
70
+ # So for example, running
71
+ # `Chalk::Config.register('/path/to/config.yaml')` for a file with
72
+ # contents:
73
+ #
74
+ # ```yaml
75
+ # env1:
76
+ # key1: value1
77
+ # key2: value2
78
+ # ```
79
+ #
80
+ # would yield `configatron.env1.key1 == value1`,
81
+ # `configatron.env1.key2 == value2`. Later registering a file with
82
+ # contents:
83
+ #
84
+ # ```yaml
85
+ # env1:
86
+ # key1: value3
87
+ # ```
88
+ #
89
+ # would yield `configatron.env1.key1 == value3`,
90
+ # `configatron.env1.key2 == value2`.
91
+ #
92
+ # @param filepath [String] Absolute path to the config file
93
+ # @option options [Boolean] :optional If true, it's fine for the
94
+ # file to be missing, in which case this registration is
95
+ # discarded.
96
+ # @option options [Boolean] :raw If true, the file doesn't have
97
+ # environment keys and should be splatted onto configatron
98
+ # directly. Otherwise, grab just the config under the appropriate
99
+ # environment key.
100
+ # @option options [String] :nested What key to namespace all of
101
+ # this configuration under. (So `nested: 'foo'` would result in
102
+ # configuration available under `configatron.foo.*`.)
103
+ def self.register(filepath, options={})
104
+ unless filepath.start_with?('/')
105
+ raise ArgumentError.new("Register only accepts absolute paths, not #{filepath.inspect}. (This ensures that config is always correctly loaded rather than depending on your current directory. To avoid this error in the future, you may want to use a wrapper that expands paths based on a base directory.)")
106
+ end
107
+ instance.send(:register, filepath, options)
108
+ end
109
+
110
+ # Register a given raw hash to be included in the global
111
+ # configuration.
112
+ #
113
+ # This allows you to specify arbitrary configuration at
114
+ # runtime. It's generally not recommended that you use this method
115
+ # unless your configuration really can't be encoded in config
116
+ # files. A common example is configuration from environment
117
+ # variables (which might be something like the name of your
118
+ # service).
119
+ #
120
+ # Like {.register}, if you later run {.environment=}, this
121
+ # configuration will be reapplied in the order it was registered.
122
+ #
123
+ # @param config [Hash] The raw configuration to be deep-merged into configatron.
124
+ def self.register_raw(config)
125
+ instance.send(:register_raw, config)
126
+ end
127
+
128
+ # Raises if the current environment is not one of the whitelisted
129
+ # environments provided.
130
+ #
131
+ # Generally useful if you have a dev-only codepath you want to be
132
+ # *sure* never activates in production.
133
+ def self.assert_environment(environments)
134
+ environments = [environments] if environments.kind_of?(String)
135
+ return if environments.include?(environment)
136
+
137
+ raise DisallowedEnvironment.new("Current environment #{environment.inspect} is not one of the allowed environments #{environments.inspect}")
138
+ end
139
+
140
+ # Raises if the current environment is one of the blacklisted
141
+ # environments provided.
142
+ #
143
+ # Generally useful if you have a dev-only codepath you want to be
144
+ # *sure* never activates in production.
145
+ def self.assert_not_environment(environments)
146
+ environments = [environments] if environments.kind_of?(String)
147
+ return unless environments.include?(environment)
148
+
149
+ raise DisallowedEnvironment.new("Current environment #{environment.inspect} is one of the disallowed environments #{environments.inspect}")
150
+ end
151
+
152
+ private
153
+
154
+ def initialize
155
+ # List of registered configs, in the form:
156
+ #
157
+ # file => {config: ..., options: ...}
158
+ @registrations = []
159
+ @registered_files = Set.new
160
+ @environment = 'default'
161
+ end
162
+
163
+ ## The actual instance implementations
164
+
165
+ # Possibly reconfigure if the environment changes.
166
+ def environment=(name)
167
+ @environment = name
168
+ reapply_config
169
+ end
170
+
171
+ def environment
172
+ @environment
173
+ end
174
+
175
+ def required_environments=(environments)
176
+ @required_environments = environments
177
+ @registrations.each do |registration|
178
+ # Validate all existing config
179
+ validate_config(registration)
180
+ end
181
+ end
182
+
183
+ def required_environments
184
+ @required_environments
185
+ end
186
+
187
+ def register(filepath, options)
188
+ if @registered_files.include?(filepath)
189
+ raise Error.new("You've already registered #{filepath}.")
190
+ end
191
+ @registered_files << filepath
192
+
193
+ begin
194
+ config = load!(filepath)
195
+ rescue Errno::ENOENT
196
+ return if options[:optional]
197
+ raise
198
+ end
199
+
200
+ register_parsed(config, filepath, options)
201
+ end
202
+
203
+ def register_raw(config)
204
+ register_parsed(config, nil, {})
205
+ end
206
+
207
+ private
208
+
209
+ # Register some raw config
210
+ def register_parsed(config, filepath, options)
211
+ allow_configatron_changes do
212
+ directive = {
213
+ config: config,
214
+ filepath: filepath,
215
+ options: options,
216
+ }
217
+
218
+ validate_config(directive)
219
+ @registrations << directive
220
+
221
+ allow_configatron_changes do
222
+ mixin_config(directive)
223
+ end
224
+ end
225
+ end
226
+
227
+ def allow_configatron_changes(&blk)
228
+ configatron.unlock!
229
+
230
+ begin
231
+ blk.call
232
+ ensure
233
+ configatron.lock!
234
+ end
235
+ end
236
+
237
+ def load!(filepath)
238
+ begin
239
+ loaded = YAML.load_file(filepath)
240
+ rescue Psych::BadAlias => e
241
+ # YAML parse-time errors include the filepath already, but
242
+ # load-time errors do not.
243
+ #
244
+ # Specifically, `Psych::BadAlias` (raised by doing something
245
+ # like `YAML.load('foo: *bar')`) does not:
246
+ # https://github.com/tenderlove/psych/issues/192
247
+ e.message << " (while loading #{filepath})"
248
+ raise
249
+ end
250
+ unless loaded.is_a?(Hash)
251
+ raise Error.new("YAML.load(#{filepath.inspect}) parses into a #{loaded.class}, not a Hash")
252
+ end
253
+ loaded
254
+ end
255
+
256
+ # Take a hash and mix in the environment-appropriate key to an
257
+ # existing configatron object.
258
+ def mixin_config(directive)
259
+ raw = directive[:options][:raw]
260
+
261
+ config = directive[:config]
262
+ filepath = directive[:filepath]
263
+
264
+ if !raw && filepath && config && !config.include?(environment)
265
+ # Directive is derived from a file (i.e. not runtime_config)
266
+ # with environments and that file existed, but is missing the
267
+ # environment.
268
+ raise MissingEnvironment.new("Current environment #{environment.inspect} not defined in config file #{directive[:filepath].inspect}. (HINT: you should have a YAML key of #{environment.inspect}. You may want to inherit a default via YAML's `<<` operator.)")
269
+ end
270
+
271
+ if raw
272
+ choice = config
273
+ elsif filepath && config
274
+ # Derived from file, and file present
275
+ choice = config.fetch(environment)
276
+ elsif filepath
277
+ # Derived from file, but file missing
278
+ choice = {}
279
+ else
280
+ # Manually specified runtime_config
281
+ choice = config
282
+ end
283
+
284
+ subconfigatron = configatron
285
+ if nested = directive[:options][:nested]
286
+ nested.split('.').each do |key|
287
+ subconfigatron = subconfigatron[key]
288
+ end
289
+ end
290
+
291
+ subconfigatron.configure_from_hash(choice)
292
+ end
293
+
294
+ def validate_config(directive)
295
+ (@required_environments || []).each do |environment|
296
+ raw = directive[:options][:raw]
297
+
298
+ config = directive[:config]
299
+ filepath = directive[:filepath]
300
+
301
+ next if raw
302
+
303
+ if filepath && config && !config.include?(environment)
304
+ raise MissingEnvironment.new("Required environment #{environment.inspect} not defined in config file #{directive[:filepath].inspect}. (HINT: you should have a YAML key of #{environment.inspect}. You may want to inherit a default via YAML's `<<` operator.)")
305
+ end
306
+ end
307
+ end
308
+
309
+ def reapply_config
310
+ allow_configatron_changes do
311
+ configatron.reset!
312
+ @registrations.each do |registration|
313
+ mixin_config(registration)
314
+ end
315
+ end
316
+ end
317
+ end
@@ -0,0 +1,8 @@
1
+ class Chalk::Config
2
+ # Base error class for Chalk::Config
3
+ class Error < StandardError; end
4
+ # Thrown if an environment is missing from a config file.
5
+ class MissingEnvironment < Error; end
6
+ # Thrown from environment assertions.
7
+ class DisallowedEnvironment < Error; end
8
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module Chalk
3
+ class Config
4
+ VERSION = "0.2.1"
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ :tddium:
2
+ :ruby_version: ruby-1.9.3-p194
3
+ :bundler_version: 1.3.5
4
+ :test_pattern:
5
+ - "test/wholesome/[^_]*.rb"
6
+ - "test/unit/[^_]*.rb"
7
+ - "test/integration/[^_]*.rb"
8
+ - "test/functional/[^_]*.rb"
9
+ :boot_hook: >
10
+ /usr/bin/env sh -c "gem install --user-install --no-rdoc --no-ri github_api -v=0.10.1 && gem install --user-install --no-rdoc --no-ri --ignore-dependencies tddium_status_github -v=0.2.0 &&
11
+ echo '$:.concat(Dir.glob(File.join(Gem.user_dir, \"gems\", \"*\", \"lib\"))); require \"tddium_status_github\"' >> Rakefile"
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'minitest/autorun'
5
+ require 'minitest/spec'
6
+ require 'mocha/setup'
7
+
8
+ module Critic
9
+ class Test < ::MiniTest::Spec
10
+ def setup
11
+ # Put any stubs here that you want to apply globally
12
+ end
13
+
14
+ def fresh_chalk_config
15
+ config = Chalk::Config.send(:new)
16
+ Chalk::Config.stubs(instance: config)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path('../../_lib', __FILE__)
2
+
3
+ module Critic::Functional
4
+ module Stubs
5
+ end
6
+
7
+ class Test < Critic::Test
8
+ include Stubs
9
+ end
10
+ end
@@ -0,0 +1,102 @@
1
+ require File.expand_path('../_lib', __FILE__)
2
+ require 'chalk-config'
3
+
4
+ class Critic::Functional::GeneralTest < Critic::Functional::Test
5
+ include Configatron::Integrations::Minitest
6
+
7
+ before do
8
+ fresh_chalk_config
9
+
10
+ Chalk::Config.environment = 'testing'
11
+ Chalk::Config.register(File.expand_path('../general/config.yaml', __FILE__))
12
+ end
13
+
14
+ it 'loads config correctly' do
15
+ assert_equal('value1', configatron.config1)
16
+ assert_equal('value3', configatron.config2)
17
+ end
18
+
19
+ it 'allows switching environments' do
20
+ Chalk::Config.environment = 'default'
21
+ assert_equal('value1', configatron.config1)
22
+ assert_equal('value2', configatron.config2)
23
+ end
24
+
25
+ describe 'register_raw' do
26
+ it 'merges in registered config' do
27
+ Chalk::Config.register_raw(foo: 'bar', config1: 'hi')
28
+ assert_equal('bar', configatron.foo)
29
+ assert_equal('hi', configatron.config1)
30
+ end
31
+
32
+ it 'environment validation continues to succeed' do
33
+ Chalk::Config.register_raw(foo: 'bar')
34
+ Chalk::Config.required_environments = ['default']
35
+ end
36
+ end
37
+
38
+ describe 'raw files' do
39
+ it 'merges the file contents directly' do
40
+ Chalk::Config.register(File.expand_path('../general/raw.yaml', __FILE__),
41
+ raw: true)
42
+ assert_equal('there', configatron.hi)
43
+ assert_equal('bat', configatron.baz)
44
+ assert_equal('no_environment', configatron.config1)
45
+ end
46
+
47
+ it 'does not try to validate presence of environments' do
48
+ Chalk::Config.required_environments = ['default']
49
+ Chalk::Config.register(File.expand_path('../general/raw.yaml', __FILE__),
50
+ raw: true)
51
+ end
52
+ end
53
+
54
+ describe 'missing nested files' do
55
+ it 'does not create the relevant config key' do
56
+ Chalk::Config.register(File.expand_path('../general/nonexistent.yaml', __FILE__),
57
+ optional: true,
58
+ nested: 'nonexistent')
59
+ assert_raises(Configatron::UndefinedKeyError) do
60
+ configatron.nonexistent
61
+ end
62
+ end
63
+
64
+ it 'environment validation continues to succeed' do
65
+ Chalk::Config.register(File.expand_path('../general/nonexistent.yaml', __FILE__),
66
+ optional: true,
67
+ nested: 'nonexistent')
68
+ Chalk::Config.required_environments = ['default']
69
+ end
70
+ end
71
+
72
+ describe 'nested config files' do
73
+ it 'nests' do
74
+ Chalk::Config.register(File.expand_path('../general/raw.yaml', __FILE__),
75
+ nested: 'nested',
76
+ raw: true)
77
+ assert_equal('there', configatron.nested.hi)
78
+ end
79
+
80
+ it 'deep-nests' do
81
+ Chalk::Config.register(File.expand_path('../general/raw.yaml', __FILE__),
82
+ nested: 'nested.deeply',
83
+ raw: true)
84
+ assert_equal('there', configatron.nested.deeply.hi)
85
+ end
86
+ end
87
+
88
+ describe 'required_environments' do
89
+ it 'raises if an existing config is missing an environment' do
90
+ assert_raises(Chalk::Config::MissingEnvironment) do
91
+ Chalk::Config.required_environments = ['missing']
92
+ end
93
+ end
94
+
95
+ it 'raises if a new config is missing an environment' do
96
+ Chalk::Config.required_environments = ['testing']
97
+ assert_raises(Chalk::Config::MissingEnvironment) do
98
+ Chalk::Config.register(File.expand_path('../general/missing.yaml', __FILE__))
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,7 @@
1
+ default: &default
2
+ config1: value1
3
+ config2: value2
4
+
5
+ testing:
6
+ <<: *default
7
+ config2: value3
@@ -0,0 +1,2 @@
1
+ default:
2
+ hi: there
@@ -0,0 +1,3 @@
1
+ hi: there
2
+ baz: bat
3
+ config1: no_environment
@@ -0,0 +1,10 @@
1
+ require File.expand_path('../../_lib', __FILE__)
2
+
3
+ module Critic::Integration
4
+ module Stubs
5
+ end
6
+
7
+ class Test < Critic::Test
8
+ include Stubs
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path('../../_lib', __FILE__)
2
+
3
+ module Critic::Meta
4
+ module Stubs
5
+ end
6
+
7
+ class Test < Critic::Test
8
+ include Stubs
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path('../../_lib', __FILE__)
2
+
3
+ module Critic::Unit
4
+ module Stubs
5
+ end
6
+
7
+ class Test < Critic::Test
8
+ include Stubs
9
+ end
10
+ end
@@ -0,0 +1,62 @@
1
+ require File.expand_path('../_lib', __FILE__)
2
+ require 'chalk-config'
3
+
4
+ class Critic::Unit::ChalkConfig < Critic::Unit::Test
5
+ include Configatron::Integrations::Minitest
6
+
7
+ before do
8
+ fresh_chalk_config
9
+ end
10
+
11
+ describe '.assert_environment' do
12
+ it 'raises if the environment is not in the array' do
13
+ Chalk::Config.environment = 'hello'
14
+ assert_raises(Chalk::Config::DisallowedEnvironment) do
15
+ Chalk::Config.assert_environment(%w{foo bar})
16
+ end
17
+ end
18
+
19
+ it 'raises if the environment is not the string' do
20
+ Chalk::Config.environment = 'hello'
21
+ assert_raises(Chalk::Config::DisallowedEnvironment) do
22
+ Chalk::Config.assert_environment('foo')
23
+ end
24
+ end
25
+
26
+ it 'does not raise if the environment is in the array' do
27
+ Chalk::Config.environment = 'hello'
28
+ Chalk::Config.assert_environment(%w{hello there})
29
+ end
30
+
31
+ it 'does not raise if the environment is the string' do
32
+ Chalk::Config.environment = 'hello'
33
+ Chalk::Config.assert_environment('hello')
34
+ end
35
+ end
36
+
37
+ describe '.assert_not_environment' do
38
+ it 'does not raise if the environment is not in the array' do
39
+ Chalk::Config.environment = 'hello'
40
+ Chalk::Config.assert_not_environment(%w{foo bar})
41
+ end
42
+
43
+ it 'does not raise if the environment is not the string' do
44
+ Chalk::Config.environment = 'hello'
45
+ Chalk::Config.assert_not_environment('foo')
46
+ end
47
+
48
+ it 'raises if the environment is in the array' do
49
+ Chalk::Config.environment = 'hello'
50
+ assert_raises(Chalk::Config::DisallowedEnvironment) do
51
+ Chalk::Config.assert_not_environment(%w{hello there})
52
+ end
53
+ end
54
+
55
+ it 'raises if the environment is the string' do
56
+ Chalk::Config.environment = 'hello'
57
+ assert_raises(Chalk::Config::DisallowedEnvironment) do
58
+ Chalk::Config.assert_not_environment('hello')
59
+ end
60
+ end
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chalk-config
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stripe
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-12-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: configatron
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '4.4'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '4.4'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: minitest
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: mocha
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: chalk-rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Layer over configatron with conventions for environment-based and site-specific
95
+ config
96
+ email:
97
+ - oss@stripe.com
98
+ executables: []
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - .gitignore
103
+ - .yardopts
104
+ - Gemfile
105
+ - LICENSE.txt
106
+ - README.md
107
+ - Rakefile
108
+ - chalk-config.gemspec
109
+ - demo_schema.rb
110
+ - lib/chalk-config.rb
111
+ - lib/chalk-config/errors.rb
112
+ - lib/chalk-config/version.rb
113
+ - tddium.yml
114
+ - test/_lib.rb
115
+ - test/functional/_lib.rb
116
+ - test/functional/general.rb
117
+ - test/functional/general/config.yaml
118
+ - test/functional/general/missing.yaml
119
+ - test/functional/general/raw.yaml
120
+ - test/integration/_lib.rb
121
+ - test/meta/_lib.rb
122
+ - test/unit/_lib.rb
123
+ - test/unit/chalk-config.rb
124
+ homepage: https://github.com/stripe/chalk-config
125
+ licenses: []
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ segments:
137
+ - 0
138
+ hash: 2093141279955038068
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ none: false
141
+ requirements:
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ segments:
146
+ - 0
147
+ hash: 2093141279955038068
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 1.8.25
151
+ signing_key:
152
+ specification_version: 3
153
+ summary: chalk-configatron uses a config_schema.yaml file to figure out how to configure
154
+ your app
155
+ test_files:
156
+ - test/_lib.rb
157
+ - test/functional/_lib.rb
158
+ - test/functional/general.rb
159
+ - test/functional/general/config.yaml
160
+ - test/functional/general/missing.yaml
161
+ - test/functional/general/raw.yaml
162
+ - test/integration/_lib.rb
163
+ - test/meta/_lib.rb
164
+ - test/unit/_lib.rb
165
+ - test/unit/chalk-config.rb