Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #35198 from paracycle/uk-change-config-for-behaviour
Allow deprecated non-symbol access to nested `config_for` hashes
  • Loading branch information
gmcgibbon committed Feb 11, 2019
2 parents 05232f3 + de96628 commit f9e6819
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 9 deletions.
5 changes: 5 additions & 0 deletions railties/CHANGELOG.md
@@ -1,3 +1,8 @@
* Fix non-symbol access to nested hashes returned from `Rails::Application.config_for`
being broken by allowing non-symbol access with a deprecation notice.

*Ufuk Kayserilioglu*

* Fix deeply nested namespace command printing.

*Gannon McGibbon*
Expand Down
38 changes: 36 additions & 2 deletions railties/lib/rails/application.rb
Expand Up @@ -7,6 +7,7 @@
require "active_support/message_verifier"
require "active_support/encrypted_configuration"
require "active_support/deprecation"
require "active_support/hash_with_indifferent_access"
require "rails/engine"
require "rails/secrets"

Expand Down Expand Up @@ -230,8 +231,8 @@ def config_for(name, env: Rails.env)
config = YAML.load(ERB.new(yaml.read).result) || {}
config = (config["shared"] || {}).merge(config[env] || {})

ActiveSupport::OrderedOptions.new.tap do |config_as_ordered_options|
config_as_ordered_options.update(config.deep_symbolize_keys)
ActiveSupport::OrderedOptions.new.tap do |options|
options.update(NonSymbolAccessDeprecatedHash.new(config))
end
else
raise "Could not load configuration. No such file - #{yaml}"
Expand Down Expand Up @@ -590,5 +591,38 @@ def build_request(env)
def build_middleware
config.app_middleware + super
end

class NonSymbolAccessDeprecatedHash < HashWithIndifferentAccess # :nodoc:
def initialize(value = nil)
if value.is_a?(Hash)
value.each_pair { |k, v| self[k] = v }
else
super
end
end

def []=(key, value)
if value.is_a?(Hash)
value = self.class.new(value)
end
super(key.to_sym, value)
end

private

def convert_key(key)
unless key.kind_of?(Symbol)
ActiveSupport::Deprecation.warn(<<~MESSAGE.squish)
Accessing hashes returned from config_for by non-symbol keys
is deprecated and will be removed in Rails 6.1.
Use symbols for access instead.
MESSAGE

key = key.to_sym
end

key
end
end
end
end
115 changes: 108 additions & 7 deletions railties/test/application/configuration_test.rb
Expand Up @@ -1714,7 +1714,7 @@ def index
assert_equal true, Rails.application.config.action_mailer.show_previews
end

test "config_for loads custom configuration from yaml accessible as symbol" do
test "config_for loads custom configuration from yaml accessible as symbol or string" do
app_file "config/custom.yml", <<-RUBY
development:
foo: 'bar'
Expand All @@ -1727,6 +1727,7 @@ def index
app "development"

assert_equal "bar", Rails.application.config.my_custom_config[:foo]
assert_equal "bar", Rails.application.config.my_custom_config["foo"]
end

test "config_for loads nested custom configuration from yaml as symbol keys" do
Expand All @@ -1746,6 +1747,25 @@ def index
assert_equal 1, Rails.application.config.my_custom_config[:foo][:bar][:baz]
end

test "config_for loads nested custom configuration from yaml with deprecated non-symbol access" do
app_file "config/custom.yml", <<-RUBY
development:
foo:
bar:
baz: 1
RUBY

add_to_config <<-RUBY
config.my_custom_config = config_for('custom')
RUBY

app "development"

assert_deprecated do
assert_equal 1, Rails.application.config.my_custom_config["foo"]["bar"]["baz"]
end
end

test "config_for makes all hash methods available" do
app_file "config/custom.yml", <<-RUBY
development:
Expand All @@ -1762,12 +1782,93 @@ def index

actual = Rails.application.config.my_custom_config

assert_equal actual, foo: 0, bar: { baz: 1 }
assert_equal actual.keys, [ :foo, :bar ]
assert_equal actual.values, [ 0, baz: 1]
assert_equal actual.to_h, foo: 0, bar: { baz: 1 }
assert_equal actual[:foo], 0
assert_equal actual[:bar], baz: 1
assert_equal({ foo: 0, bar: { baz: 1 } }, actual)
assert_equal([ :foo, :bar ], actual.keys)
assert_equal([ 0, baz: 1], actual.values)
assert_equal({ foo: 0, bar: { baz: 1 } }, actual.to_h)
assert_equal(0, actual[:foo])
assert_equal({ baz: 1 }, actual[:bar])
end

test "config_for generates deprecation notice when nested hash methods are called with non-symbols" do
app_file "config/custom.yml", <<-RUBY
development:
foo:
bar: 1
baz: 2
qux:
boo: 3
RUBY

app "development"

actual = Rails.application.config_for("custom")[:foo]

# slice
assert_deprecated do
assert_equal({ bar: 1, baz: 2 }, actual.slice("bar", "baz"))
end

# except
assert_deprecated do
assert_equal({ qux: { boo: 3 } }, actual.except("bar", "baz"))
end

# dig
assert_deprecated do
assert_equal(3, actual.dig("qux", "boo"))
end

# fetch - hit
assert_deprecated do
assert_equal(1, actual.fetch("bar", 0))
end

# fetch - miss
assert_deprecated do
assert_equal(0, actual.fetch("does-not-exist", 0))
end

# fetch_values
assert_deprecated do
assert_equal([1, 2], actual.fetch_values("bar", "baz"))
end

# key? - hit
assert_deprecated do
assert(actual.key?("bar"))
end

# key? - miss
assert_deprecated do
assert_not(actual.key?("does-not-exist"))
end

# slice!
actual = Rails.application.config_for("custom")[:foo]

assert_deprecated do
slice = actual.slice!("bar", "baz")
assert_equal({ bar: 1, baz: 2 }, actual)
assert_equal({ qux: { boo: 3 } }, slice)
end

# extract!
actual = Rails.application.config_for("custom")[:foo]

assert_deprecated do
extracted = actual.extract!("bar", "baz")
assert_equal({ bar: 1, baz: 2 }, extracted)
assert_equal({ qux: { boo: 3 } }, actual)
end

# except!
actual = Rails.application.config_for("custom")[:foo]

assert_deprecated do
actual.except!("bar", "baz")
assert_equal({ qux: { boo: 3 } }, actual)
end
end

test "config_for uses the Pathname object if it is provided" do
Expand Down

0 comments on commit f9e6819

Please sign in to comment.