From cb0acdae88bb45911ffcce2236b197586f5c1d9e Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 13 Jul 2024 18:41:48 +0200 Subject: [PATCH] Improvements to deprecate_constant --- .../deprecation/constant_accessor.rb | 70 ++++++++++++------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/activesupport/lib/active_support/deprecation/constant_accessor.rb b/activesupport/lib/active_support/deprecation/constant_accessor.rb index 8763c6d5a6..0eaaabb8a4 100644 --- a/activesupport/lib/active_support/deprecation/constant_accessor.rb +++ b/activesupport/lib/active_support/deprecation/constant_accessor.rb @@ -2,28 +2,6 @@ module ActiveSupport class Deprecation - # DeprecatedConstantAccessor transforms a constant into a deprecated one by - # hooking +const_missing+. - # - # It takes the names of an old (deprecated) constant and of a new constant - # (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 - # etc. - # - # 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) - # include ActiveSupport::Deprecation::DeprecatedConstantAccessor - # 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. - # (Backtrace information…) - # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"] module DeprecatedConstantAccessor def self.included(base) require "active_support/inflector/methods" @@ -39,9 +17,53 @@ def const_missing(missing_const_name) super end - def deprecate_constant(const_name, new_constant, deprecator:, message: nil) + # Provides a way to rename constants with a deprecation cycle in which + # both the old and new names work, but using the old one prints a + # deprecation message. + # + # In order to rename A::B to C::D, you need to delete the + # definition of A::B and declare the deprecation in +A+: + # + # require "active_support/deprecation" + # + # module A + # include ActiveSupport::Deprecation::DeprecatedConstantAccessor + # + # deprecate_constant "B", "C::D", deprecator: ActiveSupport::Deprecation.new + # end + # + # The first argument is a constant name (no colons). It is the name of + # the constant you want to deprecate in the enclosing class or module. + # + # The second argument is the constant path of the replacement. That + # has to be a full path even if the replacement is defined in the same + # namespace as the deprecated one was. + # + # In both cases, strings and symbols are supported. + # + # The +deprecator+ keyword argument is the object that will print the + # deprecation message, an instance of ActiveSupport::Deprecation. + # + # With that in place, client code referencing A::B will see + # + # DEPRECATION WARNING: A::B is deprecated! Use C::D instead. + # (called from ...) + # + # The message can be customized with the optional +message+ keyword + # argument. + # + # For this to work, a +const_missing+ hook is installed. When client + # code references the deprecated constant, the callback prints the + # message and constantizes the replacement. + # + # Caveat: If the deprecated constant name is reachable in a different + # namespace and Ruby constant lookup finds it, the hook won't be + # called and the deprecation won't work as intended. This may happen, + # for example, if an ancestor of the enclosing namespace has a + # constant with the same name. This is an unsupported edge case. + def deprecate_constant(old_constant_name, new_constant_path, deprecator:, message: nil) 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 } + class_variable_get(:@@_deprecated_constants)[old_constant_name.to_s] = { new: new_constant_path, message: message, deprecator: deprecator } end end base.singleton_class.prepend extension