Establecer una variable de instancia en un método de ámbito de clase
Frecuentes
Visto 579 veces
1
Quiero crear una variable de instancia cuando se llama a un determinado método y también establecerlo en un valor predeterminado. La instancia solo debería poder obtener la variable pero no establecerla. Aquí hay un fragmento de código para mostrar mi problema:
class Foo
def self.bar(var)
attr_reader name.to_sym
instance_variable_set("@#{var.to_s}",[var.to_s])
end
end
class Bar < Foo
bar :foo
end
puts Bar.new.foo
Cuando ejecuto este código, espero obtener ["foo"]
pero en cambio consigo nil
. Parece ser un problema de alcance, pero después de jugar con el código durante un tiempo, parece que no lo entiendo bien.
Edit: Acabo de encontrar un artículo excepcionalmente bueno (leído hasta el final) que aborda este problema de manera muy concisa. Haz click en mi
1 Respuestas
2
Creo que este es el tipo de patrón que estás tratando de crear:
class Foo
def self.all_properties
@all_properties ||= []
end
def self.property( pr_name )
attr_reader pr_name.to_sym
instance_variable_set( "@default_#{pr_name.to_s}", [pr_name.to_s] )
all_properties << pr_name.to_sym
end
def set_default_properties
self.class.all_properties.each do | pr_name |
default = self.class.instance_variable_get( "@default_#{pr_name.to_s}" )
instance_variable_set( "@#{pr_name}", default.clone )
end
end
def initialize
set_default_properties
end
end
class Bar < Foo
property :foo
end
p Bar.new.foo
Es más complejo de lo que podría suponer inicialmente. Debe colocar la lista de propiedades administradas y sus valores predeterminados en algún lugar. He hecho esto arriba con el variables de instancia de clase @all_properties
y @default_foo
. Puede reenviarlos a cada instancia con un poco más de metaprogramación que la que tiene en la pregunta; básicamente, se debe realizar una copia de los valores predeterminados escondidos en la clase cuando se definió durante la creación de instancias. ¿Por qué? Debido a que la definición de clase no se vuelve a ejecutar durante la creación de instancias, sucede solo una vez antes (a menos que comience a modificar la clase sobre la marcha en el constructor, ¡pero eso sería inusual!)
Tenga en cuenta que clone
en el código anterior no es suficiente para evitar que las instancias interfieran entre sí. No he implementado un clon profundo (dejado como ejercicio para cualquiera que lea el código), pero la siguiente variación de esa línea funcionará para el código tal como está, porque la estructura de los datos predeterminados es siempre la misma:
instance_variable_set( "@#{pr_name}", [ default[0].clone ] )
Respondido el 11 de Septiembre de 13 a las 15:09
El descuido de que "no se pueden modificar instancias que no existen" me permitió creer poder resolver esto de una manera más concisa. Muchas gracias. - Soren Titze
No hay problema. Ordené el código y agregué un detalle importante: usar clone
. Puede o no necesitar esto, pero tener instancias que compartan punteros para atribuir valores me pareció incorrecto como punto de partida. Técnicamente, con una matriz que contiene una cadena, esto debería ser incluso un clon profundo, pero eso es más complejo, por lo que no se hace aquí. - Neil Slater
Me gusta la adición de clone
mucho. Tan pronto como te alejas de los detalles esenciales de los lenguajes pesados de puntero, uno es propenso a pasar por alto cosas como esa. - Soren Titze
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas ruby or haz tu propia pregunta.
Supongo que puedes hacer uso del método de inicialización en Ruby. - Vamsi Krishna
Bueno, pero ya no tengo la información sobre qué valor predeterminado establecer durante la inicialización. ¿O me estoy perdiendo algo? ¿Podría dar un ejemplo? - Sören Titze
Como esta escrito
bar :foo
solo se llama cuando la clase está definida y la variable de instancia se establece en la claseBar
.attr_reader
funciona, porque se supone que eso debe llamarse contra la clase. No puede modificar instancias que no existen. Sin embargo, probablemente podría esconder nombres y valores predeterminados en algún lugar (quizás en la clase de destino) y proporcionar un método al que pueda llamar desdeinitialize
para leer todos los valores predeterminados. - Neil Slater