2010-08-09 12 views
7

En el siguiente código se usa el módulo include. De la forma en que lo veo si se elimina el módulo de inclusión, también se creará un método de instancia. Entonces, ¿por qué el usuario incluye el módulo?por qué utilizar include module cuando class_eval por sí solo sería suficiente

http://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations.rb#L1416

include Module.new { 
      class_eval <<-RUBY, __FILE__, __LINE__ + 1 
      def destroy      # def destroy 
       super       # super 
       #{reflection.name}.clear  # posts.clear 
      end        # end 
      RUBY 
     } 
+0

tal vez haciendo ping a los chicos del núcleo de los rieles? – rogerdpack

Respuesta

11

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.

+0

respuesta increíble ... gracias :) – Orlando

0

supongo ... pero ellos no quieren sobreescribir el método de "destruir", y quiere dejarlo disponible para ser sobrecargado por algún usuario final (usted o yo), sin que elimine esta funcionalidad "reflection.clear".

Entonces, al incluirlo como un módulo, pueden llamar a "super" que llamará al original o la versión sobrecargada (escrita por el usuario final).

0

Gracias a include, destroy método no se sobrescribe. Llega a la clase fantasma de la que se deriva la clase real. De esta forma, cuando se llame al destroy en el objeto AR, se llamará al original y super llamará a uno desde el módulo anónimo (que más tarde llamará al destroy original de la clase de la que deriva).

Un poco complicado, de hecho.

Cuestiones relacionadas