diff options
Diffstat (limited to 'railties')
-rw-r--r-- | railties/.gitignore | 1 | ||||
-rw-r--r-- | railties/CHANGELOG.md | 5 | ||||
-rw-r--r-- | railties/Rakefile | 2 | ||||
-rw-r--r-- | railties/lib/rails/application.rb | 38 | ||||
-rw-r--r-- | railties/lib/rails/command/behavior.rb | 5 | ||||
-rw-r--r-- | railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt | 9 | ||||
-rw-r--r-- | railties/test/application/configuration_test.rb | 115 | ||||
-rw-r--r-- | railties/test/application/server_test.rb | 16 | ||||
-rw-r--r-- | railties/test/isolation/abstract_unit.rb | 18 | ||||
-rw-r--r-- | railties/test/isolation/assets/config/webpack/development.js | 3 | ||||
-rw-r--r-- | railties/test/isolation/assets/config/webpack/production.js | 3 | ||||
-rw-r--r-- | railties/test/isolation/assets/config/webpack/test.js | 3 | ||||
-rw-r--r-- | railties/test/isolation/assets/config/webpacker.yml | 8 | ||||
-rw-r--r-- | railties/test/isolation/assets/package.json | 9 |
14 files changed, 199 insertions, 36 deletions
diff --git a/railties/.gitignore b/railties/.gitignore index c08562e016..17a49da08c 100644 --- a/railties/.gitignore +++ b/railties/.gitignore @@ -2,4 +2,5 @@ /test/500.html /test/fixtures/tmp/ /test/initializer/root/log/ +/test/isolation/assets/yarn.lock /tmp/ diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 19f4de8a1d..d66add2ca0 100644 --- a/railties/CHANGELOG.md +++ b/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* diff --git a/railties/Rakefile b/railties/Rakefile index 1c4725e2d6..4ae546b202 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -38,7 +38,7 @@ namespace :test do test_patterns = dirs.map { |dir| "test/#{dir}/*_test.rb" } test_files = Dir[*test_patterns].select do |file| - !file.start_with?("test/fixtures/") + !file.start_with?("test/fixtures/") && !file.start_with?("test/isolation/assets/") end.sort if ENV["BUILDKITE_PARALLEL_JOB_COUNT"] diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 5a924ab8e6..d0417f8a49 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -7,6 +7,7 @@ require "active_support/key_generator" 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" @@ -230,8 +231,8 @@ module Rails 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}" @@ -590,5 +591,38 @@ module Rails 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 diff --git a/railties/lib/rails/command/behavior.rb b/railties/lib/rails/command/behavior.rb index 7f32b04cf1..7fb2a99e67 100644 --- a/railties/lib/rails/command/behavior.rb +++ b/railties/lib/rails/command/behavior.rb @@ -71,8 +71,9 @@ module Rails paths = [] namespaces.each do |namespace| pieces = namespace.split(":") - paths << pieces.dup.push(pieces.last).join("/") - paths << pieces.join("/") + path = pieces.join("/") + paths << "#{path}/#{pieces.last}" + paths << path end paths.uniq! paths diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index 94f7dd0c79..ed1cf09eeb 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -107,9 +107,10 @@ Rails.application.configure do # The `database_resolver` class is used by the middleware to determine which # database is appropriate to use based on the time delay. # - # The `database_operations` class is used by the middleware to set timestamps - # for the last write to the primary. The resolver uses the operations class - # timestamps to determine how long to wait before reading from the replica. + # The `database_resolver_context` class is used by the middleware to set + # timestamps for the last write to the primary. The resolver uses the context + # class timestamps to determine how long to wait before reading from the + # replica. # # By default Rails will store a last write timestamp in the session. The # DatabaseSelector middleware is designed as such you can define your own @@ -117,5 +118,5 @@ Rails.application.configure do # these configuration options. # config.active_record.database_selector = { delay: 2.seconds } # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver - # config.active_record.database_operations = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session + # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 37fba72ee3..9ea4d6dc5f 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -1714,7 +1714,7 @@ module ApplicationTests 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' @@ -1727,6 +1727,7 @@ module ApplicationTests 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 @@ -1746,6 +1747,25 @@ module ApplicationTests 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: @@ -1762,12 +1782,93 @@ module ApplicationTests 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 diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb index 5b8aab08d5..9df36b3444 100644 --- a/railties/test/application/server_test.rb +++ b/railties/test/application/server_test.rb @@ -30,17 +30,15 @@ module ApplicationTests pid = nil Bundler.with_original_env do - begin - pid = Process.spawn("bin/rails server -P tmp/dummy.pid", chdir: app_path, in: replica, out: replica, err: replica) - assert_output("Listening", primary) + pid = Process.spawn("bin/rails server -P tmp/dummy.pid", chdir: app_path, in: replica, out: replica, err: replica) + assert_output("Listening", primary) - rails("restart") + rails("restart") - assert_output("Restarting", primary) - assert_output("Inherited", primary) - ensure - kill(pid) if pid - end + assert_output("Restarting", primary) + assert_output("Inherited", primary) + ensure + kill(pid) if pid end end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index ca7601f6fe..0e8e0e86ee 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -472,21 +472,17 @@ Module.new do FileUtils.rm_rf(app_template_path) FileUtils.mkdir_p(app_template_path) - Dir.chdir "#{RAILS_FRAMEWORK_ROOT}/actionview" do - `yarn build` - end - - `#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-bundle --skip-listen --no-rc` + `#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-bundle --skip-listen --no-rc --skip-webpack-install` File.open("#{app_template_path}/config/boot.rb", "w") do |f| f.puts "require 'rails/all'" end - Dir.chdir(app_template_path) { `yarn add https://github.com/rails/webpacker.git` } # Use the latest version. - - # Manually install `webpack` as bin symlinks are not created for sub dependencies - # in workspaces. See https://github.com/yarnpkg/yarn/issues/4964 - Dir.chdir(app_template_path) { `yarn add webpack@4.17.1 --tilde` } - Dir.chdir(app_template_path) { `yarn add webpack-cli` } + assets_path = "#{RAILS_FRAMEWORK_ROOT}/railties/test/isolation/assets" + FileUtils.cp("#{assets_path}/package.json", "#{app_template_path}/package.json") + FileUtils.cp("#{assets_path}/config/webpacker.yml", "#{app_template_path}/config/webpacker.yml") + FileUtils.cp_r("#{assets_path}/config/webpack", "#{app_template_path}/config/webpack") + FileUtils.ln_s("#{assets_path}/node_modules", "#{app_template_path}/node_modules") + FileUtils.chdir(app_template_path) { `bin/rails webpacker:binstubs` } # Fake 'Bundler.require' -- we run using the repo's Gemfile, not an # app-specific one: we don't want to require every gem that lists. diff --git a/railties/test/isolation/assets/config/webpack/development.js b/railties/test/isolation/assets/config/webpack/development.js new file mode 100644 index 0000000000..395290f431 --- /dev/null +++ b/railties/test/isolation/assets/config/webpack/development.js @@ -0,0 +1,3 @@ +process.env.NODE_ENV = process.env.NODE_ENV || 'development' +const { environment } = require('@rails/webpacker') +module.exports = environment.toWebpackConfig() diff --git a/railties/test/isolation/assets/config/webpack/production.js b/railties/test/isolation/assets/config/webpack/production.js new file mode 100644 index 0000000000..d064a6a7fb --- /dev/null +++ b/railties/test/isolation/assets/config/webpack/production.js @@ -0,0 +1,3 @@ +process.env.NODE_ENV = process.env.NODE_ENV || 'production' +const { environment } = require('@rails/webpacker') +module.exports = environment.toWebpackConfig() diff --git a/railties/test/isolation/assets/config/webpack/test.js b/railties/test/isolation/assets/config/webpack/test.js new file mode 100644 index 0000000000..395290f431 --- /dev/null +++ b/railties/test/isolation/assets/config/webpack/test.js @@ -0,0 +1,3 @@ +process.env.NODE_ENV = process.env.NODE_ENV || 'development' +const { environment } = require('@rails/webpacker') +module.exports = environment.toWebpackConfig() diff --git a/railties/test/isolation/assets/config/webpacker.yml b/railties/test/isolation/assets/config/webpacker.yml new file mode 100644 index 0000000000..0b1f43a407 --- /dev/null +++ b/railties/test/isolation/assets/config/webpacker.yml @@ -0,0 +1,8 @@ +default: &default + check_yarn_integrity: false +development: + <<: *default +test: + <<: *default +production: + <<: *default diff --git a/railties/test/isolation/assets/package.json b/railties/test/isolation/assets/package.json new file mode 100644 index 0000000000..106b1029f0 --- /dev/null +++ b/railties/test/isolation/assets/package.json @@ -0,0 +1,9 @@ +{ + "name": "dummy", + "private": true, + "dependencies": { + "@rails/ujs": "file:../../../../actionview", + "@rails/webpacker": "https://github.com/rails/webpacker.git", + "turbolinks": "^5.2.0" + } +} |