Establecer una variable de instancia en un método de ámbito de clase

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

preguntado el 11 de septiembre de 13 a las 14:09

Supongo que puedes hacer uso del método de inicialización en Ruby. -

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? -

Como esta escrito bar :foo solo se llama cuando la clase está definida y la variable de instancia se establece en la clase Bar. 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 desde initialize para leer todos los valores predeterminados. -

1 Respuestas

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 or haz tu propia pregunta.