在运行时继承新的类方法

时间:2012-02-01 16:43:51

标签: ruby inheritance metaprogramming

在Ruby中,<ClassName>.constants对于检查类非常有用:

> Numeric.constants(false)
=> [:KILOBYTE, :MEGABYTE, :GIGABYTE, :TERABYTE, :PETABYTE, :EXABYTE]
> Object.constants(false)
=> [:Object, :Module, ...]

这在Module

中定义
> Object.method(:constants)
=> #<Method: Class(Module)#constants>

我想添加另一种方法来打印包含所有常量及其值的哈希。这是迄今为止的结果:

def Module.constants_hash(inherit=true)
    self.constants(inherit).inject({}) do |hash, constant|
        hash[constant] = self::const_get(constant)
        hash
    end
end

这适用于Module(虽然它没有常量,因此结果只是一个空哈希),但它没有被继承

> Object.constants_hash(false)
NoMethodError: undefined method `constants_hash' for Object:Class
from (pry):117:in `<main>'

我当然可以将代码中的类名更改为例如Object使调用工作,但是是否可以使所有依赖模块继承新方法?换句话说,是否可以在运行时添加方法,然后由require修改后的类继承?

我不会过载原始Module.constants,以避免更改API。

2 个答案:

答案 0 :(得分:4)

ObjectClass类型的实例。 Class类继承自Module。要使Object.constants_hash生效,您需要定义ClassModule类的实例方法:

class Module
  def constants_hash(inherit=true)
      self.constants(inherit).inject({}) do |hash, constant|
          hash[constant] = self::const_get(constant)
          hash
      end
  end
end

在您的代码中,您只需将constants_hash添加到Module实例(类型Class)的单件类中,这就是您无法获得的原因预期的结果。

答案 1 :(得分:0)

您可以通过在模块中声明此方法,然后根据需要将其混合到每个上下文中来获得更好的运气:

module ConstantsHash
  def constants_hash(inherit = true)
    Hash[
      self.constants(inherit).collect do |constant|
        [ constant, const_get(constant) ]
      end
    ]
  end
end

Module.extend(ConstantsHash)
Object.extend(ConstantsHash)

puts Object.constants_hash.inspect

请注意,使用Hash[]代替inject({ })似乎在这种情况下效果更好。