Acceder a las variables de clase Ruby con class_eval e instance_eval
-
26-09-2019 - |
Pregunta
Tengo lo siguiente:
class Test
@@a = 10
def show_a()
puts "a: #{@@a}"
end
class << self
@@b = '40'
def show_b
puts "b: #{@@b}"
end
end
end
¿Por qué funciona el siguiente?
Test.instance_eval{show_b}
b: 40
=> nil
Pero no puedo acceder @@b
¿directamente?
Test.instance_eval{ @@b }
NameError: uninitialized class variable @@b in Object
Del mismo modo, lo siguiente funciona
t = Test.new
t.instance_eval{show_a}
a: 10
=> nil
Pero lo siguiente falla
t.instance_eval{ @@a }
NameError: uninitialized class variable @@a in Object
No entiendo por qué no puedo acceder a las variables de clase directamente desde el instance_eval
bloques.
Solución
Solo le hice la misma pregunta a Matz durante la fiesta de Rubykaigi. Estaba a media derrota, pero él estaba perfectamente sobrio, por lo que puedes tomar esto como la respuesta definitiva.
Anton tiene razón: la razón por la que no puede acceder a las variables de clase a través de instance_eval () es "solo porque". Incluso class_eval () comparte el mismo problema (el propio Matz no estaba totalmente seguro de class_eval () hasta que le dije que ya lo había probado). Más específicamente: en cuanto al alcance, las variables de clase son más parecidas a las constantes que las variables de instancia, por lo que cambiar a sí mismo (como instance_eval () y class_eval () do) no hará ninguna diferencia cuando se trata de acceder a ellas.
En general, podría ser una buena idea evitar las variables de clase por completo.
Otros consejos
EDITAR: El siguiente código se probó con 1.8.7 y 1.9.1 ... Parece que la situación es diferente nuevamente con 1.9.2:/
La situación en realidad no es tan directa. Hay diferencias en el comportamiento dependiendo de si está utilizando 1.8 o 1.9 y si está utilizando class_eval
o instance_eval
.
Los ejemplos a continuación detallan el comportamiento en la mayoría de las situaciones.
También incluí el comportamiento de las constantes, en buena medida, ya que su comportamiento es similar a las variables de clase, pero no exactamente las mismas que las de clase.
Variables de clase
class_eval
En Ruby 1.8:
class Hello
@@foo = :foo
end
Hello.class_eval { @@foo } #=> uninitialized class variable
class_eval
En Ruby 1.9:
Hello.class_eval { @@foo } #=> :foo
Entonces variables de clase son miró hacia arriba en 1.9 (pero no en 1.8) cuando se usa class_eval
instance_eval
En Ruby 1.8 y 1.9
Hello.instance_eval { @@foo } #=> uninitialized class variable
Hello.new.instance_eval { @@foo } #=> uninitialized class variable
Parece que las variables de clase son no miró hacia arriba en 1.8 o 1.9 Al usar instance_eval
Lo que también es interesante es el caso de constantes:
Constantes
class_eval
En Ruby 1.8
class Hello
Foo = :foo
end
Hello.class_eval { Foo } #=> uninitialized constant
class_eval
En Ruby 1.9
Hello.class_eval { Foo } #=> :foo
Entonces, como con las variables de clase, las constantes se buscan en 1.9 pero pero no en 1.8 para class_eval
instance_eval
En Ruby 1.8
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> uninitialized constant
instance_eval
En Ruby 1.9
Hello.instance_eval { Foo } #=> uninitialized constant
Hello.new.instance_eval { Foo } #=> :foo
Parece que la búsqueda constante no es muy análoga a la variable de clase, busque Ruby 1.9. A Hello
instancia lo hace Obtenga acceso a la constante mientras el Hello
La clase no lo hace.
Bueno, probablemente la mejor respuesta es "solo porque": la instancia_eval en pocas palabras crea algún tipo de proceso singleton que se invoca con la vinculación de un objeto dado. Estoy de acuerdo en que suena un poco extraño, pero es lo que es.
Si ejecuta instance_eval con una cadena, incluso obtendrá una advertencia de que su método intenta acceder a la variable de clase:
irb(main):038:0> Test.new.instance_eval "@@a"
(eval):1: warning: class variable access from toplevel singleton method
NameError: (eval):1:in `irb_binding': uninitialized class variable ...
Ruby 2.1
Esta es la forma más concisa y semánticamente correcta que he encontrado para acceder a una variable de clase:
class Hello
@@foo = :foo_value
end
Hello.class_variable_get :@@foo #=> :foo_value