diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb index 123c194aacc2d..518f214fe30e3 100644 --- a/actionmailer/test/url_test.rb +++ b/actionmailer/test/url_test.rb @@ -18,7 +18,9 @@ class WelcomeController < ActionController::Base class UrlTestMailer < ActionMailer::Base include AppRoutes.url_helpers - default_url_options[:host] = "www.basecamphq.com" + def default_url_options + { host: "www.basecamphq.com" } + end configure do |c| c.assets_dir = "" # To get the tests to pass diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index a99db277eecdb..81aaef9500d65 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -90,6 +90,42 @@ module Routing # User.find(1).base_uri # => "/users/1" # module UrlFor + class DefaultUrlOptionsProxy # :nodoc: + def initialize(owner) + @owner = owner + end + + def to_hash + @owner.default_url_options_hash + end + + def method_missing(name, *args) + return super unless @owner.default_url_options_hash.respond_to?(name) + + @owner.default_url_options_hash.public_send(name, *args) + rescue FrozenError + trigger_deprecation + + @owner.default_url_options_hash = @owner.default_url_options_hash.dup + @owner.default_url_options_hash.public_send(name, *args) + end + + def respond_to_missing?(name, _) + @owner.respond_to?(name) || super + end + + private + + def trigger_deprecation + ActionController.deprecator.warn(<<~MSG.squish) + Mutating the `default_url_options` is deprecated and will raise a FrozenError in the next + Rails release. Mutating the `default_url_options` can be a source of hard to debug issues. + + Instead, implement `def default_url_options` in your class or controller. + MSG + end + end + extend ActiveSupport::Concern include PolymorphicRoutes @@ -97,12 +133,13 @@ module UrlFor unless method_defined?(:default_url_options) # Including in a class uses an inheritable hash. Modules get a plain hash. if respond_to?(:class_attribute) - class_attribute :default_url_options + class_attribute :default_url_options, :default_url_options_hash else - mattr_writer :default_url_options + mattr_writer :default_url_options, :default_url_options_hash end - self.default_url_options = {} + self.default_url_options_hash = {}.freeze + self.default_url_options = DefaultUrlOptionsProxy.new(self).freeze end include(*_url_for_modules) if respond_to?(:_url_for_modules) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 035f15405cc20..767707d8d8652 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -138,7 +138,8 @@ def initialize(app) end def url_options - @url_options ||= default_url_options.dup.tap do |url_options| + @url_options ||= begin + url_options = {}.merge(default_url_options) url_options.reverse_merge!(controller.url_options) if controller.respond_to?(:url_options) if @app.respond_to?(:routes) diff --git a/actionview/test/activerecord/polymorphic_routes_test.rb b/actionview/test/activerecord/polymorphic_routes_test.rb index ab3e99d4b7ba1..5f4a0b5d0c2f0 100644 --- a/actionview/test/activerecord/polymorphic_routes_test.rb +++ b/actionview/test/activerecord/polymorphic_routes_test.rb @@ -66,7 +66,9 @@ class PolymorphicRoutesTest < ActionController::TestCase Routes.draw { } include Routes.url_helpers - default_url_options[:host] = "example.com" + def default_url_options + { host: "example.com" } + end def setup super @@ -83,7 +85,7 @@ def setup end def assert_url(url, args) - host = self.class.default_url_options[:host] + host = default_url_options[:host] assert_equal url.delete_prefix("http://#{host}"), polymorphic_path(args) assert_equal url, polymorphic_url(args) @@ -741,7 +743,7 @@ class PolymorphicPathRoutesTest < PolymorphicRoutesTest attr_accessor :controller def assert_url(url, args) - host = self.class.default_url_options[:host] + host = default_url_options[:host] assert_equal url.delete_prefix("http://#{host}"), url_for(args) end