diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 772a50b3688..dfd28c734cb 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,39 @@ +* Deprecate usage of the singleton `ActiveSupport::Deprecation`. + + All usage of `ActiveSupport::Deprecation` as a singleton is deprecated, the most common one being + `ActiveSupport::Deprecation.warn`. Gem authors should now create their own deprecator (`ActiveSupport::Deprecation` + object), and use it to emit deprecation warnings. + + Calling any of the following without specifying a deprecator argument is also deprecated: + * Module.deprecate + * deprecate_constant + * DeprecatedObjectProxy + * DeprecatedInstanceVariableProxy + * DeprecatedConstantProxy + * deprecation-related test assertions + + Use of `ActiveSupport::Deprecation.silence` and configuration methods like `behavior=`, `disallowed_behavior=`, + `disallowed_warnings=` should now be aimed at the [application's deprecators](https://api.rubyonrails.org/classes/Rails/Application.html#method-i-deprecators). + + ```ruby + Rails.application.deprecators.silence do + # code that emits deprecation warnings + end + ``` + + If your gem has a Railtie or Engine, it's encouraged to add your deprecator to the application's deprecators, that + way the deprecation related configuration options will apply to it as well, e.g. + `config.active_support.report_deprecations` set to `false` in the production environment will also disable your + deprecator. + + ```ruby + initializer "my_gem.deprecator" do |app| + app.deprecators[:my_gem] = MyGem.deprecator + end + ``` + + *Étienne Barrié* + * Add `Object#with` to set and restore public attributes around a block ```ruby @@ -459,6 +495,9 @@ deprecator.warn("bar") # => raise ActiveSupport::DeprecationException ``` + Note that global `ActiveSupport::Deprecation` methods such as `ActiveSupport::Deprecation.warn` + and `ActiveSupport::Deprecation.disallowed_warnings` have been deprecated. + *Jonathan Hefner* * Add italic and underline support to `ActiveSupport::LogSubscriber#color` diff --git a/activesupport/lib/active_support/core_ext/module/deprecation.rb b/activesupport/lib/active_support/core_ext/module/deprecation.rb index 71c42eb3575..596001f1cda 100644 --- a/activesupport/lib/active_support/core_ext/module/deprecation.rb +++ b/activesupport/lib/active_support/core_ext/module/deprecation.rb @@ -1,17 +1,12 @@ # frozen_string_literal: true class Module - # deprecate :foo - # deprecate bar: 'message' - # deprecate :foo, :bar, baz: 'warning!', qux: 'gone!' + # deprecate :foo, deprecator: MyLib.deprecator + # deprecate :foo, bar: "warning!", deprecator: MyLib.deprecator # - # You can also use custom deprecator instance: - # - # deprecate :foo, deprecator: MyLib::Deprecator.new - # deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new - # - # \Custom deprecators must respond to deprecation_warning(deprecated_method_name, message, caller_backtrace) - # method where you can implement your custom warning behavior. + # A deprecator is typically an instance of ActiveSupport::Deprecation, but you can also pass any object that responds + # to deprecation_warning(deprecated_method_name, message, caller_backtrace) where you can implement your + # custom warning behavior. # # class MyLib::Deprecator # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil) @@ -19,7 +14,15 @@ class Module # Kernel.warn message # end # end - def deprecate(*method_names) - ActiveSupport::Deprecation.deprecate_methods(self, *method_names) + def deprecate(*method_names, deprecator: nil, **options) + if deprecator.is_a?(ActiveSupport::Deprecation) + deprecator.deprecate_methods(self, *method_names, **options) + elsif deprecator + # we just need any instance to call deprecate_methods, but the deprecation will be emitted by deprecator + ActiveSupport.deprecator.deprecate_methods(self, *method_names, **options, deprecator: deprecator) + else + ActiveSupport.deprecator.warn("Module.deprecate without a deprecator is deprecated") + ActiveSupport::Deprecation._instance.deprecate_methods(self, *method_names, **options) + end end end diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 490c048ccec..29c88f63263 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -3,8 +3,33 @@ require "singleton" module ActiveSupport - # \Deprecation specifies the API used by Rails to deprecate methods, instance - # variables, objects, and constants. + # \Deprecation specifies the API used by Rails to deprecate methods, instance variables, objects, and constants. It's + # also available for gems or applications. + # + # For a gem, use Deprecation.new to create a Deprecation object and store it in your module or class (in order for + # users to be able to configure it). + # + # module MyLibrary + # def self.deprecator + # @deprecator ||= ActiveSupport::Deprecation.new("2.0", "MyLibrary") + # end + # end + # + # For a Railtie or Engine, you may also want to add it to the application's deprecators, so that the application's + # configuration can be applied to it. + # + # module MyLibrary + # class Railtie < Rails::Railtie + # initializer "deprecator" do |app| + # app.deprecators[:my_library] = MyLibrary.deprecator + # end + # end + # end + # + # With the above initializer, configuration settings like the following will affect +MyLibrary.deprecator+: + # + # # in config/environments/test.rb + # config.active_support.deprecation = :raise class Deprecation # active_support.rb sets an autoload for ActiveSupport::Deprecation. # @@ -25,7 +50,7 @@ module ActiveSupport require "active_support/core_ext/module/deprecation" require "concurrent/atomic/thread_local_var" - include Singleton + include Singleton # :nodoc: include InstanceDelegator include Behavior include Reporting diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb index d95082efbfe..552eb132d63 100644 --- a/activesupport/lib/active_support/deprecation/behaviors.rb +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -88,10 +88,11 @@ module ActiveSupport # Deprecation warnings raised by gems are not affected by this setting # because they happen before Rails boots up. # - # ActiveSupport::Deprecation.behavior = :stderr - # ActiveSupport::Deprecation.behavior = [:stderr, :log] - # ActiveSupport::Deprecation.behavior = MyCustomHandler - # ActiveSupport::Deprecation.behavior = ->(message, callstack, deprecation_horizon, gem_name) { + # deprecator = ActiveSupport::Deprecation.new + # deprecator.behavior = :stderr + # deprecator.behavior = [:stderr, :log] + # deprecator.behavior = MyCustomHandler + # deprecator.behavior = ->(message, callstack, deprecation_horizon, gem_name) { # # custom stuff # } # @@ -102,7 +103,7 @@ module ActiveSupport end # Sets the behavior for disallowed deprecations (those configured by - # ActiveSupport::Deprecation.disallowed_warnings=) to the specified + # ActiveSupport::Deprecation#disallowed_warnings=) to the specified # value. As with +behavior=+, this can be a single value, array, or an # object that responds to +call+. def disallowed_behavior=(behavior) diff --git a/activesupport/lib/active_support/deprecation/constant_accessor.rb b/activesupport/lib/active_support/deprecation/constant_accessor.rb index 1ed00158123..73e2a190e72 100644 --- a/activesupport/lib/active_support/deprecation/constant_accessor.rb +++ b/activesupport/lib/active_support/deprecation/constant_accessor.rb @@ -6,8 +6,7 @@ module ActiveSupport # hooking +const_missing+. # # It takes the names of an old (deprecated) constant and of a new constant - # (both in string form) and optionally a deprecator. The deprecator defaults - # to +ActiveSupport::Deprecator+ if none is specified. + # (both in string form) and a deprecator. # # The deprecated constant now returns the same object as the new one rather # than a proxy object, so it can be used transparently in +rescue+ blocks @@ -19,7 +18,7 @@ module ActiveSupport # # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune) # include ActiveSupport::Deprecation::DeprecatedConstantAccessor - # deprecate_constant 'PLANETS', 'PLANETS_POST_2006' + # deprecate_constant 'PLANETS', 'PLANETS_POST_2006', deprecator: ActiveSupport::Deprecation.new # # PLANETS.map { |planet| planet.capitalize } # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead. @@ -40,7 +39,9 @@ module ActiveSupport super end - def deprecate_constant(const_name, new_constant, message: nil, deprecator: ActiveSupport::Deprecation.instance) + def deprecate_constant(const_name, new_constant, message: nil, deprecator: nil) + ActiveSupport.deprecator.warn("DeprecatedConstantAccessor.deprecate_constant without a deprecator is deprecated") unless deprecator + deprecator ||= ActiveSupport::Deprecation._instance class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants) class_variable_get(:@@_deprecated_constants)[const_name.to_s] = { new: new_constant, message: message, deprecator: deprecator } end diff --git a/activesupport/lib/active_support/deprecation/instance_delegator.rb b/activesupport/lib/active_support/deprecation/instance_delegator.rb index 59dd30ae303..24b8d0822d5 100644 --- a/activesupport/lib/active_support/deprecation/instance_delegator.rb +++ b/activesupport/lib/active_support/deprecation/instance_delegator.rb @@ -1,11 +1,10 @@ # frozen_string_literal: true -require "active_support/core_ext/module/delegation" - module ActiveSupport class Deprecation module InstanceDelegator # :nodoc: def self.included(base) + base.singleton_class.alias_method(:_instance, :instance) base.extend(ClassMethods) base.singleton_class.prepend(OverrideDelegators) base.public_class_method :new @@ -18,7 +17,30 @@ module ActiveSupport end def method_added(method_name) - singleton_class.delegate(method_name, to: :instance) + use_instead = + case method_name + when :silence, :behavior=, :disallowed_behavior=, :disallowed_warnings=, :silenced=, :debug= + target = "(defined?(Rails.application.deprecators) ? Rails.application.deprecators : ActiveSupport::Deprecation._instance)" + "Rails.application.deprecators.#{method_name}" + when :warn, :deprecate_methods, :gem_name, :gem_name=, :deprecation_horizon, :deprecation_horizon= + "your own Deprecation object" + else + "Rails.application.deprecators[framework].#{method_name} where framework is for example :active_record" + end + args = /[^\]]=\z/.match?(method_name) ? "arg" : "..." + target ||= "ActiveSupport::Deprecation._instance" + singleton_class.module_eval <<~RUBY, __FILE__, __LINE__ + 1 + def #{method_name}(#{args}) + #{target}.#{method_name}(#{args}) + ensure + ActiveSupport.deprecator.warn("Calling #{method_name} on ActiveSupport::Deprecation is deprecated and will be removed from Rails (use #{use_instead} instead)") + end + RUBY + end + + def instance + ActiveSupport.deprecator.warn("ActiveSupport::Deprecation.instance is deprecated (use your own Deprecation object)") + super end end diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index 54375982c2a..921168c94ab 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -16,38 +16,21 @@ module ActiveSupport # def eee; end # end # - # Using the default deprecator: - # ActiveSupport::Deprecation.deprecate_methods(Fred, :aaa, bbb: :zzz, ccc: 'use Bar#ccc instead') + # deprecator = ActiveSupport::Deprecation.new('next-release', 'MyGem') + # + # deprecator.deprecate_methods(Fred, :aaa, bbb: :zzz, ccc: 'use Bar#ccc instead') # # => Fred # # Fred.new.aaa - # # DEPRECATION WARNING: aaa is deprecated and will be removed from Rails 5.1. (called from irb_binding at (irb):10) + # # DEPRECATION WARNING: aaa is deprecated and will be removed from MyGem next-release. (called from irb_binding at (irb):10) # # => nil # # Fred.new.bbb - # # DEPRECATION WARNING: bbb is deprecated and will be removed from Rails 5.1 (use zzz instead). (called from irb_binding at (irb):11) + # # DEPRECATION WARNING: bbb is deprecated and will be removed from MyGem next-release (use zzz instead). (called from irb_binding at (irb):11) # # => nil # # Fred.new.ccc - # # DEPRECATION WARNING: ccc is deprecated and will be removed from Rails 5.1 (use Bar#ccc instead). (called from irb_binding at (irb):12) - # # => nil - # - # Passing in a custom deprecator: - # custom_deprecator = ActiveSupport::Deprecation.new('next-release', 'MyGem') - # ActiveSupport::Deprecation.deprecate_methods(Fred, ddd: :zzz, deprecator: custom_deprecator) - # # => [:ddd] - # - # Fred.new.ddd - # DEPRECATION WARNING: ddd is deprecated and will be removed from MyGem next-release (use zzz instead). (called from irb_binding at (irb):15) - # # => nil - # - # Using a custom deprecator directly: - # custom_deprecator = ActiveSupport::Deprecation.new('next-release', 'MyGem') - # custom_deprecator.deprecate_methods(Fred, eee: :zzz) - # # => [:eee] - # - # Fred.new.eee - # DEPRECATION WARNING: eee is deprecated and will be removed from MyGem next-release (use zzz instead). (called from irb_binding at (irb):18) + # # DEPRECATION WARNING: ccc is deprecated and will be removed from MyGem next-release (use Bar#ccc instead). (called from irb_binding at (irb):12) # # => nil def deprecate_methods(target_module, *method_names) options = method_names.extract_options! diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index bd236375926..d49b834d5bf 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -25,11 +25,10 @@ module ActiveSupport end end - # DeprecatedObjectProxy transforms an object into a deprecated one. It - # takes an object, a deprecation message, and optionally a deprecator. The - # deprecator defaults to +ActiveSupport::Deprecator+ if none is specified. + # DeprecatedObjectProxy transforms an object into a deprecated one. It takes an object, a deprecation message, and + # a deprecator. # - # deprecated_object = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, "This object is now deprecated") + # deprecated_object = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, "This object is now deprecated", ActiveSupport::Deprecation.new) # # => # # # deprecated_object.to_s @@ -37,10 +36,11 @@ module ActiveSupport # (Backtrace) # # => "#" class DeprecatedObjectProxy < DeprecationProxy - def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance) + def initialize(object, message, deprecator = nil) @object = object @message = message - @deprecator = deprecator + ActiveSupport.deprecator.warn("DeprecatedObjectProxy without a deprecator is deprecated") unless deprecator + @deprecator = deprecator || ActiveSupport::Deprecation._instance end private @@ -53,15 +53,15 @@ module ActiveSupport end end - # DeprecatedInstanceVariableProxy transforms an instance variable into a - # deprecated one. It takes an instance of a class, a method on that class - # and an instance variable. It optionally takes a deprecator as the last - # argument. The deprecator defaults to +ActiveSupport::Deprecator+ if none - # is specified. + # DeprecatedInstanceVariableProxy transforms an instance variable into a deprecated one. It takes an instance of a + # class, a method on that class, an instance variable, and a deprecator as the last argument. + # + # Trying to use the deprecated instance variable will result in a deprecation warning, pointing to the method as a + # replacement. # # class Example # def initialize - # @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request) + # @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, ActiveSupport::Deprecation.new) # @_request = :special_request # end # @@ -86,11 +86,12 @@ module ActiveSupport # example.request.to_s # # => "special_request" class DeprecatedInstanceVariableProxy < DeprecationProxy - def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance) + def initialize(instance, method, var = "@#{method}", deprecator = nil) @instance = instance @method = method @var = var - @deprecator = deprecator + ActiveSupport.deprecator.warn("DeprecatedInstanceVariableProxy without a deprecator is deprecated") unless deprecator + @deprecator = deprecator || ActiveSupport::Deprecation._instance end private @@ -103,18 +104,16 @@ module ActiveSupport end end - # DeprecatedConstantProxy transforms a constant into a deprecated one. It - # takes the full names of an old (deprecated) constant and of a new constant - # (both in string form) and optionally a deprecator. The deprecator defaults - # to +ActiveSupport::Deprecator+ if none is specified. The deprecated constant - # now returns the value of the new one. + # DeprecatedConstantProxy transforms a constant into a deprecated one. It takes the full names of an old + # (deprecated) constant and of a new constant (both in string form) and a deprecator. The deprecated constant now + # returns the value of the new one. # # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto) # # # (In a later update, the original implementation of `PLANETS` has been removed.) # # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune) - # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006') + # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("PLANETS", "PLANETS_POST_2006", ActiveSupport::Deprecation.new) # # PLANETS.map { |planet| planet.capitalize } # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead. @@ -128,12 +127,13 @@ module ActiveSupport super end - def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance, message: "#{old_const} is deprecated! Use #{new_const} instead.") + def initialize(old_const, new_const, deprecator = nil, message: "#{old_const} is deprecated! Use #{new_const} instead.") Kernel.require "active_support/inflector/methods" @old_const = old_const @new_const = new_const - @deprecator = deprecator + ActiveSupport.deprecator.warn("DeprecatedConstantProxy without a deprecator is deprecated") unless deprecator + @deprecator = deprecator || ActiveSupport::Deprecation._instance @message = message end diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index bad80975029..657d962c601 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -10,10 +10,9 @@ module ActiveSupport # Name of gem where method is deprecated attr_accessor :gem_name - # Outputs a deprecation warning to the output configured by - # ActiveSupport::Deprecation.behavior. + # Outputs a deprecation warning to the output configured by ActiveSupport::Deprecation#behavior. # - # ActiveSupport::Deprecation.warn('something broke!') + # ActiveSupport::Deprecation.new.warn('something broke!') # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" def warn(message = nil, callstack = nil) return if silenced @@ -30,11 +29,12 @@ module ActiveSupport # Silence deprecation warnings within the block. # - # ActiveSupport::Deprecation.warn('something broke!') + # deprecator = ActiveSupport::Deprecation.new + # deprecator.warn('something broke!') # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" # - # ActiveSupport::Deprecation.silence do - # ActiveSupport::Deprecation.warn('something broke!') + # deprecator.silence do + # deprecator.warn('something broke!') # end # # => nil def silence(&block) @@ -61,27 +61,28 @@ module ActiveSupport # expressions. (Symbols are treated as strings). These are compared against # the text of deprecation warning messages generated within the block. # Matching warnings will be exempt from the rules set by - # +ActiveSupport::Deprecation.disallowed_warnings+ + # +ActiveSupport::Deprecation#disallowed_warnings+ # # The optional if: argument accepts a truthy/falsy value or an object that # responds to .call. If truthy, then matching warnings will be allowed. # If falsey then the method yields to the block without allowing the warning. # - # ActiveSupport::Deprecation.disallowed_behavior = :raise - # ActiveSupport::Deprecation.disallowed_warnings = [ + # deprecator = ActiveSupport::Deprecation.new + # deprecator.disallowed_behavior = :raise + # deprecator.disallowed_warnings = [ # "something broke" # ] # - # ActiveSupport::Deprecation.warn('something broke!') + # deprecator.warn('something broke!') # # => ActiveSupport::DeprecationException # - # ActiveSupport::Deprecation.allow ['something broke'] do - # ActiveSupport::Deprecation.warn('something broke!') + # deprecator.allow ['something broke'] do + # deprecator.warn('something broke!') # end # # => nil # - # ActiveSupport::Deprecation.allow ['something broke'], if: Rails.env.production? do - # ActiveSupport::Deprecation.warn('something broke!') + # deprecator.allow ['something broke'], if: Rails.env.production? do + # deprecator.warn('something broke!') # end # # => ActiveSupport::DeprecationException for dev/test, nil for production def allow(allowed_warnings = :all, if: true, &block) diff --git a/activesupport/lib/active_support/deprecator.rb b/activesupport/lib/active_support/deprecator.rb index 6cdcbc2bcb3..3e3c38ecd23 100644 --- a/activesupport/lib/active_support/deprecator.rb +++ b/activesupport/lib/active_support/deprecator.rb @@ -2,6 +2,6 @@ module ActiveSupport def self.deprecator # :nodoc: - @deprecator ||= ActiveSupport::Deprecation.new + ActiveSupport::Deprecation._instance end end diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb index 4350416286d..073300bb81f 100644 --- a/activesupport/lib/active_support/testing/deprecation.rb +++ b/activesupport/lib/active_support/testing/deprecation.rb @@ -7,8 +7,6 @@ module ActiveSupport module Deprecation ## # :call-seq: - # assert_deprecated(&block) - # assert_deprecated(match, &block) # assert_deprecated(deprecator, &block) # assert_deprecated(match, deprecator, &block) # @@ -29,14 +27,12 @@ module ActiveSupport # assert_deprecated(CustomDeprecator) do # CustomDeprecator.warn "foo should no longer be used" # end - # - # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation. - # - # assert_deprecated do - # ActiveSupport::Deprecation.warn "foo should no longer be used" - # end def assert_deprecated(match = nil, deprecator = nil, &block) match, deprecator = nil, match if match.is_a?(ActiveSupport::Deprecation) + unless deprecator + ActiveSupport.deprecator.warn("assert_deprecated without a deprecator is deprecated") + deprecator = ActiveSupport::Deprecation._instance + end result, warnings = collect_deprecations(deprecator, &block) assert !warnings.empty?, "Expected a deprecation warning within the block but received none" if match @@ -52,16 +48,14 @@ module ActiveSupport # CustomDeprecator.warn "message" # fails assertion # end # - # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation. - # - # assert_not_deprecated do - # ActiveSupport::Deprecation.warn "message" # fails assertion - # end - # - # assert_not_deprecated do - # CustomDeprecator.warn "message" # passes assertion + # assert_not_deprecated(ActiveSupport::Deprecation.new) do + # CustomDeprecator.warn "message" # passes assertion, different deprecator # end def assert_not_deprecated(deprecator = nil, &block) + unless deprecator + ActiveSupport.deprecator.warn("assert_not_deprecated without a deprecator is deprecated") + deprecator = ActiveSupport::Deprecation._instance + end result, deprecations = collect_deprecations(deprecator, &block) assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}" result @@ -72,18 +66,14 @@ module ActiveSupport # # collect_deprecations(CustomDeprecator) do # CustomDeprecator.warn "message" - # :result - # end # => [:result, ["message"]] - # - # If no +deprecator+ is given, defaults to ActiveSupport::Deprecation. - # - # collect_deprecations do - # CustomDeprecator.warn "custom message" - # ActiveSupport::Deprecation.warn "message" + # ActiveSupport::Deprecation.new.warn "other message" # :result # end # => [:result, ["message"]] def collect_deprecations(deprecator = nil) - deprecator ||= ActiveSupport::Deprecation + unless deprecator + ActiveSupport.deprecator.warn("collect_deprecations without a deprecator is deprecated") + deprecator = ActiveSupport::Deprecation._instance + end old_behavior = deprecator.behavior deprecations = [] deprecator.behavior = Proc.new do |message, callstack| diff --git a/activesupport/test/deprecation/method_wrappers_test.rb b/activesupport/test/deprecation/method_wrappers_test.rb index 65c8b50a452..2d5a44561a6 100644 --- a/activesupport/test/deprecation/method_wrappers_test.rb +++ b/activesupport/test/deprecation/method_wrappers_test.rb @@ -39,10 +39,13 @@ class MethodWrappersTest < ActiveSupport::TestCase def test_deprecate_methods_warning_with_optional_deprecator @deprecator = ActiveSupport::Deprecation.new("next-release", "MyGem") - ActiveSupport::Deprecation.deprecate_methods(@klass, :old_method, deprecator: @deprecator) + other_deprecator = ActiveSupport::Deprecation.new + other_deprecator.deprecate_methods(@klass, :old_method, deprecator: @deprecator) assert_deprecated(/old_method .* MyGem next-release/, @deprecator) do - assert_equal @klass.new.new_method, @klass.new.old_method + assert_not_deprecated(other_deprecator) do + assert_equal @klass.new.new_method, @klass.new.old_method + end end end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index f05882fdb0e..4a5d7ab2179 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -39,12 +39,26 @@ class DeprecationTest < ActiveSupport::TestCase end end + test "assert_deprecated is deprecated without a deprecator" do + assert_deprecated(ActiveSupport.deprecator) do + assert_deprecated do + ActiveSupport::Deprecation._instance.warn + end + end + end + test "assert_not_deprecated" do assert_not_deprecated(@deprecator) do 1 + 1 end end + test "assert_not_deprecated is deprecated without a deprecator" do + assert_deprecated(ActiveSupport.deprecator) do + assert_not_deprecated { } + end + end + test "collect_deprecations returns the return value of the block and the deprecations collected" do result = collect_deprecations(@deprecator) do @deprecator.warn @@ -55,6 +69,12 @@ class DeprecationTest < ActiveSupport::TestCase assert_match "DEPRECATION WARNING:", result.last.sole end + test "collect_deprecations is deprecated without a deprecator" do + assert_deprecated(ActiveSupport.deprecator) do + collect_deprecations { } + end + end + test "Module::deprecate" do klass = Class.new(Deprecatee) klass.deprecate :zero, :one, :multi, deprecator: @deprecator @@ -88,11 +108,28 @@ class DeprecationTest < ActiveSupport::TestCase end end + test "Module::deprecate without a deprecator is deprecated" do + klass = Class.new(Deprecatee) + _, deprecations = collect_deprecations(ActiveSupport.deprecator) do + klass.deprecate :zero + end + assert_match "Module.deprecate without a deprecator is deprecated", deprecations.sole + assert_deprecated(/zero is deprecated/, ActiveSupport::Deprecation._instance) do + klass.new.zero + end + end + test "DeprecatedObjectProxy" do deprecated_object = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, ":bomb:", @deprecator) assert_deprecated(/:bomb:/, @deprecator) { deprecated_object.to_s } end + test "DeprecatedObjectProxy without a deprecator is deprecated" do + assert_deprecated(ActiveSupport.deprecator) do + ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, ":bomb:") + end + end + test "nil behavior is ignored" do @deprecator.behavior = nil assert_deprecated("fubar", @deprecator) { @deprecator.warn("fubar") } @@ -265,6 +302,12 @@ class DeprecationTest < ActiveSupport::TestCase assert_equal instance.foo_bar.inspect, fubar_inspected end + test "DeprecatedInstanceVariableProxy without a deprecator is deprecated" do + assert_deprecated(ActiveSupport.deprecator) do + ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(Deprecatee.new, :foobar, "@fubar") + end + end + test "DeprecatedConstantProxy" do proxy = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("FUBAR", "Undeprecated::Foo::BAR", @deprecator) @@ -292,6 +335,12 @@ class DeprecationTest < ActiveSupport::TestCase end end + test "DeprecatedConstantProxy without a deprecator is deprecated" do + assert_deprecated(ActiveSupport.deprecator) do + ActiveSupport::Deprecation::DeprecatedConstantProxy.new("Fuu", "Undeprecated::Foo") + end + end + test "deprecate_constant" do legacy = Module.new { def self.name; "Legacy"; end } legacy.include ActiveSupport::Deprecation::DeprecatedConstantAccessor @@ -315,6 +364,13 @@ class DeprecationTest < ActiveSupport::TestCase end end + test "deprecate_constant is deprecated without a deprecator" do + legacy = Module.new.include(ActiveSupport::Deprecation::DeprecatedConstantAccessor) + assert_deprecated("DeprecatedConstantAccessor.deprecate_constant without a deprecator is deprecated", ActiveSupport.deprecator) do + legacy.deprecate_constant "OLD", "NEW" + end + end + test "assert_deprecated raises when no deprecation warning" do assert_raises(Minitest::Assertion) do assert_deprecated(@deprecator) { 1 + 1 } @@ -328,8 +384,8 @@ class DeprecationTest < ActiveSupport::TestCase end test "assert_deprecated without match argument" do - assert_deprecated do - ActiveSupport::Deprecation.warn + assert_deprecated(@deprecator) do + @deprecator.warn end end @@ -703,6 +759,180 @@ class DeprecationTest < ActiveSupport::TestCase end end + test "warn delegator is deprecated" do + _, deprecations = collect_deprecations(ActiveSupport.deprecator) do + ActiveSupport::Deprecation.warn "foo" + end + assert_equal 2, deprecations.size + assert_match("foo", deprecations.first) + assert_match("use your own Deprecation object instead", deprecations.last) + end + + test "deprecate_methods delegator is deprecated" do + assert_deprecated("use your own Deprecation object instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.deprecate_methods(Deprecatee) + end + end + + test "silence delegator is deprecated" do + assert_deprecated("use Rails.application.deprecators.silence instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.silence { } + end + end + + test "silence delegator is deprecated and delegates to the application's deprecators when available" do + with_rails_application_deprecators do + assert_deprecated("use Rails.application.deprecators.silence instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.silence do + assert_not_deprecated(@deprecator) do + @deprecator.warn + end + end + end + end + end + + test "allow delegator is deprecated" do + assert_deprecated("use Rails.application.deprecators[framework].allow", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.allow { } + end + end + + test "behavior delegators are deprecated" do + old_behavior = ActiveSupport::Deprecation._instance.behavior + assert_deprecated("use Rails.application.deprecators[framework].behavior", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.behavior + end + assert_deprecated("use Rails.application.deprecators.behavior= instead", ActiveSupport.deprecator) do + # we have to keep the same behavior for assert_deprecated to work + ActiveSupport::Deprecation.behavior = ActiveSupport.deprecator.behavior + end + ensure + ActiveSupport::Deprecation._instance.behavior = old_behavior + end + + test "behavior= delegator is deprecated and delegates to the application's deprecators when available" do + with_rails_application_deprecators do + called = false + assert_deprecated("use Rails.application.deprecators.behavior= instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.behavior = ->(*) { called = true } + @deprecator.warn + end + assert called + end + end + + test "disallowed_behavior delegators are deprecated" do + old_behavior = ActiveSupport::Deprecation._instance.disallowed_behavior + assert_deprecated("use Rails.application.deprecators[framework].disallowed_behavior", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.disallowed_behavior + end + assert_deprecated("use Rails.application.deprecators.disallowed_behavior= instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.disallowed_behavior = ->(*) { } + end + ensure + ActiveSupport::Deprecation._instance.disallowed_behavior = old_behavior + end + + test "disallowed_behavior= delegators is deprecated and delegates to the application's deprecators when available" do + with_rails_application_deprecators do + called = false + assert_deprecated("use Rails.application.deprecators.disallowed_behavior= instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.disallowed_behavior = ->(*) { called = true } + end + @deprecator.disallowed_warnings = :all + @deprecator.warn + assert called + end + end + + test "debug delegators are deprecated" do + old_debug = ActiveSupport::Deprecation._instance.debug + assert_deprecated("use Rails.application.deprecators[framework].debug", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.debug + end + assert_deprecated("use Rails.application.deprecators.debug= instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.debug = true + end + ensure + ActiveSupport::Deprecation._instance.debug = old_debug + end + + test "debug= delegator is deprecated and delegates to the application's deprecators when available" do + with_rails_application_deprecators do + assert_deprecated("use Rails.application.deprecators.debug= instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.debug = true + end + assert @deprecator.debug + end + end + + test "silenced delegators are deprecated" do + old_silenced = ActiveSupport::Deprecation._instance.silenced + assert_deprecated("use Rails.application.deprecators[framework].silenced", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.silenced + end + assert_deprecated("use Rails.application.deprecators.silenced= instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.silenced = false + end + ensure + ActiveSupport::Deprecation._instance.silenced = old_silenced + end + + test "silenced= delegator is deprecated and delegates to the application's deprecators when available" do + with_rails_application_deprecators do + assert_deprecated("use Rails.application.deprecators.silenced= instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.silenced = true + end + assert @deprecator.silenced + end + end + + test "disallowed_warnings delegators are deprecated" do + old_disallowed_warnings = ActiveSupport::Deprecation._instance.disallowed_warnings + assert_deprecated("use Rails.application.deprecators[framework].disallowed_warnings", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.disallowed_warnings + end + assert_deprecated("use Rails.application.deprecators.disallowed_warnings= instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.disallowed_warnings = [] + end + ensure + ActiveSupport::Deprecation._instance.disallowed_warnings = old_disallowed_warnings + end + + test "disallowed_warnings= delegator is deprecated and delegates to the application's deprecators when available" do + with_rails_application_deprecators do + assert_deprecated("use Rails.application.deprecators.disallowed_warnings= instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.disallowed_warnings = :all + end + assert_equal :all, @deprecator.disallowed_warnings + end + end + + test "gem_name delegators are deprecated" do + old_gem_name = ActiveSupport::Deprecation._instance.gem_name + assert_deprecated("use your own Deprecation object instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.gem_name + end + assert_deprecated("use your own Deprecation object instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.gem_name = "MyGem" + end + ensure + ActiveSupport::Deprecation._instance.gem_name = old_gem_name + end + + test "deprecation_horizon delegators are deprecated" do + old_deprecation_horizon = ActiveSupport::Deprecation._instance.deprecation_horizon + assert_deprecated("use your own Deprecation object instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.deprecation_horizon + end + assert_deprecated("use your own Deprecation object instead", ActiveSupport.deprecator) do + ActiveSupport::Deprecation.deprecation_horizon = "2.0" + end + ensure + ActiveSupport::Deprecation._instance.deprecation_horizon = old_deprecation_horizon + end + test "warn deprecation skips the internal caller locations" do @deprecator.behavior = ->(_, callstack, *) { @callstack = callstack } method_that_emits_deprecation(@deprecator) @@ -715,6 +945,13 @@ class DeprecationTest < ActiveSupport::TestCase deprecator.warn end + def with_rails_application_deprecators(&block) + application = Struct.new(:deprecators).new(ActiveSupport::Deprecation::Deprecators.new) + rails = Struct.new(:application).new(application) + rails.application.deprecators[:deprecator] = @deprecator + stub_const(Object, :Rails, rails, &block) + end + def deprecator_with_messages klass = Class.new(ActiveSupport::Deprecation) deprecator = klass.new diff --git a/guides/Rakefile b/guides/Rakefile index 59cbe79869c..9d684d82bb7 100644 --- a/guides/Rakefile +++ b/guides/Rakefile @@ -12,8 +12,7 @@ namespace :guides do desc "Generate .mobi file" task :kindle do - require "active_support/deprecation" - ActiveSupport::Deprecation.warn("The guides:generate:kindle rake task is deprecated and will be removed in 7.2. Run rake guides:generate:epub instead") + warn("DEPRECATION WARNING: The guides:generate:kindle rake task is deprecated and will be removed in 7.2. Run rake guides:generate:epub instead.") Rake::Task["guides:generate:epub"].invoke end diff --git a/railties/lib/rails/deprecator.rb b/railties/lib/rails/deprecator.rb index 320b00b6938..2ac56b10434 100644 --- a/railties/lib/rails/deprecator.rb +++ b/railties/lib/rails/deprecator.rb @@ -2,6 +2,6 @@ module Rails def self.deprecator # :nodoc: - ActiveSupport::Deprecation.instance + @deprecator ||= ActiveSupport::Deprecation.new end end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index d9c67ac11ab..7037f678247 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -4008,7 +4008,7 @@ module ApplicationTests test "Rails.application.deprecators includes framework deprecators" do app "production" - assert_includes Rails.application.deprecators.each, ActiveSupport::Deprecation.instance + assert_includes Rails.application.deprecators.each, ActiveSupport::Deprecation._instance assert_equal ActionCable.deprecator, Rails.application.deprecators[:action_cable] assert_equal AbstractController.deprecator, Rails.application.deprecators[:action_controller] assert_equal ActionController.deprecator, Rails.application.deprecators[:action_controller]