Создание класса динамически - PullRequest
16 голосов
/ 23 февраля 2012

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

Что-то вроде этого;

    variable = "ValidClassName"

        class variable

        end

Test = ValidClassName.new

Если возможно, я также буду признателен за некоторые советы о том, как динамически добавлять атрибуты (и методы) в этот новый класс.

Я получу «настройки» длякласс, и они будут выглядеть примерно так:

title :Person
attribute :name, String
attribute :age, Fixnum

Но не следует рассчитывать на прием только этого явного файла, атрибуты могут отличаться по типу числа конца.

Что в концесгенерирует класс, который должен выглядеть примерно так:

class Person
   def initialize(name, age)

       @name_out = name
       @age_out = age
   end

end

Справка?

Ответы [ 2 ]

28 голосов
/ 23 февраля 2012

Класс получает свое имя, когда ему присваивается константа. Так что это легко сделать в общем виде с const_set.

Например, предположим, что вы хотите использовать Struct для создания класса с некоторыми атрибутами, вы можете:

name = "Person"
attributes = [:name, :age]

klass = Object.const_set name, Struct.new(*attributes)
# Now use klass or Person or const_get(name) to refer to your class:
Person.new("John Doe", 42) # => #<struct Person name="John Doe", age=42>

Чтобы наследовать от другого класса, замените Struct.new на Class.new(MyBaseClass), скажем:

class MyBaseClass; end

klass = Class.new(MyBaseClass) do
  ATTRIBUTES = attributes
  attr_accessor *ATTRIBUTES
  def initialize(*args)
    raise ArgumentError, "Too many arguments" if args.size > ATTRIBUTES.size
    ATTRIBUTES.zip(args) do |attr, val|
      send "#{attr}=", val
    end
  end
end
Object.const_set name, klass
Person.new("John Doe", 42) # => #<Person:0x007f934a975830 @name="John Doe", @age=42> 
8 голосов
/ 23 февраля 2012

Ваш код будет выглядеть примерно так:

variable = "SomeClassName"
klass = Class.new(ParentClass)
# ...maybe evaluate some code in the context of the new, anonymous class
klass.class_eval {  }
# ...or define some methods
klass.send(:title, :Person)
klass.send(:attribute, :name, String)
# Finally, name that class!
ParentClass.send(:const_set, variable, klass)

... или вы можете просто использовать eval:

eval <<DYNAMIC
  class #{name}
    title :Person
    attribute :name, String
    # ...or substitute other stuff in here.
  end
DYNAMIC
...