views:

149

answers:

2

You include the modules in classes to extend the class functionality in terms of both adding class methods and instance methods to that particular class.

module M
 def self.class_method_from_module
  'from class_method_from_module'
 end

 def instance_method_from_module
  'from instance_method_from_module'
 end
end

class C
 include M

 def self.class_method
  'from class_method'
 end

 def instance_method
  'from instance_method'
 end
end

puts C.class_method => 'from class_method'

puts C.class_method_from_module => 'from class_method_from_module'

puts C.new.instance_method => 'from instance_method'

puts C.new.instance_method_from_module => 'instance_method_from_module'

Now even after removing the module M from the memory with the following:

Object.send(:remove_const, :M) #remove the module M

puts C.class_method_from_module => 'from class_method_from_module'

puts C.new.instance_method_from_module => 'instance_method_from_module'

instead of method missing. Why is that? What is the best way to remove the functionality added by a module to a class?

+1  A: 

When you include a mixin in a class, you are effectively adding the methods defined in the module to the class, or replacing methods in the class with those in the module that have the same name. The class does not have any tie to the module, which is why 'undefining' the M module won't affect class C. All that does is prevent you from mixing in M beyond that point.

You could use undef_method to remove methods from class C, but that will have side effects, potentially -- if a method was overriden by including a module, you will not get the original back, for instance. Undefining a class method is kind of ugly.

C.send(:undef_method, :instance_method_from_module)
class << C
  self
end.send(:undef_method, :class_method_from_module)
Daniel Vandersluis
Thanks for the feedback Daniel.My understanding is that when you call a method on an object it looks for the methods defined in the order (1) metaclass of that particular object (2) its class (3) included module (4) superclass (5) superclass included modules and so on, so when I include a module and if it affectively adds those methods, how does ruby knows which method belongs to metaclass, class, module, and so on... I thought that by mixin the module, ruby keeps a reference to that particular module, and by removing the constant, it should effectively remove the reference.
satynos
As far as I understand it, no reference to the module is kept; the mixin happens, and then the module becomes (so to speak) part of the class. Thus, even though you removed the module, the class still retains the mixed in methods/properties.
Daniel Vandersluis
There has to be some reference to the module (some kind of knowledge that this method is defined in the module or some sort) kept in the memory table, otherwise how would ruby know where to look for in the hierarchy chain when a method is actually not defined in the class. Thanks for the feedback though.
satynos
+1  A: 

The details vary by implementation, but I know that at least in JRuby and MRI 1.8 there is a construct called an Include Class that is inserted in to the inheritance chain of a class when a Module is extended or included. The Module therefor won't be garbage collected, since the Include Class still refers to it, and the methods will still be on your class. There are some great articles by Patrick Farley on this and related topics in his blog.

So, to "remove" a module, you could individually undefine each method that came from the module, but that's a pretty unwieldy mechanism for that purpose. If using a gem is acceptable to you, it would probably be better would be to use Mixology, which was designed specifically for the purpose of adding and removing modules dynamically.

John Hyland