2012-02-22 21 views
9

Estoy tratando de crear una nueva clase, sin saber el nombre de la clase hasta que se supone que se creo.Creando una clase dinámicamente

Algo como esto;

variable = "ValidClassName" 

     class variable 

     end 

Test = ValidClassName.new 

Si es posible, También apreciaría consejos som sobre cómo agregar dinámicamente atributos (y métodos) a esta nueva clase.

voy a estar retreiving 'configuración' para la clase, y que se verá algo como esto:

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

pero no debe ser diseñado sólo para ese archivo explícita, los atributos podrían diferir en el número final tipo.

cual al final va a generar una clase que debe ser algo como:

class Person 
    def initialize(name, age) 

     @name_out = name 
     @age_out = age 
    end 

end 

Ayuda?

+1

¿Quieres crear el código fuente para una clase? o ¿quieres generar la fuente y decirle a ruby ​​que compile/cargue la clase en tiempo de ejecución? – ardnew

+1

Por curiosidad, ¿qué problema estás resolviendo? ¿Cómo planeas utilizar estas clases creadas dinámicamente? – ctcherry

+0

Se supone que mi clase actúa como un 'marco', una vez creado mal uso para instanciar objetos (siempre que cumplan con las demandas, establecidas por la clase) desde un archivo yaml. El YAML posee un grupo de "personas", y algunas de ellas tienen atributos que coinciden con los requisitos. – BSG

Respuesta

23

Una clase gana su nombre cuando se le asigna a una constante. Por lo tanto, es fácil de hacer de manera genérica con const_set.

Por ejemplo, digamos que desea utilizar Struct para construir una clase con algunos atributos, puede:

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> 

heredar de otra clase, reemplace la Struct.new por Class.new(MyBaseClass), dicen:

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> 
+0

Estoy obteniendo un error al declarar el klass, el nombre debería ser una constante, así que escribí en mayúscula klass en Klass en 2, 3 líneas y ahora está funcionando. – tebayoso

+0

Oh, claro. Ejemplo modificado. –

6

Su código sería algo parecido a esto:

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) 

... o usted podría utilizar eval:

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

Lo siento, estoy fuera de mi alcance aquí. ¿Podría tratar de explicarlo como si tuviera tres años ...? : p – BSG

+1

No estoy seguro de que esto se vuelva mucho más fácil. El segundo caso es probablemente más fácil de entender: 'eval' toma una cadena y la evalúa * en el momento de la llamada * como si fuera un código de Ruby. Entonces, usted crea una cadena con el código fuente para su clase dinámica en tiempo de ejecución, luego la evalúa. El primer caso crea un nuevo objeto de clase, le hace cosas para crear los métodos deseados, etc., y luego le da un nombre, que en Ruby es lo mismo que asignar el objeto de clase a una constante. –

Cuestiones relacionadas