chalk-config 0.2.1

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