带块的动态类定义

els*_*udo 7 ruby metaprogramming

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

['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
Run Code Online (Sandbox Code Playgroud)

我可以简单地使用eval并将类定义为:

['one', 'two', 'three'].each do |model|
    eval "class ThirdPartyClass::#{model.capitalize} < ThirdPartyClass; ...; end"
end
Run Code Online (Sandbox Code Playgroud)

但我不喜欢它,因为那时它是凌乱的字符串插值.基于块的方法似乎"更好",从美学角度讲.

有没有什么方法可以使用基于块的语法非匿名地定义该类(即从头开始使用名称),或者我注定要留在eval凌乱的字符串输入的土地上?

dbe*_*hur 0

Class#inherited创建类时会调用回调。实例化匿名类时,这始终是在将该类分配给常量之前,为其分配一个名称。我看不出有什么办法可以解决这个问题。您可以通过使用 eval 创建一个空类(使用特殊的类语法使名称在 before 之前可见)来最小化inherited字符串 eval 中的代码量,然后以块形式使用 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
Run Code Online (Sandbox Code Playgroud)