diff --git a/sinatra-contrib/lib/sinatra/config_file.rb b/sinatra-contrib/lib/sinatra/config_file.rb
index 7106b457..c5071d2c 100644
--- a/sinatra-contrib/lib/sinatra/config_file.rb
+++ b/sinatra-contrib/lib/sinatra/config_file.rb
@@ -8,7 +8,7 @@ module Sinatra
#
# Sinatra::ConfigFile is an extension that allows you to load the
# application's configuration from YAML files. It automatically detects if
- # the files contains specific environment settings and it will use the
+ # the files contain specific environment settings and it will use those
# corresponding to the current one.
#
# You can access those options through +settings+ within the application. If
@@ -94,18 +94,11 @@ module Sinatra
# In either case, settings.foo will return the environment name, and
# settings.bar will return "bar".
#
- # Be aware that if you have a different environment, besides development,
- # test and production, you will also need to adjust the +environments+
- # setting, otherwise the settings will not load. For instance, when
- # you also have a staging environment:
+ # If you wish to provide defaults that may be shared among all the
+ # environments, this can be done by using a YAML alias, and then overwriting
+ # values in environments where appropriate:
#
- # set :environments, %w{development test production staging}
- #
- # If you wish to provide defaults that may be shared among all the environments,
- # this can be done by using one of the existing environments as the default using
- # the YAML alias, and then overwriting values in the other environments:
- #
- # development: &common_settings
+ # default: &common_settings
# foo: 'foo'
# bar: 'bar'
#
@@ -131,11 +124,9 @@ module Sinatra
raise UnsupportedConfigType unless ['.yml', '.erb'].include?(File.extname(file))
logger.info "loading config file '#{file}'" if logging? && respond_to?(:logger)
document = ERB.new(IO.read(file)).result
- yaml = config_for_env(YAML.load(document)) || {}
- yaml.each_pair do |key, value|
- for_env = config_for_env(value)
- set key, for_env unless value and for_env.nil? and respond_to? key
- end
+ yaml = YAML.load(document)
+ config = config_for_env(yaml)
+ config.each_pair { |key, value| set(key, value) }
end
end
end
@@ -149,23 +140,32 @@ module Sinatra
private
- # Given a +hash+ with some application configuration it returns the
- # settings applicable to the current environment. Note that this can only
- # be done when all the keys of +hash+ are environment names included in the
- # +environments+ setting (which is an Array of Strings). Also, the
- # returned config is a indifferently accessible Hash, which means that you
- # can get its values using Strings or Symbols as keys.
+ # Given a +hash+ containing application configuration it returns
+ # settings applicable to the current environment. Note: It gives
+ # precedence to environment settings defined at the root-level.
def config_for_env(hash)
- if hash.respond_to?(:keys) && hash.keys.all? { |k| environments.include?(k.to_s) }
- hash = hash[environment.to_s] || hash[environment.to_sym]
- end
+ return from_environment_key(hash) if environment_keys?(hash)
- if hash.respond_to?(:to_hash)
- IndifferentHash[hash.to_hash]
- else
- hash
+ hash.each_with_object(IndifferentHash[]) do |(k, v), acc|
+ if environment_keys?(v)
+ acc.merge!(k => v[environment.to_s]) if v.key?(environment.to_s)
+ else
+ acc.merge!(k => v)
+ end
end
end
+
+ # Given a +hash+ returns the settings corresponding to the current
+ # environment.
+ def from_environment_key(hash)
+ hash[environment.to_s] || hash[environment.to_sym] || {}
+ end
+
+ # Returns true if supplied with a hash that has any recognized
+ # +environments+ in its root keys.
+ def environment_keys?(hash)
+ hash.is_a?(Hash) && hash.any? { |k, _| environments.include?(k.to_s) }
+ end
end
register ConfigFile
diff --git a/sinatra-contrib/spec/config_file/with_env_defaults.yml b/sinatra-contrib/spec/config_file/with_env_defaults.yml
new file mode 100644
index 00000000..a7be1a3a
--- /dev/null
+++ b/sinatra-contrib/spec/config_file/with_env_defaults.yml
@@ -0,0 +1,16 @@
+---
+default: &default
+ foo: default
+ bar: baz
+
+development:
+ <<: *default
+ foo: development
+
+production:
+ <<: *default
+ foo: production
+
+test:
+ <<: *default
+ foo: test
diff --git a/sinatra-contrib/spec/config_file_spec.rb b/sinatra-contrib/spec/config_file_spec.rb
index fbb384dd..9048e914 100644
--- a/sinatra-contrib/spec/config_file_spec.rb
+++ b/sinatra-contrib/spec/config_file_spec.rb
@@ -73,4 +73,24 @@ describe Sinatra::ConfigFile do
config_file 'key_value_override.yml'
expect(settings.foo).to eq('foo')
end
+
+ context 'when file contains superfluous environments' do
+ before { config_file 'with_env_defaults.yml' }
+
+ it 'loads settings for the current environment anyway' do
+ expect { settings.foo }.not_to raise_error
+ end
+ end
+
+ context 'when file contains defaults' do
+ before { config_file 'with_env_defaults.yml' }
+
+ it 'uses the overridden value' do
+ expect(settings.foo).to eq('test')
+ end
+
+ it 'uses the default value' do
+ expect(settings.bar).to eq('baz')
+ end
+ end
end