views:

216

answers:

6

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?

+2  A: 

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

ghoppe
+2  A: 
klochner
Wouldn't there be a problem if a method with the same name already existed in class `C` before including module `Mod`?
Mladen Jablanović
sure. Assuming Mod uses alias_method to overwrite the former methods, you could revert the overwritten methods, but I think that level of detail was overkill for the question.
klochner
After further testing, neither undef_method or remove_method actually does what I need.
Glenjamin
+1  A: 

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
Roman Gonzalez
+1  A: 

Try Mixology: http://www.somethingnimble.com/bliki/mixology

banister
A: 

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).

jug