From: Calamitas Date: 2008-04-19T20:52:51+09:00 Subject: Re: Included in Kernel, yet won't show up in `C::ancestors'? On Sat, Apr 19, 2008 at 7:20 AM, Arlen Cuss wrote: > Hi all, > > I'm fairly sure I'm a long way down the path of madness with this one. > Please bare with me. I've been on this for some time now. > > I've been trying to work out the order in which items appear in > "SomeClass::ancestors". I thought I had it before: > > [SomeClass, SomeClass'sIncludedModules..., SuperClass, > SuperClass'sIncludedModules, SuperSuperClass, ...] > > I constructed a test case: > > $ ruby -e 'module M; end; module N; end; class A; include M; end; class B < > A; include N; end; p B.ancestors' > [B, N, A, M, Object, Kernel] > > This is the expected result! But then I wondered.. what if we included > something in `M'? It shouldn't appear according to my theory here, since M > is not included by SomeClass itself. But, sure enough, it does appear. > (which makes sense!) > > The reason I presumed this is because if I include a module in Kernel, it > won't appear in the list of any Class's ancestors, yet it will appear in > Kernel's: > > $ ruby -e 'module A; end; module Kernel; include A; end; class C; end; p > C.ancestors; p Kernel.ancestors' > [C, Object, Kernel] > [Kernel, A] > > This confused me slightly, since it goes directly against what we experience > with this: > > $ ruby -e 'module A; end; module B; include A; end; class C; include B; end; > class D < C; end; p D.ancestors' > [D, C, B, A, Object, Kernel] > > .. that is, the modules included by a superclass's included module appearing > in the ancestors list. In the previous example, Object (superclass of C) > includes Kernel, and we don't see Kernel's included A. Here, C (superclass > of D) includes B, yet we also see A. So, that proves that theory wrong. You've bumped here into what some people call the double inclusion problem or the dynamic inclusion problem. With modules in modules, the order of the inclusions matters. A slightly reordered version of your last example is this: $ ruby -e 'module A; end; module B; end; class C; include B; end; module B ; include A ; end ; class D < C; end; p D.ancestors ; p B.ancestors' [D, C, B, Object, Kernel] [B, A] See how A disappeared from the ancestors even though it is still included in A? This is what you experience with Kernel: Kernel was included in Object before you included your own module in it. This is because of the way module inclusion is implemented in Ruby. In database terms, Ruby's internal representation is denormalized for efficiency reasons. The danger is inconsistency, which is what happens here. Note that you can force inclusion of recursively included modules by reincluding the top-level module after recursive inclusions, like this: ruby -e 'module A; end; module Kernel; include A; end; class Object ; include Kernel ; end ; class C; end; p C.ancestors' [C, Object, Kernel, A] The reinclusion of Kernel in Object doesn't result in it being included twice (Ruby tries to prevent that from happening), but it does update the recursive inclusion. So basically, Kernel is nothing special in this regard, except that by its predefinition it resulted in a different inclusion order than your other test cases. HTH, Peter