I'll explain what i'm looking for in code as thats probably the most succinct:
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
I had tried just undefining the Mixin module using remove_const, but this doesn't seem to make any difference to Whatever. I had assumed that #include just added a reference to the module into the class's method resolution chain - but this behaviour doesn't agree with that.
Can anyone tell me what include actually does behind the scenes, and how to reverse this?
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>
As it seems probably you want to accomplish these on instances instead of the whole class, so I would change klochner's code a bit to handle just one instance instead of all the instances of a class.
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
As Mladen pointed out, it would be cool to avoid removing methods that are overwritten on the host class, so an [only, exclude]
options for this method would be ideal.
>> 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
I'm not sure what you're trying to accomplish, but perhaps instead of using include
to add instance methods, what you want to do is use extend
to add methods just to particular instances of the class, then you wouldn't need to remove them.
More information on the difference between include
and extend
Some years ago I used the gem evil for un-including modules etc., but apparently it is no longer maintained. So I just tried un instead (only on my old ruby 1.8.7). Worked fine as advertised:
DESCRIPTION:
un provides unextend and uninclude to allow for a better prototype-oriented programming experience.
If you replace your "# some magic here" (after installing un) by
require 'un'
Whatever.uninclude Mixin
you get the behavior as described by you - almost. Object has already a method called method, so you get a "wrong number of arguments" error instead.
It would be nice if someone tries it on ruby 1.9 or on jruby and reports the results (I make the answer community wiki for this).