跳至内容 跳至搜索
方法
C
D
I

类公共方法

included(base)

# File activesupport/lib/active_support/deprecation/constant_accessor.rb, line 6
def self.included(base)
  require "active_support/inflector/methods"

  extension = Module.new do
    def const_missing(missing_const_name)
      if class_variable_defined?(:@@_deprecated_constants)
        if (replacement = class_variable_get(:@@_deprecated_constants)[missing_const_name.to_s])
          replacement[:deprecator].warn(replacement[:message] || "#{name}::#{missing_const_name} is deprecated! Use #{replacement[:new]} instead.", caller_locations)
          return ActiveSupport::Inflector.constantize(replacement[:new].to_s)
        end
      end
      super
    end

    # 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 <tt>A::B</tt> to <tt>C::D</tt>, you need to delete the
    # definition of <tt>A::B</tt> 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, references to <tt>A::B</tt> still work, they
    # evaluate to <tt>C::D</tt> now, and trigger a deprecation warning:
    #
    #   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)[old_constant_name.to_s] = { new: new_constant_path, message: message, deprecator: deprecator }
    end
  end
  base.singleton_class.prepend extension
end

实例公共方法

const_missing(missing_const_name)

# File activesupport/lib/active_support/deprecation/constant_accessor.rb, line 10
def const_missing(missing_const_name)
  if class_variable_defined?(:@@_deprecated_constants)
    if (replacement = class_variable_get(:@@_deprecated_constants)[missing_const_name.to_s])
      replacement[:deprecator].warn(replacement[:message] || "#{name}::#{missing_const_name} is deprecated! Use #{replacement[:new]} instead.", caller_locations)
      return ActiveSupport::Inflector.constantize(replacement[:new].to_s)
    end
  end
  super
end

deprecate_constant(old_constant_name, new_constant_path, deprecator:, message: nil)

提供了一种重命名常量的方式,并带有弃用周期,在此期间旧名称和新名称都有效,但使用旧名称会打印弃用消息。

为了将 A::B 重命名为 C::D,您需要删除 A::B 的定义,并在 A 中声明弃用。

require "active_support/deprecation"

module A
  include ActiveSupport::Deprecation::DeprecatedConstantAccessor

  deprecate_constant "B", "C::D", deprecator: ActiveSupport::Deprecation.new
end

第一个参数是常量名(不带冒号)。它是您想在封闭的类或模块中弃用的常量的名称。

第二个参数是替换项的常量路径。这必须是一个完整的路径,即使替换项定义在与被弃用的常量相同的命名空间中。

在这两种情况下,字符串和符号都支持。

deprecator 关键字参数是用于打印弃用消息的对象,它是 ActiveSupport::Deprecation 的一个实例。

有了这个,对 A::B 的引用仍然有效,它们现在会评估为 C::D,并触发一个弃用警告。

DEPRECATION WARNING: A::B is deprecated! Use C::D instead.
(called from ...)

可以使用可选的 message 关键字参数来自定义消息。

为了实现这一点,安装了一个 const_missing 钩子。当客户端代码引用被弃用的常量时,回调会打印消息并常量化替换项。

注意:如果被弃用的常量名可以在不同的命名空间中访问,并且 Ruby 常量查找找到了它,那么钩子将不会被调用,弃用也不会按预期工作。例如,如果封闭命名空间的一个祖先拥有同名的常量,可能会发生这种情况。这是一个不受支持的边缘情况。

# File activesupport/lib/active_support/deprecation/constant_accessor.rb, line 65
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)[old_constant_name.to_s] = { new: new_constant_path, message: message, deprecator: deprecator }
end