I found a bug (?) with routes.url_helpers. Let’s say my routes file contains
resources :fruits
Now I have the following setup:
module A
end
module B
include A
end
A.module_eval do
include Rails.application.routes.url_helpers
end
Object.new.extend(A).fruit_path #=> That works.
Object.new.extend(B).fruit_path #=> NoMethodError: undefined method `fruit_path’ for #Object:0x98e56d0
In a proper Ruby setup B should have references to any method in A. Obviously, this doesn’t work with url_helpers. I didn’t check its implementation so far, is there any magic done inside the url_helpers that breaks the expected behaviour?
As you probably know, the interpreter keeps a linear representation of the ancestors of a base class or module. Algorithms that walk up the ancestors go over that linearization following pointers up, as in a linked list, rather than following pointers recursively which would be the immediate mental model for this.
OK, if you add methods or constants to any class or module in that ancestry chain they are seen:
module M
end
class C
include M
end
module M
def foo
end
end
C.new.foo # WORKS
If you include more modules into the base class or module, their methods are found:
class C
end
module M
def foo
end
end
class C
include M
end
C.new.foo # WORKS
The linear ancestry that exists behind the scenes gets updated to reflect the new ancestor.
But if once the linearization is done you modify the 2nd-degree ancestors, those are not propagated:
module M
end
class C
include M
end
module N
def bar
end
end
module M
include N
end
C.new.bar # DOES NOT WORK
As you see the linearization of the ancestry chain of C does not get updated with N, albeit if you computed the ancestry chain in that very moment N should belong to it.
I asked for the rationale behind this, conceptually does not seem coherent. Matz said it could be changed if someone came with an implementation that was performant enough[*].