Antes que nada, dejemos una cosa clara. Cuando llaman al super
dentro del class_eval
, no tiene absolutamente nada que ver con el motivo por el que utilizaron include Module.new {}
. De hecho, el super
que se llamó dentro del método destroy
es completamente irrelevante para responder a su pregunta. Podría haber cualquier código arbitrario dentro de ese método de destrucción.
Ahora que lo sacamos del camino, esto es lo que está pasando.
En Ruby, si simplemente define un método de clase, y luego lo define de nuevo en la misma clase, no podrá llamar al super
para acceder al método anterior.
Por ejemplo:
class Foo
def foo
'foo'
end
def foo
super + 'bar'
end
end
Foo.new.foo # => NoMethodError: super: no superclass method `foo' for #<Foo:0x101358098>
Esto tiene sentido, debido a que la primera foo
no se definió en algunos superclase, o en cualquier lugar en la cadena de búsqueda (que es donde super
puntos). Sin embargo, usted puede definir el primer foo
de tal manera que cuando lo sobrescriba más adelante, estará disponible llamando al super
. Esto es exactamente lo que querían lograr con hacer módulo incluir.
class Foo
include Module.new { class_eval "def foo; 'foo' end" }
def foo
super + 'bar'
end
end
Foo.new.foo # => "foobar"
Esto funciona, porque cuando se incluye un módulo, ruby lo inserta en la cadena de búsqueda. De esta forma, puede llamar posteriormente al super
en el segundo método y esperar que se llame al método incluido. Estupendo.
Sin embargo, puede preguntarse, ¿por qué no simplemente incluir un módulo sin todos los trucos? ¿Por qué están usando sintaxis de bloque? Sabemos que mi ejemplo anterior es exactamente equivalente al siguiente:
module A
def foo
'foo'
end
end
class Foo
include A
def foo
super + 'bar'
end
end
Foo.new.foo # => "foobar"
Entonces, ¿por qué no hicieron eso? La respuesta es - la llamada al reflection
.Necesitaban capturar la variable (o método) que estaba disponible en el contexto actual, que es reflection
.
Dado que están definiendo el nuevo módulo utilizando sintaxis de bloque, todas las variables fuera del bloque están disponibles para su uso dentro del bloque. Conveniente.
Solo para ilustrar.
class Foo
def self.add_foo_to_lookup_chain_which_returns(something)
# notice how I can use variable something in the class_eval string
include Module.new { class_eval "def foo; '#{something}' end" }
end
end
# so somewhere else I can do
class Foo
add_foo_to_lookup_chain_which_returns("hello")
def foo
super + " world"
end
end
Foo.new.foo # => "hello world"
Neat, ¿eh?
Ahora permítanme enfatizarlo de nuevo. La llamada al super
dentro del método destroy
en su ejemplo no tiene nada que ver con ninguna de las anteriores. Lo llamaron por sus propias razones, porque tal vez la clase donde esto está sucediendo está subclasificando otra clase que ya definió destroy
.
Espero que esto quede claro.
tal vez haciendo ping a los chicos del núcleo de los rieles? – rogerdpack