Xavier Noria wrote in post #1139855:
> Yes, this is a leak of the implementation.
It means re-opening a module can be risky. What would be the possible
work-around ?
Reopening a module to **include** another module doesn't work well with the
semantics of the language as you are discovering. I learned that the hard
way extracting prototype-rails from Rails back in the day, and after
hitting my head against a wall a few times at something that didn't work as
expected.
That way, when a method is resolved MRI only follows super pointers, it
> does not traverse the actual tree of ancestors at runtime.
**super pointers** means ?
Let me explain. Let's say we have these modules:
module N
def x
:x
end
end
module M
include N
end
With those definitions N is an ancestor of M, right? Now let's define
class C
include M
end
When you invoke #x on an instance of C:
C.new.x
after checking C itself _conceptually_ the method dispatch algorithm looks
into the first ancestor, M, fails, then it **recurses** in the ancestors of
M, N. Found, dispatch.
If we add a new module reopening M:
module O
def y
:y
end
end
# reopen, assume M, N and C exist as above
module M
include O
end
the same algorithm should in theory be able to dispatch C.new.y. When
recursing in M now O would show up. But it actually does not work that way,
and it does not because of the implementation, not because it shouldn't.
The problem is that when class C was defined, the ancestor chains of the
included modules were flattened, resulting in a flat list:
C.ancestors # => [C, M, N, Object, Kernel, BasicObject]
See N there? Not only the directly included modules are present, but they
are unfolded. Well, that list can be actually found in the implementation
of MRI. You are not just recursing and unfolding the tree when ancestors is
called, the flat list is stored as is.
If you reopen C to include another module, the list gets updated, but if
you reopen M as we did, O is not injected in the list of C. In MRI M has no
idea it was included in C indeed.
Let's go for "super pointers" now.
Following super pointers means that the elements of the list are actually
chained by a "super" reference, the chain is not an array but a linked list
so to speak.
That is, "super" of C is M, "super" of M is N, "super" of N is Object, etc.
Since the "super" of (for example) "N" depends on the ancestor chain it is
included (a different class D including M could have ancestors between N
and Object) MRI actually has an indirection, the chain consists of proxy
modules that have a reference to the original module (for dispatching
stuff), and its own "super" reference to its parent in that particular
chain.
···
On Fri, Mar 14, 2014 at 4:01 PM, Arup Rakshit <lists@ruby-forum.com> wrote: