【问题标题】:Dynamic class definition with block带块的动态类定义
【发布时间】:2013-01-16 17:23:02
【问题描述】:

所以我遇到了这种情况,我想以编程方式定义一堆类。我在下面使用的方法工作正常,除了我从这里继承的第 3 方类不喜欢该类是匿名定义的事实(基本上,它没有它需要的信息,即. 'inherited' 钩子中的类名,在我有机会将匿名类设置为常量之前)。

['one', 'two', 'three'].each do |model|
    cls = Class.new(ThirdPartyClass) do
        define_method :model do
            model
        end
    end
    ThirdPartyClass.const_set(model.capitalize, cls)
end

我可以简单地使用 eval 并定义类,如下所示:

['one', 'two', 'three'].each do |model|
    eval "class ThirdPartyClass::#{model.capitalize} < ThirdPartyClass; ...; end"
end

但我不喜欢它,因为它是杂乱的字符串插值。从美学上讲,基于块的方法看起来“更好”。

有什么方法可以使用基于块的语法非匿名地定义该类(即从一开始就使用名称),还是我注定要留在 eval 混乱的字符串输入领域?

【问题讨论】:

  • 奇怪的是,你希望一个类在同一个类的名称空间内并且是同一个类的子类。
  • 没错,但是命名空间的事情与这个特定的问题是正交的(至少我是这么认为的)。如果您好奇,我使用的是 classy_enum gem,按照该 gem 的约定,枚举值必须位于主枚举类的命名空间内。它产生的枚举语法有点类似于其他语言中的语法。

标签: ruby metaprogramming


【解决方案1】:

Class#inherited 回调在创建类时被调用。当实例化一个匿名类时,这总是在类可以被分配给常量之前,给它分配一个名字。我看不到任何解决此问题的方法。您可以最小化字符串 eval 中的代码量,方法是使用 eval 使用特殊的类语法创建一个空类,使名称在 inherited 之前可见,然后以块形式跟进 class_eval 以定义你的方法。

class Super
  def self.inherited(child)
    puts "#{self.name} inherited by #{child.inspect} named '#{child.name}'"
  end
end

# Your way, inherited can't see name
['one', 'two', 'three'].each do |model|
  klassname = model.capitalize
  klass = Class.new(Super) do
    puts "defining #{model} inheriting from Super"
    define_method :model do
      model
    end
  end
  Super.const_set(klassname, klass)
end

# this way inherited sees name because we use special class definition syntax in minimal string eval
['four', 'five', 'six'].each do |model|
  klassname = model.capitalize

  eval %Q{
    class Super::#{klassname} < Super
      puts %Q[defining Super::#{klassname} inheriting from Super]
    end
  }

  Super.const_get(klassname).class_eval do
    puts "defining methods for Super::#{klassname} inheriting from Super"
    define_method :model do
      model
    end
  end
end

# produces:
Super inherited by #<Class:0x0000010084c988> named ''
defining one inheriting from Super
Super inherited by #<Class:0x0000010084c640> named ''
defining two inheriting from Super
Super inherited by #<Class:0x0000010084c320> named ''
defining three inheriting from Super
Super inherited by Super::Four named 'Super::Four'
defining Super::Four inheriting from Super
defining methods for Super::Four inheriting from Super
Super inherited by Super::Five named 'Super::Five'
defining Super::Five inheriting from Super
defining methods for Super::Five inheriting from Super
Super inherited by Super::Six named 'Super::Six'
defining Super::Six inheriting from Super
defining methods for Super::Six inheriting from Super

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-06-11
    • 2016-09-06
    • 2011-11-26
    • 2011-11-25
    • 2012-07-11
    • 2014-08-13
    • 2021-11-30
    • 2011-12-07
    相关资源
    最近更新 更多