ruby - tecnicas - Definición dinámica de clase CON un nombre de clase
juegos para iniciar una clase (4)
¿Qué tal el siguiente código?
dynamic_name = "TestEval2"
class_string = """
class #{dynamic_name}
def method1
end
end
"""
eval(class_string)
dummy2 = Object.const_get(dynamic_name)
puts "dummy2: #{dummy2}"
Eval no devuelve el objeto Class de tiempo de ejecución, al menos en mi PC no lo hace. Use Object.const_get para obtener el objeto Class.
¿Cómo defino dinámicamente una clase en Ruby WITH a name?
Sé cómo crear una clase dinámicamente sin un nombre usando algo como:
dynamic_class = Class.new do
def method1
end
end
Pero no puedes especificar un nombre de clase. Quiero crear una clase dinámicamente con un nombre.
Aquí hay un ejemplo de lo que quiero hacer pero, por supuesto, en realidad no funciona.
(Tenga en cuenta que no estoy creando una instancia de una clase sino una definición de clase)
class TestEval
def method1
puts "name: #{self.name}"
end
end
class_name = "TestEval"
dummy = eval("#{class_name}")
puts "dummy: #{dummy}"
dynamic_name = "TestEval2"
class_string = """
class #{dynamic_name}
def method1
end
end
"""
dummy2 = eval(class_string)
puts "dummy2: #{dummy2}" # doesn''t work
Salida real:
dummy: TestEval
dummy2:
Salida deseada:
dummy: TestEval
dummy2: TestEval2
=============================================== ====
Respuesta: una solución totalmente dinámica que utiliza el método de sepp2k
dynamic_name = "TestEval2"
Object.const_set(dynamic_name, Class.new)
dummy2 = eval("#{dynamic_name}")
puts "dummy2: #{dummy2}"
El nombre de una clase es simplemente el nombre de la primera constante que se refiere a ella.
Es decir, si hago myclass = Class.new
y luego MyClass = myclass
, el nombre de la clase se convertirá en MyClass
. Sin embargo, no puedo hacer MyClass =
si no sé el nombre de la clase hasta el tiempo de ejecución.
Entonces, en su lugar, puede usar Module#const_set
, que establece dinámicamente el valor de una const. Ejemplo:
dynamic_name = "ClassName"
Object.const_set(dynamic_name, Class.new { def method1() 42 end })
ClassName.new.method1 #=> 42
He estado jugando con esto también. En mi caso, estaba intentando probar las extensiones de ActiveRecord :: Base. Necesitaba poder crear dinámicamente una clase, y como el registro activo busca una tabla basada en un nombre de clase, esa clase no puede ser anónima.
No estoy seguro de si esto ayuda a su caso, pero esto es lo que se me ocurrió:
test_model_class = Class.new(ActiveRecord::Base) do
def self.name
''TestModel''
end
attr_accessible :foo, :bar
end
En lo que respecta a ActiveRecord, la definición de self.name fue suficiente. Supongo que esto funcionará en todos los casos donde una clase no puede ser anónima.
(Acabo de leer la respuesta de sepp2k y estoy pensando que es mejor. Dejaré esto aquí de todos modos).
Sé que esta es una pregunta muy antigua, y algunos otros Rubyistas podrían rechazarme de la comunidad por esto, pero estoy trabajando en la creación de una gema de envoltura muy fina que envuelva un popular proyecto de Java con clases de rubí. Basado en la respuesta de @sepp2k, creé un par de métodos auxiliares porque tuve que hacer esto muchas, muchas veces en un proyecto. Tenga en cuenta que el espacio de nombres de estos métodos para que no estén contaminando un espacio de nombres de alto nivel como Object o Kernel.
module Redbeam
# helper method to create thin class wrappers easily within the given namespace
#
# @param parent_klass [Class] parent class of the klasses
# @param klasses [Array[String, Class]] 2D array of [class, superclass]
# where each class is a String name of the class to create and superclass
# is the class the new class will inherit from
def self.create_klasses(parent_klass, klasses)
parent_klass.instance_eval do
klasses.each do |klass, superklass|
parent_klass.const_set klass, Class.new(superklass)
end
end
end
# helper method to create thin module wrappers easily within the given namespace
#
# @param parent_klass [Class] parent class of the modules
# @param modules [Array[String, Module]] 2D array of [module, supermodule]
# where each module is a String name of the module to create and supermodule
# is the module the new module will extend
def self.create_modules(parent_klass, modules)
parent_klass.instance_eval do
modules.each do |new_module, supermodule|
parent_klass.const_set new_module, Module.new { extend supermodule }
end
end
end
end
Para utilizar estos métodos (tenga en cuenta que esto es JRuby):
module Redbeam::Options
Redbeam.create_klasses(self, [
[''PipelineOptionsFactory'', org.apache.beam.sdk.options.PipelineOptionsFactory]
])
Redbeam.create_modules(self, [
[''PipelineOptions'', org.apache.beam.sdk.options.PipelineOptions]
])
end
¿¿POR QUÉ??
Esto me permite crear una gema JRuby que utiliza el proyecto Java y permitiría que la comunidad de código abierto y yo decoremos estas clases en el futuro, según sea necesario. También crea un espacio de nombres más amigable para usar las clases. Como mi gema es una envoltura muy, muy delgada, tuve que crear muchas, muchas subclases y módulos para extender otros módulos.
Como decimos en JD Power, "este es un desarrollo impulsado por la apología: lo siento".