Динамически определять именованные классы в Ruby
Я пишу внутренний DSL в Ruby. Для этого мне нужно программно создать именованные классы и вложенные классы. Каков наилучший способ сделать это? Я считаю, что есть два способа сделать это:
- Используйте
Class.new
для создания анонимного класса, затем используйте define_method
для добавления к нему методов и, наконец, вызовите const_set
, чтобы добавить их в качестве имен констант в какое-то пространство имен.
- Используйте
eval
Я протестировал первый способ, и он сработал, но, будучи новым для Ruby, я не уверен, что правильное размещение классов как констант.
Есть ли другие, лучшие способы? Если нет, то какое из указанных выше предпочтительнее?
Ответы
Ответ 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)