2010-03-08 8 views
20

voy a explicar lo que estoy buscando en el código como eso es probablemente la más sucinta:¿Cómo puedo revertir de rubí incluyen la función

module Mixin 
    def method 
    puts "Foo" 
    end 
end 

class Whatever 
    include Mixin 
end 

w = Whatever.new 
w.method 
=> "Foo" 

# some magic here 
w2 = Whatever.new 
w.method 
=> NoMethodError 

que había intentado apenas undefining el módulo Mixin usando remove_const, pero este doesn No parece hacer ninguna diferencia en lo que sea. Supuse que #include acaba de agregar una referencia al módulo en la cadena de resolución de métodos de la clase, pero este comportamiento no concuerda con eso.

¿Alguien puede decirme qué incluye realmente detrás de las escenas, y cómo revertir esto?

+0

La razón para querer esto es porque una gema que estamos escribiendo para merb reemplazará a los asistentes de formulario existentes, y tenemos que eliminarlos de alguna manera. Por lo tanto, no puedo alterar realmente cómo se incluye el primer módulo, solo lo que se puede hacer después. – Glenjamin

+1

Posible duplicado de [¿Es posible revertir el módulo incluido en una clase?] (Http://stackoverflow.com/questions/1019477/is-it-possible-to-reverse-the-included-module-in-a -class) – lulalala

Respuesta

4
module Mod 
    def foo 
    puts "fooing" 
    end 
end 

class C 
    include Mod 
    def self.remove_module(m) 
    m.instance_methods.each{|m| undef_method(m)} 
    end 
end 

>> c = C.new 
>> c.foo 
fooing 
>> C.remove_module(Mod) 
=> ["foo"] 
>> c.foo 
NoMethodError: undefined method `foo' for #< C:0x11b0d90> 

+2

¿No habría un problema si un método con el mismo nombre ya existía en la clase 'C' antes de incluir el módulo' Mod'? –

+0

seguro. Suponiendo que Mod use alias_method para sobrescribir los métodos anteriores, podría revertir los métodos sobrescritos, pero creo que ese nivel de detalle fue excesivo para la pregunta. – klochner

+0

Después de realizar más pruebas, ni undef_method ni remove_method realmente hacen lo que necesito. – Glenjamin

3

No estoy seguro de lo que está intentando lograr, pero quizás en lugar de usar include para agregar métodos de instancia, lo que quiere hacer es usar extend para agregar métodos solo a instancias particulares de la clase, entonces no lo haría no es necesario eliminarlos.

More information on the difference between include and extend

6

Como parece probable que quiere lograr estos en los casos en lugar de toda la clase, así que iba a cambiar el código de klochner un poco de manejar una sola instancia en lugar de todas las instancias de una clase .

module ModuleRemover 

    def remove_module(mod, options = {}) 
     metaclass = class << self; self end 
     mod.instance_methods.each {|method_name| metaclass.class_eval { undef_method(method_name.to_sym) }} 
    end 

end 

Como Mladen señaló, que estaría bien para evitar la eliminación de los métodos que se sobrescriben en la clase de acogida, por lo que un [only, exclude] opciones para este método sería ideal. Hace

>> c1 = C.new 
>> c1.foo 
=> fooing 
>> c1.extend(ModuleRemover) 
>> c1.remove_module(Mod) 
>> c1.foo 
=> NoMethodError: undefined method `foo' for #< C:0x11b0d90> 
>> c2 = C.new 
>> c2.foo 
=> fooing 
+0

¿hay alguna manera de hacerlo sin eliminar los métodos llamados por el mismo nombre en toda la cadena de herencia? cambiar undef_method para eliminar_metodo no funciona para mí – Sv1

0

algunos años he utilizado la joya evil para los módulos de la ONU, incluyendo etc., pero al parecer ya no se mantiene. Así que, en su lugar, probé un (solo en mi viejo ruby ​​1.8.7). Funcionado bien como se anuncia:

DESCRIPCIÓN:

ONU proporciona deshaga la extensión y uninclude para permitir una mejor experiencia en programación orientada prototipo.

Si reemplaza el "# un poco de magia aquí" (después de instalar un) por

require 'un' 
Whatever.uninclude Mixin 

que presentamos lo mejor comportamiento como se describe por usted - casi. El objeto ya tiene un método llamado método, por lo que se obtiene un error de "número incorrecto de argumentos".

Sería bueno si alguien lo intenta en ruby ​​1.9 o en jruby e informa los resultados (hago la wiki de la comunidad de respuestas para esto).

Cuestiones relacionadas