2010-08-19 15 views
5
class Foo 
    include Module.new { class_eval "def lab; puts 'm' end" } 

    def lab 
     super 
     puts 'c' 
    end 
end 

Foo.new.lab #=> m c 

======================================= =================================instance_eval vs class_eval en el módulo

class Foo 
    include Module.new { instance_eval "def lab; puts 'm' end" } 

    def lab 
     super 
     puts 'c' 
    end 
end 

Aviso aquí me cambió class_eval a instance_eval

Foo.new.lab rescue nil#=> no super class method lab 
Foo.lab #=> undefined method lab for Foo class 

Parece que incluir el módulo no definió un método de instancia ni un método de clase.

¿Alguna explicación de lo que está pasando aquí?

Este código fue probado en ruby ​​1.8.7 en mac.

Respuesta

9

Primero, piense en lo que hace include. Hace que los métodos instancia del módulo se incluyan en los métodos instancia en la clase incluida. es decir, aparte del hecho de que su ejemplo de trabajo utiliza un módulo anónima es equivalente a:

module M1 
    def lab 
    puts 'm' 
    end 
end 

class Foo 
    include M1 

    def lab 
     super 
     puts 'c' 
    end 
end 

Después, piense en lo que hace class_eval. Evalúa el código dado en el contexto de la clase o módulo. es decir, es exactamente como si reabrió el módulo y escribió el código pasado al class_eval. Así MyModule = Module.new { class_eval "def lab; puts 'm' end" } es equivalente a

module MyModule 
    def lab 
    puts 'm' 
    end 
end 

Esperamos que esto explica el caso en el que trabaja.

Cuando se utiliza instance_eval está evaluando el código en el contexto del objeto receptor (en este caso la instancia de módulo) de modo MyMod2 = Module.new { instance_eval "def lab; puts 'm' end" } es equivalente a

module MyMod2 
    def MyMod2.lab 
    puts 'm' 
    end 
end 

es decir, se crea una método módulo que Llamarías a través de MyMod2.lab y dichos métodos no se agregarán como métodos de instancia por include.


Tenga en cuenta: esta respuesta pide prestado un poco de su explicación de una respuesta que escribí a un previous question asking about instance_eval vs. class_eval relativa a un ejemplo de The Ruby Programming Language libro. Es posible que encuentres esa respuesta útil también.

+0

excelente explicación. dividir las cosas y mirarlas un paso a la vez ayuda. –

3

incluyendo un módulo solo toma los métodos de instancia - está buscando extender. Por suerte para conseguir lo mejor de ambos mundos, sólo tiene que hacer:

module Something 
    def self.included(base) 
    base.extend ClassMethods 
    end 

    module ClassMethods 
    def blah 
     puts "lol" 
    end 
    end 
end 

class Test 
    include Something 
end 

IRB:

>> Test.blah 
lol 
=> nil 
Cuestiones relacionadas