在类方法中使用重写的类常量

时间:2016-02-15 06:15:57

标签: ruby

要覆盖子类中的类常量,可以执行以下操作:

class Foo
  CONST = [:foo, :baz]

  def self.const
    self::CONST
  end

end

class Bar < Foo
  CONST = [:foo, :bar]
end

print Foo.const # [:foo, :baz]
print Bar.const # [:foo, :bar]

这可以按预期工作。问题是当我尝试从类方法中调用它时,例如在使用define_method

class Foo
  CONST = [:foo, :baz]

  def self.const
    self::CONST
  end

  self.const.each do |c|
    define_method("#{c}?") {
      "#{c} exists"
    }
  end
end


foo = Foo.new
print foo.baz? # baz exists.

bar = Bar.new
print bar.bar? # undefined method `bar?'

如何重写类常量,以便在这种情况下定义正确的方法bar?,而不必复制子类中的代码?有没有使用类变量而不是类常量的DRY方法呢?

1 个答案:

答案 0 :(得分:3)

因为define_method在词法范围内运行,即它在Foo类定义的主体中是内联的,所以没有任何东西可以使它在Bar中运行。

class Foo
  CONST = [:foo, :baz]

  def self.define_const_methods(const)
    const.each do |c|
      define_method("#{c}?") { "#{c} exists" }
    end
  end

  define_const_methods(CONST)
end

class Bar < Foo
  CONST = [:foo, :bar]
  define_const_methods(CONST)
end

这应该可以解决问题。所以你在它的词法范围内调用Foo类末尾的define_const_methods。而且你也可以在任何继承它的类上调用它。继承类应该找到它自己的常量版本。

但这非常难看,所以你可以完全省去常量,只需使用define_const_methods来定义它们。就像ActiveRecord定义关联方法(has_one,has_many等)时一样。那么你可以把它简化为;

class Foo
  def self.define_my_methods(meths)
    meths.each do |c|
      define_method("#{c}?") { "#{c} exists" }
    end
  end

  define_my_methods [:foo, :baz]
end

class Bar < Foo
  define_my_methods [:foo, :bar]
end