Skip to content
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

Classes or modules in explicit namespaces are not found #35475

Closed
fxn opened this issue Mar 5, 2019 · 11 comments · Fixed by Shopify/bootsnap#257
Closed

Classes or modules in explicit namespaces are not found #35475

fxn opened this issue Mar 5, 2019 · 11 comments · Fixed by Shopify/bootsnap#257
Assignees
Milestone

Comments

@fxn
Copy link
Member

fxn commented Mar 5, 2019

The root problem

This is really a bug in the current Ruby 2.5, but I open an issue here for reference.

Ruby 2.6 does not have this problem. The fix was backported to 2.5, so in principle should be fine in some future release.

How it affects Rails 6 Beta 2

When bootsnap loads bytecodes from its cache, Ruby 2.5 does not trigger :class events, but Zeitwerk needs them to be aware of children in explicit namespaces.

That means the if you have a namespace defined in a file like Hotel here:

# app/models/hotel.rb
class Hotel < ApplicationRecord
  include Pricing
end

# app/models/hotel/pricing.rb
module Hotel::Pricing
end

Zeitwerk won't be aware of the existence of Hotel::Pricing and loading Hotel will err.

You can see this problem in a brand new 6.0.0.beta2 app running Ruby 2.5.3:

$ bin/rails runner 'p ActionMailbox::InboundEmail::Incineratable'
Running via Spring preloader in process 18858
Please specify a valid ruby command or the path of a script to run.
Run 'rails runner -h' for help.

uninitialized constant #<Class:0x00007f8c177664e8>::Incineratable
Did you mean?  ActionMailbox::IncinerationJob

Workarounds

If your application is affected by this you have three options:

  • Upgrade to Ruby 2.6.

  • Disable the bytecode cache of bootsnap in config/boot.rb this way:

    # In config/boot.rb, replace require "bootsnap/setup" with the following.
    require "bootsnap"
    Bootsnap.setup(
      cache_dir: "tmp/cache",
      development_mode: ENV["RAILS_ENV"] == "development",
      compile_cache_iseq: false
    )
  • Enable classic mode in config/application.rb:

    config.load_defaults 6.0
    config.autoloader = :classic
@fxn fxn added this to the 6.0.0 milestone Mar 5, 2019
@fxn fxn self-assigned this Mar 5, 2019
@fxn fxn changed the title Classes or modules in explicit namespace are not found Classes or modules in explicit namespaces are not found Mar 5, 2019
@fxn
Copy link
Member Author

fxn commented Mar 14, 2019

I just checked 2.5.4p155, and the issue seems to be still present.

@dhh
Copy link
Member

dhh commented Apr 22, 2019

@fxn How about we just set config.autoloader = :zeitwerk (or whatever it is) for newly generated applications? Then people with existing applications can switch at their leisure and compatibility?

@dhh
Copy link
Member

dhh commented Apr 22, 2019

Sounds like @rafaelfranca would like to just fix this in bootsnap by disabling the iseq cache for 2.5 👍

@rafaelfranca
Copy link
Member

PR is up Shopify/bootsnap#257

@rafaelfranca
Copy link
Member

Bootsnap 1.4.4 released with the fix.

@obromios
Copy link

obromios commented Jan 9, 2020

I had this problem with rails 6.0.2 and ruby 2.5 so upgraded to ruby 2.6.5 and bootsnap 1.4.5 and it has mostly fixed the problem. However am seeing a strange effect when I run rails console. The first time I run it I get an exception:

app/helpers/eclubs/members_helper.rb:1:in `<main>': uninitialized constant Eclubs
Did you mean?  Club (NameError)

The relevant file is

#app/helpers/eclubs/members_helper.rb
module Eclubs::MembersHelper
end

But if I immediately exit and run console again it loads up correctly. Do you think this is related and how do I fix it?

@fxn
Copy link
Member Author

fxn commented Jan 9, 2020

That is very strange:

% cat app/helpers/eclubs/members_helper.rb
module Eclubs::MembersHelper
end
% bin/rails c
Loading development environment (Rails 6.0.2.1)
irb(main):001:0> Eclubs::MembersHelper
=> Eclubs::MembersHelper

Could you please throw

Rails.autoloaders.log!

in config/application.rb after the line that loads the framework defaults, trigger the error, and share the output?

@obromios
Copy link

@fxn, I have added the autoloaders command but am having trouble duplicating exception. I will explore further and come back to you.

@obromios
Copy link

obromios commented Jan 17, 2020

@fxn, I solved the problem, thanks to your suggestion to use Rails.autoloaders.log!. The log showed that immediately prior to the exception occuring there was the following line;

Zeitwerk@rails.main: constant EclubsHelper loaded from file app/helpers/eclubs_helper.rb

Prior to creating the Eclubs namespace, the rails generator had created an unused file
#app/helpers/eclubs_helper.rb

module EclubsHelper
end

The solution in this case was just to delete app/helpers/eclubs_helper.rb.

I have a work flow of creating name spaces after a particular controller became too complex, which means there were quite a few other cases of having an ordinary (non namespaced) helper clashing with the namespace. For some reason everything would normally load OK, but then occasionally an exception would be thrown on one of the these duplicate name clashes. I will refactor these duplicates, unless you can suggest an easier fix.

Thank you for your help.

@obromios
Copy link

obromios commented Feb 1, 2020

Having eliminated all helpers with a name starting with admin_, I am still intermittently seeing this problem. Below is a file with the output with Rails.autoloaders.log! enabled.

The exception occurs at line 556. The file that is causing the exception contains the following:

module Admin::ProfessionalContactsHelper
end

This occurs intermittently when I start rails console. It can also happen in a rspec test, but if I stop spring and rerun the test it is fixed.

I am not sure if it is related, but I am also having another issue with the loading process.

temp_zeitwert.txt

@fxn
Copy link
Member Author

fxn commented Feb 1, 2020

Hi @obromios, I have created #38365 to work on it, since this issue was about something that happened during betas with some sspecific versions of stuff. What you describe in principle should not be related to this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants