首页 > 解决方案 > 在 Ruby 中,是否可以在超类的子类中定义常量?

问题描述

假设我有一个由许多子类扩展的 Ruby 类。我希望这些子类中的每一个都有一个名为FRIENDLY_NAME. 显然,我可以编辑每个类并添加常量。但是,如果它没有在子类中指定,有没有办法在超类的子类中定义一个常量?

例如,当尚未定义时,也许我可以将每个子类设置FRIENDLY_NAME为子类的类名解调和下划线。FRIENDLY_NAME

标签: rubyclassconstants

解决方案


直到关于TracePoint

为了扩展我原来的问题,我有一个我想多次扩展的超类:

class BaseClass
end

我希望该类的所有扩展版本都有一个FRIENDLY_NAME常量。当在子类中明确指定时,这将是常量的值。例如:

class Subclass1 < BaseClass
  FRIENDLY_NAME = 'Bob seems like a friendly name!'
end

因此:

pry(main)> Subclass1::FRIENDLY_NAME
=> "Bob seems like a friendly name!"

但是,也许我有另一个没有定义常量的子类。例如:

class Subclass2 < BaseClass
end

这就是我在没有常数的情况下得到的(显然):

pry(main)> Subclass2::FRIENDLY_NAME
NameError: uninitialized constant Subclass2::FRIENDLY_NAME
from /bundle/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/active_support.rb:79:in `block in load_missing_constant'
Caused by NameError: uninitialized constant Subclass2::FRIENDLY_NAME
from /bundle/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/active_support.rb:60:in `block in load_missing_constant'

如何添加该常数?您可以添加self.inherited到超类。每次扩展超类时都会调用此方法。Module在named上还有一个方法const_set,所以我可以像这样自动设置孩子的常量:

class BaseClass
  def self.inherited(child)
    child.const_set(:FRIENDLY_NAME, child.name)
  end
end

这适用于Subclass2

pry(main)> Subclass2::FRIENDLY_NAME
=> "Subclass2"

但是,它会产生丑陋的警告Subclass1

(pry):10: warning: already initialized constant Subclass1::FRIENDLY_NAME
(pry):3: warning: previous definition of FRIENDLY_NAME was here

最初我尝试使用该const_defined?方法来检查是否FRIENDLY_NAME已经定义了常量。问题是,在子类中定义常量之前self.inherited调用该方法。基本上,只要 Ruby 看到它运行.< BaseClassself.inherited

最终我发现了这个关于如何在定义类后执行代码的问题。答案是使用TracePoint.

TracePoint是一个允许我们在代码中处理事件的类。这些可能是在执行一行代码时,当我们调用一个方法时,当引发错误时等等。我们关心的是:end事件,它发生在类或模块定义完成时。

知道了这一点,我们可以self.inherited像这样更新我们的方法:

class BaseClass
  def self.inherited(child)
    TracePoint.trace(:end) do |t|
      if child == t.self
        unless child.const_defined?(:FRIENDLY_NAME)
          child.const_set(:FRIENDLY_NAME, child.code)
        end
      end
    end
  end
end

现在,两者Subclass1都有Subclass2一个FRIENDLY_NAME常数,但都不会产生警告!


推荐阅读