Simplified Code: https://gist.github.com/4563204 I am trying to understand why when I reopen a module A and 'include' a module B, class Klass that has already included module A do not gain the new methods from module B. When I do reopen module A and add a new method, class Klass that has already included module A gain these methods. What is the difference between reopening a module and adding a method and including another module?
on 2013-01-18 09:52
on 2013-01-18 14:35
hmmm... not sure what's going on there, i'd expect the same behavior that you do. you've probably already found ways to work around this, but if not, both of these work: module A def foo1; end end module B def foo2; end end module A include B def foo3; end end class Klass include A end p Klass.instance_methods **** or **** module A def foo1; end end class Klass include A end module B def foo2; end end class Klass include B end module A def foo3; end end p Klass.instance_methods wish i could explain (and understand!) why your original example doesn't work as expected... - j
on 2013-01-18 20:14
I've updated the gist a bit to reflect what you found about moving the class decoration below the module A reopening. This is just really baffling =( For a concrete example, if I wrote an extension module SuperEnum to Enumerable and wanted all objects that included Enumerable to have access to this extension, I would expect that I could open up the module Enumerable and include module SuperEnum.
on 2013-01-19 03:01
not sure i understood correctly, but this works - module SuperEnum def super_enum_method; end end module Enumerable include SuperEnum end class Foo include Enumerable end p Foo.instance_methods.include?(:super_enum_method) => true
on 2013-01-19 12:53
I asked about this to Matz in the past and it works that way basically as a consequence of the implementation. > > As you probably know, MRI stores a linear flat chain of ancestors of a class or module. Each ancestor has a pointer to its own direct ancestor in the chain of that particular class or module conceptually (there are indirections but they are not important). Thing is, albeit new methods are found because method name resolution follows the pointers at the time of the call (conceptually), and albeit if you include another module in the very class the pointers are adjusted, when you include a 2nd order module the ancestor chains of the classes or modules the enclosing module was already mixed in are not updated and reflattened so to speak. Indeed, modules just do not keep that information, they do not know where they have been included. So that update is not even possible with the current implementation. Matz said he would be willing to make this work provided the implementation had no performance penalty. Sent from my iPhone
on 2013-01-19 21:08
Ahh, that makes sense! Thanks Xavier!