Подтвердить что ты не робот

Динамически определять именованные классы в Ruby

Я пишу внутренний DSL в Ruby. Для этого мне нужно программно создать именованные классы и вложенные классы. Каков наилучший способ сделать это? Я считаю, что есть два способа сделать это:

  • Используйте Class.new для создания анонимного класса, затем используйте define_method для добавления к нему методов и, наконец, вызовите const_set, чтобы добавить их в качестве имен констант в какое-то пространство имен.
  • Используйте eval

Я протестировал первый способ, и он сработал, но, будучи новым для Ruby, я не уверен, что правильное размещение классов как констант.

Есть ли другие, лучшие способы? Если нет, то какое из указанных выше предпочтительнее?

4b9b3361

Ответ 1

Если вы хотите создать класс с динамическим именем, вам нужно будет сделать практически то, что вы сказали. Однако вам не нужно использовать define_method. Вы можете просто передать блок в Class.new, в котором вы инициализируете класс. Это семантически идентично содержимому class/end.

Помните const_set, чтобы быть добросовестным из получателя (self) в этой области. Если вы хотите, чтобы класс был определен глобально, вам нужно будет вызвать const_set в модуле TopLevel (который по-разному изменяется по имени и деталям Ruby).

a_new_class = Class.new(Object) do
  attr_accessor :x

  def initialize(x)
    print #{self.class} initialized with #{x}"
    @x = x
  end
end

SomeModule.const_set("ClassName", a_new_class)

c = ClassName.new(10)

...

Ответ 2

Вам не нужно использовать const_set. Возвращаемое значение Class.new может быть присвоено константа и блок Class.new - class_eval.

class Ancestor; end
SomeClass = Class.new(Ancestor) do
  def initialize(var)
     print "#{self.class} initialized with #{var}"
  end
end
=> SomeClass
SomeClass.new("foo")
# SomeClass initialized with foo=> #<SomeClass:0x668b68>

Ответ 3

Должно быть как

a_new_class = Class.new(Object) do

attr_accessor :x

 def initialize(x)
  @x = x
 end
end

SomeModule = Module.new
SomeModule.const_set("ClassName", a_new_class)

c = SomeModule::ClassName.new(10)