Rails app with hanami fails on hanami dependency I didn't (want to) add

After we sorted through a bunch of problems regarding running a (partial) hanami in a Rails app, I worked to incorporate the learnings and fixes into the sample application I am working on.

For those interested, the prior work is in

I have uploaded an archive of the current state I am working with to Proton Drive

After you extract it you can bundle everything with bundle --local to get all the same versions of things I am using.

When I then run bin/rails zeitwerk:check --trace you can see my problem:

** Invoke zeitwerk:check (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute zeitwerk:check
Hold on, I am eager loading the application.
bin/rails aborted!
LoadError: cannot load such file -- hanami/assets (LoadError)

I am using zeitwerk’s check as a shortcut to eager loading the application. I am not sure why it suddenly wants hanami/assets…

And, once I give it assets, it wants db and it wants … I think it eventually forces me to installed all hanami gems

@stephan You’re doing amazing work uncovering bugs for us. Thank you! :heart:

I think I know the fix here. Give me 30 minutes.

1 Like

@stephan Can you please tell me how to disable the gem caching (into vendor/gems/) for this codebase? It makes working with a local copy of the hanami gem really hard.

UPDATE: Actually, scratch that request. Initially, even after removing .bundle/config and removing everything inside vendor/gems, newly installed gems seemed to be reappearing there. But after deleting the directory entirely and trying again in a new shell, it seems I’m good.

@stephan I made some headway. This is about excluding files from Zeitwerk autoloading that require gems that may not be installed. But I’ve just got one or two hitches to figure out. Bear with me and I’ll get you a working answer ASAP.

1 Like

Thank you!

I had previously found hanami/lib/hanami/extensions.rb at main · hanami/hanami · GitHub and suspected it had something to do with this. I think it is not, my hunch is that adding more code like this is what’s needed?

Either way! Again, thank you for the help!

Hi again @stephan! OK, try this:

Update your Gemfile to depend on these PR branches:

gem "hanami", github: "hanami/hanami", branch: "exclude-more-files-from-zeitwerk"
gem "hanami-view", github: "hanami/view", branch: "exclude-more-files-from-zeitwerk"

Then, remove everything inside slices/hanami_predictor/relations/ as well as the slices/hanami_predictor/db directories. These depend on the hanami-db gem, which you don’t have bundled.

After that, running bin/rails zeitwerk:check --trace should get you an error that is more within your control:

❯ bin/rails zeitwerk:check --trace
** Invoke zeitwerk:check (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute zeitwerk:check
Hold on, I am eager loading the application.
bin/rails aborted!
NoMethodError: undefined method 'describe' for module RSpec (NoMethodError)

RSpec.describe "Predictor Slice" do
     ^^^^^^^^^
/Users/tim/Source/scratch/sportsball/slices/hanami_predictor/spec/operations/create_prediction_spec.rb:2:in '<main>'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/3.4.0/bundled_gems.rb:82:in 'Kernel.require'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/3.4.0/bundled_gems.rb:82:in 'block (2 levels) in Kernel#replace_require'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/bootsnap-1.18.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in 'Kernel#require'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/core_ext/kernel.rb:26:in 'Kernel#require'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/cref.rb:63:in 'Module#const_get'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/cref.rb:63:in 'Zeitwerk::Cref#get'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/eager_load.rb:173:in 'block in Zeitwerk::Loader::EagerLoad#actual_eager_load_dir'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/helpers.rb:47:in 'block in Zeitwerk::Loader::Helpers#ls'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/helpers.rb:25:in 'Array#each'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/helpers.rb:25:in 'Zeitwerk::Loader::Helpers#ls'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/eager_load.rb:168:in 'Zeitwerk::Loader::EagerLoad#actual_eager_load_dir'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/eager_load.rb:17:in 'block (2 levels) in Zeitwerk::Loader::EagerLoad#eager_load'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/eager_load.rb:16:in 'Hash#each'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/eager_load.rb:16:in 'block in Zeitwerk::Loader::EagerLoad#eager_load'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/eager_load.rb:10:in 'Thread::Mutex#synchronize'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader/eager_load.rb:10:in 'Zeitwerk::Loader::EagerLoad#eager_load'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader.rb:430:in 'block in Zeitwerk::Loader.eager_load_all'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader.rb:428:in 'Array#each'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/zeitwerk-2.7.2/lib/zeitwerk/loader.rb:428:in 'Zeitwerk::Loader.eager_load_all'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/zeitwerk_checker.rb:6:in 'Rails::ZeitwerkChecker.check'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/tasks/zeitwerk.rake:29:in 'block (2 levels) in <main>'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/task.rb:281:in 'block in Rake::Task#execute'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/task.rb:281:in 'Array#each'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/task.rb:281:in 'Rake::Task#execute'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/task.rb:219:in 'block in Rake::Task#invoke_with_call_chain'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/task.rb:199:in 'Monitor#synchronize'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/task.rb:199:in 'Rake::Task#invoke_with_call_chain'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/task.rb:188:in 'Rake::Task#invoke'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/application.rb:188:in 'Rake::Application#invoke_task'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/application.rb:138:in 'block (2 levels) in Rake::Application#top_level'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/application.rb:138:in 'Array#each'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/application.rb:138:in 'block in Rake::Application#top_level'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/application.rb:147:in 'Rake::Application#run_with_threads'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/application.rb:132:in 'Rake::Application#top_level'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/commands/rake/rake_command.rb:27:in 'block (2 levels) in Rails::Command::RakeCommand.perform'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/application.rb:214:in 'Rake::Application#standard_exception_handling'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/commands/rake/rake_command.rb:27:in 'block in Rails::Command::RakeCommand.perform'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/commands/rake/rake_command.rb:44:in 'block in Rails::Command::RakeCommand.with_rake'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/rake-13.2.1/lib/rake/rake_module.rb:59:in 'Rake.with_application'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/commands/rake/rake_command.rb:41:in 'Rails::Command::RakeCommand.with_rake'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/commands/rake/rake_command.rb:20:in 'Rails::Command::RakeCommand.perform'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/command.rb:156:in 'Rails::Command.invoke_rake'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/command.rb:73:in 'block in Rails::Command.invoke'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/command.rb:149:in 'Rails::Command.with_argv'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/command.rb:69:in 'Rails::Command.invoke'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-7.2.2.1/lib/rails/commands.rb:18:in '<main>'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/3.4.0/bundled_gems.rb:82:in 'Kernel.require'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/3.4.0/bundled_gems.rb:82:in 'block (2 levels) in Kernel#replace_require'
/Users/tim/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/bootsnap-1.18.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in 'Kernel#require'
bin/rails:4:in '<main>'
Tasks: TOP => zeitwerk:check

This error arises because it’s trying to load a spec file, because you’ve put the spec files directly inside the slice directory.

To prevent these from being autoloaded, create a slices/hanami_predictor/config/slice.rb file with the following:

# frozen_string_literal: true

module HanamiPredictor
  class Slice < Hanami::Slice
    autoloader.ignore(File.expand_path(File.join(__dir__, "..", "spec")))
  end
end

After that, running zeitwerk:check is all clear!

❯ bin/rails zeitwerk:check --trace
** Invoke zeitwerk:check (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute zeitwerk:check
Hold on, I am eager loading the application.
All is good!
1 Like

What an amazing and thorough response!! Once again, I can confirm that this works for me also.

Whenever I have gem changes I need to run my source code pipeline from the very beginning and since this is Chapter 10… that will take a minute. I hope to have the generator code and source code pushed up soon and will post back here when I do!

Thank you!

1 Like