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

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

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

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

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


person Little Bobby Tables    schedule 05.07.2011    source источник
comment
eval лучше избегать. stackoverflow.com/questions/637421/is- eval-должен-быть-противным   -  person Mark Thomas    schedule 05.07.2011


Ответы (3)


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

Помните с 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)

...
person Dylan Lukes    schedule 05.07.2011
comment
Я также должен упомянуть, что имена классов по своей сути являются константами. Они определены как константы модуля, в котором они содержатся. - person Dylan Lukes; 05.07.2011
comment
Не могли бы вы уточнить название модуля верхнего уровня? - person Little Bobby Tables; 06.07.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>
person Julik    schedule 05.07.2011
comment
При этом не создается класс с динамическим именем. SomeClass определяется статически. - person Dylan Lukes; 05.07.2011
comment
Не совсем. Постоянное имя будет выведено, когда класс для чего-то используется. gist.github.com/1064909 - person Julik; 05.07.2011
comment
в вашем примере вы определяете свой новый класс для SomeClass. Пример, который вы только что вставили, противоречит вашему утверждению, что вам действительно не нужно использовать const_set. Вам нужно использовать его для привязки чего-либо к константе модуля. - person Dylan Lukes; 05.07.2011
comment
В этом смысле вы правы. Я думал, что вы имели в виду класс с динамическим именем как == anon class. - person Julik; 05.07.2011

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

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)
person msroot    schedule 24.06.2014