ruby class variables instance-eval

Accediendo a las variables de clase Ruby con class_eval y instance_eval



instance-eval (4)

Acabo de hacerle la misma pregunta a Matz durante la fiesta de RubyKaigi. Estaba medio borracho, pero él estaba perfectamente sobrio, así 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 acerca de class_eval () hasta que le dije que ya lo había intentado). Más específicamente: el alcance, las variables de clase son más constantes que las variables de instancia, por lo que cambiar self (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 por completo las variables de clase.

Tengo los siguientes

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

Test.instance_eval{show_b} b: 40 => nil

¿Pero no puedo acceder a @@b directamente?

Test.instance_eval{ @@b } NameError: uninitialized class variable @@b in Object

Asimismo, las siguientes obras.

t = Test.new t.instance_eval{show_a} a: 10 => nil

pero falla lo siguiente

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 los bloques de instance_eval .


Bueno, probablemente la mejor respuesta es "solo porque": el instance_eval en pocas palabras crea algún tipo de proceso singleton que se invoca con el enlace 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 recibirá 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 ...


EDITAR: el código siguiente 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 sencilla. Hay diferencias en el comportamiento dependiendo de si está usando 1.8 o 1.9 y si está usando 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, pero no exactamente igual, a las variables 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

Así que las variables de clase se class_eval 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 no se buscan en 1.8 o 1.9 cuando se usa instance_eval

Lo que también es interesante es el caso de las constantes :

Constantes

class_eval in 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, al igual que con las variables de clase, las constantes se class_eval en 1.9 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 búsqueda de variables de clase para Ruby 1.9. Una instancia de Hello tiene acceso a la constante, mientras que la clase Hello no lo hace.


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