Динамически определять именованные классы в Ruby - PullRequest
25 голосов
/ 05 июля 2011

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

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

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

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

Ответы [ 3 ]

26 голосов
/ 05 июля 2011

Если вы хотите создать класс с динамическим именем, вам придется делать почти то же, что вы сказали. Однако вам не нужно использовать 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)

...
5 голосов
/ 05 июля 2011

Вам не нужно использовать 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>
4 голосов
/ 24 июня 2014

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

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)
...