New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Zeitwerk broken by Bundler #104
Comments
That is strange, Zeitwerk routinely works with Bundler in Rails applications and non-Rails Ruby projects. Would it be possible for you to provide a minimal application that reproduces this? |
I found it strange too. I'll try to set up a minimal app replicating the issue during the week. |
I set up a replicating app with as little as I could (by shaving away from my problem-encountering app). https://github.com/Aquaj/broken-zeitwerk-minimal-ex As I worked on it I figured a few more things:
It seems to stem from a cocktail of libraries (Karafka, Foreman, Zeitwerk, Bundler) so it's likely a fix could be found in any of them, but since retrograding to the I'll try to look into the issue more now that we have a minimal replicating case. There's no urgency to finding a fix though, as there are many ways to go around the issue. |
Would it be possible for you to inspect Object.method(:require).source_location in a few places to see it changed? For example, in a vanilla shell you get this:
|
Sure ! I also added this snippet at the very beginning of the module Kernel
def self.method_added(method_name)
return unless method_name.to_s == 'require'
puts "== REDEFINITION OF REQUIRE in Kernel"
caller.first(3).each { |callsite| puts ' - ' + callsite }
puts "=="
end
end And here are the results
|
So I guess the flow is:
ActiveSupport modifies it by including a module which then calls So by the last Editing my own message to avoid spamming comments.
Going further down I can see it go from:
Object.class_eval do
# ...
define_method(:require, Kernel.instance_method(:require))
private :require
end So now the question is: who resets Turns out it's Bundler, during a call to I've also noticed from the numerous logs that Zeitwerk doesn't seem to be coming in at the same time during a --- tmp/error_calls 2020-01-10 02:09:46.000000000 +0100
+++ tmp/ok_calls 2020-01-10 02:12:57.000000000 +0100
@@ -1,42 +1,41 @@
==== BUNDLER SETUP v2.0.2 - Bundler.object_id: <doesn't change>
@setup.present? = false
@groups.empty? = true
====
-=[ZEITWERK/PRE-MODULE]
-=[ZEITWERK/POST-MODULE]
-=[KARAFKA_BOOT_FILE/PRE-REQ]
-=[ENVIRONMENT/PRE-REQ_REL]
=[APPLICATION/PRE-REQ_REL]
=[BOOT/PRE-REQ_BUNDLER]
=[BOOT/POST-REQ_BUNDLER]
=[BOOTSNAP/PRE-MODULE]
=[BOOTSNAP/POST-MODULE]
=[ACTIVESUPPORT/PRE-LOADABLE]
=[ACTIVESUPPORT/POST-LOADABLE]
=[BOOT/POST-REQ_BOOTSNAP]
=[APPLICATION/PRE-REQ]
=[APPLICATION/PRE-BUNDLER]
=[BUNDLER/PRE-SETUP]
==== BUNDLER SETUP v2.0.2 - Bundler.object_id: <doesn't change>
-@setup.present? = false
+@setup.present? = true
@groups.empty? = false
====
=[BUNDLER/PRE-REQUIRE]
+=[ZEITWERK/PRE-MODULE]
+=[ZEITWERK/POST-MODULE]
=[BUNDLER/POST-REQUIRE]
=[APPLICATION/POST-BUNDLER]
+=[ENVIRONMENT/PRE-REQ_REL]
=[ENVIRONMENT/POST-REQ_REL]
=[RAILTIES/PRE-INITIALIZERS]
=[ ...INITIALIZERS... ]
=[LET_ZEITWERK_TAKE_OVER/PRE-RUN]
=[TAKE_OVER/PRE-INTEG]
=[TAKE_OVER/PRE-SETUP]
=[TAKE_OVER/PRE-FREEZE]
=[TAKE_OVER/PRE-DECORATE]
=[DECORATE/PRE-UNHOOK]
=[ZEITWERK/PRE-CONST-EXCLUDE]
=[ZEITWERK/PRE-LOAD-EXCLUDE]
=[LOADABLE_EXCLUDE/PRE-LOAD]
=[LOADABLE_EXCLUDE/PRE-REQUIRE]
=[LOADABLE_EXCLUDE/POST-REQUIRE]
=[ZEITWERK/POST-LOAD-EXCLUDE] (I've committed all of those But I haven't figured out why yet. Throwing in the towel for tonight, I might try to run it through ruby-prof this weekend to get a better idea of the path it takes to get there. Sorry this comment got a bit long, I've kind of been using it as a debugging log. But I figure it might be useful to anyone willing to look into it. |
Hey! Zeitwerk decorates This issue may be useful in a text search, and I'm curious and will personally try to understand what is happening, but since this is not a bug in Zeitwerk ---which routinely works together with Rails and Bundler,--- and since I like the issues and PRs counter to be 0 in this project, I prefer to close. |
Hi ! |
@Aquaj what version of bundler has the fix? |
Hi, I think Zeitwerk's gets messed up by Bundler's
reverse_rubygems_kernel_mixin
internal method.I'll do my best to explain the initial issue and what I've found so far.
On the app I'm working on we had an issue where Karafka wouldn't fail on launch (launched with
bundle exec foreman start karafka
), raising aLoadError - file not found 'app/channels/application_cable'
.As the path is a folder and not an actual ruby file, it seems like it should've been autovivified instead of being
require
d by the app. So I went digging some more and it appears that even though therequire
method defined in lib/zeitwerk/kernel.rb is called at the beginning of the application initialization, as soon as the app reachesconfig/application.rb
(more specificallyBundler.require(*Rails.group)
), Bundler tries to setup, which includes a call toreverse_rubygems_kernel_mixin
, resettingKernel#require
to its original version and overwriting Zeitwerk's modifications to it.I don't really know how to fix it, and I don't know why we only encounter it when launching our karafka process and not on the rails server one, but I'm open to ideas on how we could solve this.
For now we had to resort to defaulting back on the
:classic
loader, which I'm not a big fan of but does the work.I also have opened an issue on Bundler's repository (bundler/bundler#7553).
UPDATE
The issue was ultimately fixed in Bundler. So if you have the issue, try to update it.
If you can't update, here are some workarounds we'd found before:
:classic
autoloader (config.autoloader = :classic
inconfig/application.rb
)bundle binstub <library>
) in the Procfile for the library instead of thebundle exec
call (service: bin/<library>
instead ofservice: bundle exec <library>
)The text was updated successfully, but these errors were encountered: