La definición de Ruby y instance_eval vs. class_eval
metaprogramming (2)
Creo que su confusión proviene del hecho de que la definición no depende del yo actual, podría pensar que es una "clase actual" que tiene sus propias reglas.
Siguiendo tus ejemplos:
class A
# defs here go to A
puts self # => A
class << self
#defs here go to A''s eigenclass
end
end
A.class_eval do
#defs here go to A
end
A.instance_eval do
#defs here go to A''s eigenclass
end
s = "Hello World"
class << s
#defs here go to s''s eigenclass
end
Aquí está la parte del capítulo que habla sobre el tema y es bastante claro sobre el comportamiento.
class_eval y instance_eval se establecen a sí mismos durante la duración del bloque. Sin embargo, difieren en la forma en que configuran el entorno para la definición del método. class_eval configura las cosas como si estuvieras en el cuerpo de una definición de clase, por lo que las definiciones de métodos definirán los métodos de instancia. Por el contrario, llamar a instance_eval en una clase actúa como si estuvieras trabajando dentro de la clase singleton de self. Por lo tanto, cualquier método que defina se convertirá en métodos de clase.
Lo único que creo que vale la pena agregar es que puede llamar a instance_eval en cualquier objeto, no solo en las clases, y el comportamiento no cambia pero tiene diferentes consecuencias.
Algunas lecturas relevantes:
Estoy leyendo la sección Metaprogramming de Programming Ruby 1.9 y tengo problemas para entender lo que está pasando internamente entre class_eval
/ class_exec
vs. instance_eval
/ instance_exec
.
En primer lugar, mi entendimiento es que def
agrega un método a la tabla de métodos self
(el objeto de clase):
class A
puts self # => A
def foo; 42; end # added to the method table of self, so becomes an instance method
end
A.new.foo # => 42
Y si usamos class_eval
, obtenemos el mismo comportamiento:
A.class_eval do
puts self # => A
def bar; 42; end # same as above
end
A.new.bar # => 42
Pero de alguna manera en el caso de instance_eval
, las cosas son diferentes:
A.instance_eval do
puts self # => A
def baz; 42; end # added to the method table of an anonymous
# singleton class of self, so becomes a class method
end
puts A.baz # => 42
s = ''string''
s.instance_eval do ... end # same behavior, so now def creates an instance method
Entonces entiendo la diferencia funcional entre class_eval
y instance_eval
.
Pero los contextos dentro de los bloques class_eval
y instance_eval
parecen exactamente iguales, en particular, los puntos local_variables
del mismo objeto y las local_variables
son las mismas. Entonces, ¿qué está pasando dentro de los bloques (internamente) que hace que def
actúe de manera diferente?
¿Hay algún documento que pueda leer? El RDoc para instance_eval y class_eval no ayuda. Mirando la fuente, instance_eval parece configurar un objeto de clase singleton mientras que class_eval no lo hace, pero ¿esta diferencia es visible fuera del código C, en el nivel de Ruby?
Solo para agregar a la respuesta de @krusty.ar: def
y define_method
agregan métodos al contexto de definición del método actual (creo que así se llama, no estoy seguro), no al self
actual.
Es solo que dentro de un módulo, clase o cuerpo de clase de singleton, los dos son los mismos.
Pero, por ejemplo, en un cuerpo de script (también conocido como nivel superior), self
es el objeto main
, pero el contexto de definición del método actual es Object
.