From e5e9c558a3654d1365bae0747de64e115b2871d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Barri=C3=A9?= Date: Tue, 3 Dec 2019 15:43:10 -0500 Subject: [PATCH] Remove deprecated non-symbol access to nested config_for hashes --- guides/source/upgrading_ruby_on_rails.md | 24 ++++ railties/CHANGELOG.md | 17 +++ railties/lib/rails/application.rb | 52 +------- .../test/application/configuration_test.rb | 122 ------------------ 4 files changed, 44 insertions(+), 171 deletions(-) diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index a6d00f71b0..8eb9984872 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -75,6 +75,30 @@ To allow you to upgrade to new defaults one by one, the update task has created Upgrading from Rails 6.0 to Rails 6.1 ------------------------------------- +### `Rails.application.config_for` return value no longer supports access with String keys. + +Given a configuration file like this: + +```yaml +# config/example.yml +development: + options: + key: value +``` + +```ruby +Rails.application.config_for(:example).options +``` + +This used to return a hash on which you could access values with String keys. That was deprecated in 6.0, and now doesn't work anymore. + +You can call `with_indifferent_access` on the return value of `config_for` if you still want to access values with String keys, e.g.: + +```ruby +Rails.application.config_for(:example).with_indifferent_access.dig('options', 'key') +``` + + ### Response's Content-Type when using `respond_to#any` The Content-Type header returned in the response can differ from what Rails 6.0 returned, diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 13d8a9800f..832dd19040 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,20 @@ +* Remove access to values in nested hashes returned by `Rails.application.config_for` via String keys. + + ```yaml + # config/example.yml + development: + options: + key: value + ``` + + ```ruby + Rails.application.config_for(:example).options + ``` + + This used to return a Hash on which you could access values with String keys. This was deprecated in 6.0, and now doesn't work anymore. + + *Étienne Barrié* + * Configuration files for environments (`config/environments/*.rb`) are now able to modify `autoload_paths`, `autoload_once_paths`, and `eager_load_paths`. diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 0140c811e7..297889542e 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -228,11 +228,11 @@ def config_for(name, env: Rails.env) if yaml.exist? require "erb" - config = YAML.load(ERB.new(yaml.read).result) || {} - config = (config["shared"] || {}).merge(config[env] || {}) + config = YAML.load(ERB.new(yaml.read).result, symbolize_names: true) || {} + config = (config[:shared] || {}).merge(config[env.to_sym] || {}) ActiveSupport::OrderedOptions.new.tap do |options| - options.update(NonSymbolAccessDeprecatedHash.new(config)) + options.update(config) end else raise "Could not load configuration. No such file - #{yaml}" @@ -604,51 +604,5 @@ 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) - regular_writer(key.to_sym, convert_value(value, for: :assignment)) - 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 - - def convert_value(value, options = {}) # :doc: - if value.is_a? Hash - if options[:for] == :to_hash - value.to_hash - else - self.class.new(value) - end - elsif value.is_a?(Array) - if options[:for] != :assignment || value.frozen? - value = value.dup - end - value.map! { |e| convert_value(e, options) } - else - value - end - end - end end end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 3fba455ebb..c999a6fbac 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -1934,47 +1934,6 @@ class D < C 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 loads nested custom configuration inside array 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" - - config = Rails.application.config.my_custom_config - assert_instance_of Rails::Application::NonSymbolAccessDeprecatedHash, config[:foo][:bar].first - - assert_deprecated do - assert_equal 1, config[:foo][:bar].first["baz"] - end - end - test "config_for makes all hash methods available" do app_file "config/custom.yml", <<-RUBY development: @@ -1999,87 +1958,6 @@ class D < C 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 app_file "config/custom.yml", <<-RUBY development: