rails: Autoloading helpers fails initially in Rails 7.x, but succeeds on the second attempt
Steps to reproduce
When I try loading a Rails Application in Ruby 7.0, the app/helpers
autoload path does not appear to be working from my gems engine.
Here’s the script to reproduce the issue NOT working under 7.0:
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
gem "sitepress", github: "sitepress/sitepress", branch: "rails-7"
gem "rails", github: "rails/rails", branch: "main"
end
require "sitepress/server" # Load all the stuff needed setup the configuration below.
# Setup defaults for stand-alone Sitepress server in the current path. This
# can, and should, be over-ridden by the end-user in the `config/site.rb` file.
Sitepress.configure do |config|
config.routes = false
config.site = Sitepress::Site.new root_path: "."
end
# Create the module that should autoload without an issues.
require 'fileutils'
FileUtils.mkdir_p 'helpers'
File.write "helpers/foo_helper.rb", "module FooHelper; end;"
# Boot the Rails app
app = Sitepress::Server
app.initialize!
puts "Loading ::SiteController fails in Rails 7.0"
# See https://github.com/sitepress/sitepress/blob/rails-7/sitepress-rails/lib/sitepress/engine.rb#L15 for helper path configuration
# See https://github.com/sitepress/sitepress/blob/rails-7/sitepress-server/lib/sitepress/server.rb for the configuration of the Rails app this boots
p ::SiteController # This will raise an exception
Now for code that shows this WORKING under Rails 6:
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
gem "sitepress", github: "sitepress/sitepress", branch: "rails-7"
gem "rails"
end
require "sitepress/server" # Load all the stuff needed setup the configuration below.
# Setup defaults for stand-alone Sitepress server in the current path. This
# can, and should, be over-ridden by the end-user in the `config/site.rb` file.
Sitepress.configure do |config|
config.routes = false
config.site = Sitepress::Site.new root_path: "."
end
# Create the module that should autoload without an issues.
require 'fileutils'
FileUtils.mkdir_p 'helpers'
File.write "helpers/foo_helper.rb", "module FooHelper; end;"
# Boot the Rails app
app = Sitepress::Server
app.initialize!
puts "Loading ::SiteController succeeds in Rails 6.x"
p ::SiteController # This will not raise an exception
Finally, in a third example under Rails 7.0, I demonstrate that this is WORKING when I attempt to load the constant a second time:
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
gem "sitepress", github: "sitepress/sitepress", branch: "rails-7"
gem "rails", github: "rails/rails", branch: "main"
end
# require "sitepress/boot"
require "sitepress/server" # Load all the stuff needed setup the configuration below.
# Setup defaults for stand-alone Sitepress server in the current path. This
# can, and should, be over-ridden by the end-user in the `config/site.rb` file.
Sitepress.configure do |config|
config.routes = false
config.site = Sitepress::Site.new root_path: "."
end
# Create the module that should autoload without an issues.
require 'fileutils'
FileUtils.mkdir_p 'helpers'
File.write "helpers/foo_helper.rb", "module FooHelper; end;"
# Boot the Rails app
app = Sitepress::Server
app.initialize!
puts "Loading ::SiteController fails in Rails 7.0 on the first attempt"
begin
p ::SiteController
rescue => e
puts "Failed with #{e.inspect}"
end
puts "Loading ::SiteController succeeds in Rails 7.0 on the second attempt"
p ::SiteController
The following source files may be helpful to help diagnose the issue:
- https://github.com/sitepress/sitepress/blob/rails-7/sitepress-rails/lib/sitepress/engine.rb#L15 for helper path configuration
- https://github.com/sitepress/sitepress/blob/rails-7/sitepress-server/lib/sitepress/server.rb for the configuration of the Rails app this boots
Expected behavior
I expect the FooHelper
to be resolved in Rails 7.x, as it does in Rails 6.x. Here’s what a succesful script run looks like from above:
SiteController
Actual behavior
The FooHelper
module is not resolved when Rails attempts to find it. Here’s what the error looks like from the first Rails 7.x script above:
/Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/rails-a692e63bf400/activesupport/lib/active_support/inflector/methods.rb:280:in `constantize': uninitialized constant FooHelper (NameError)
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/rails-a692e63bf400/activesupport/lib/active_support/core_ext/string/inflections.rb:74:in `constantize'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/rails-a692e63bf400/actionpack/lib/abstract_controller/helpers.rb:177:in `block in modules_for_helpers'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/rails-a692e63bf400/actionpack/lib/abstract_controller/helpers.rb:170:in `map!'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/rails-a692e63bf400/actionpack/lib/abstract_controller/helpers.rb:170:in `modules_for_helpers'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/rails-a692e63bf400/actionpack/lib/action_controller/metal/helpers.rb:104:in `modules_for_helpers'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/rails-a692e63bf400/actionpack/lib/abstract_controller/helpers.rb:148:in `helper'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/rails-a692e63bf400/actionpack/lib/action_controller/railties/helpers.rb:19:in `inherited'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/sitepress-cd078ab7255c/sitepress-server/rails/app/controllers/application_controller.rb:1:in `<top (required)>'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/zeitwerk-2.5.0.beta3/lib/zeitwerk/kernel.rb:27:in `require'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/zeitwerk-2.5.0.beta3/lib/zeitwerk/kernel.rb:27:in `require'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/bundler/gems/sitepress-cd078ab7255c/sitepress-server/rails/app/controllers/site_controller.rb:1:in `<top (required)>'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/zeitwerk-2.5.0.beta3/lib/zeitwerk/kernel.rb:27:in `require'
from /Users/bradgessler/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/zeitwerk-2.5.0.beta3/lib/zeitwerk/kernel.rb:27:in `require'
from bug.rb:40:in `<main>'
System configuration
Rails version: 7.x
Ruby version: 3.x
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 20 (9 by maintainers)
Commits related to this issue
- Restore set_autoload_path triggering before bootstrap Fixes #43205. — committed to rails/rails by fxn 3 years ago
- fix(rails console): change session host to application default options url or fallback refactor: add port number refactor: add default fallback if default options has no host test(rails console): s... — committed to DerekCrosson/rails by deleted user 3 years ago
Thanks for patching it and a huge thanks for maintaining Zeitwerk and all of your contributions to Rails.
Fantastic @bradgessler! We have it narrowed down. I’ll think about it and will write back.
I’ve played around for a few minutes. Some details I see:
First, let’s eliminate case (3) from the equation. In Ruby, you know that if you have this
and then
That works, because the
Foo
constant was actually defined. You are not loading again and succeeding, it was the first load the succeeded partially. Of course, instances ofFoo
won’t respond to#x
, that is expected.That is in essence what is happening in case (3).
There’s one difference between case (1) and case (2), if you
after initialization, you’ll see
tmp/helpers
is present in (2), and missing in (1).I don’t know why is that yet, just reached this point by now, but looks like the problem is the configurations, for some reason, are different between both versions.
I’ll try to dig into it, thanks a lot for testing the alpha ❤️.