Search code examples
ruby-on-railsrubyautoload

Rails: Rake gives Zeitwerk error when mentioning one model class in another


So I'm having a really weird rails problem (6.1.4). Everything runs fine in normal operation but when I try to use some of my models from inside a rake task (e.g. code in seeds.rb) I get the following error: TypeError: superclass mismatch for class Comment

After doing a bunch of digging, I've narrowed the culprit down to the fact that inside one of my model classes I reference the name of another. Specifically, the following code suffices to create the problem

class Question < ApplicationRecord
  include JsonHelpers
  include PostableHelpers

  puts "#{Comment.inspect}"
end

Note that the error still happens when I comment out the whole body of the Comment class so it is just

class Comment < ApplicationRecord
end

Where Comment is another model (in models/comment.rb inheriting from ApplicationRecord). If I delete the reference to Comment in Question there is no problem (but any such reference causes the issue), nor is there a problem when I simply respond to web requests. (In my real code I have a class macro which creates a polymorphic belongs_to and takes the allowed related classes to define some utility methods and validation).

It feels like it's some kind of problem with loading constants but I have no idea how to go about fixing it. Even a simple pointer in the right direction would be super helpful.


I've included the whole error below for completeness. I'm happy to add more code if it helps, but short of just posting the whole application I'm not sure what would be relevant.

/Users/TruePath/Build/math-site/app/models/comment.rb:1:in `<main>'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:27:in `require'
/Users/TruePath/Build/math-site/app/models/question.rb:8:in `<class:Question>'
/Users/TruePath/Build/math-site/app/models/question.rb:1:in `<main>'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:27:in `require'
/Users/TruePath/Build/math-site/app/models/post.rb:18:in `<class:Post>'
/Users/TruePath/Build/math-site/app/models/post.rb:1:in `<main>'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:27:in `require'
/Users/TruePath/Build/math-site/app/models/tagging.rb:5:in `<class:Tagging>'
/Users/TruePath/Build/math-site/app/models/tagging.rb:1:in `<main>'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:27:in `require'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/inflector/methods.rb:288:in `const_get'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/inflector/methods.rb:288:in `block in constantize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/inflector/methods.rb:284:in `each'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/inflector/methods.rb:284:in `inject'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/inflector/methods.rb:284:in `constantize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/inflector/methods.rb:330:in `safe_constantize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/dependencies/zeitwerk_integration.rb:23:in `safe_constantize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/inheritance.rb:230:in `block in compute_type'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/inheritance.rb:229:in `each'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/inheritance.rb:229:in `compute_type'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/reflection.rb:409:in `compute_class'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/reflection.rb:366:in `klass'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/reflection.rb:791:in `source_reflection'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/reflection.rb:932:in `check_validity!'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/associations/association.rb:41:in `initialize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/associations/has_many_through_association.rb:10:in `initialize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/associations.rb:315:in `new'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/associations.rb:315:in `association'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/associations/builder/association.rb:103:in `tags'
/Users/TruePath/Build/math-site/app/models/page.rb:207:in `update_history'
/Users/TruePath/Build/math-site/app/models/page.rb:101:in `build_by_user'
/Users/TruePath/Build/math-site/db/seeds.rb:99:in `block in <main>'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/connection_adapters/abstract/database_statements.rb:320:in `block in transaction'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/connection_adapters/abstract/transaction.rb:319:in `block in within_new_transaction'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/connection_adapters/abstract/transaction.rb:317:in `within_new_transaction'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/connection_adapters/abstract/database_statements.rb:320:in `transaction'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/transactions.rb:209:in `transaction'
/Users/TruePath/Build/math-site/db/seeds.rb:84:in `<main>'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:48:in `load'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/bootsnap-1.10.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:48:in `load'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/railties-6.1.4/lib/rails/engine.rb:566:in `block in load_seed'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/callbacks.rb:117:in `block in run_callbacks'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/execution_wrapper.rb:88:in `wrap'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/railties-6.1.4/lib/rails/engine.rb:640:in `block (2 levels) in <class:Engine>'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/callbacks.rb:126:in `instance_exec'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/callbacks.rb:126:in `block in run_callbacks'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activesupport-6.1.4/lib/active_support/callbacks.rb:137:in `run_callbacks'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/railties-6.1.4/lib/rails/engine.rb:566:in `load_seed'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/tasks/database_tasks.rb:450:in `load_seed'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/activerecord-6.1.4/lib/active_record/railties/databases.rake:392:in `block (2 levels) in <main>'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `block in execute'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `each'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `execute'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/task.rb:219:in `block in invoke_with_call_chain'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/task.rb:188:in `invoke'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/application.rb:160:in `invoke_task'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block (2 levels) in top_level'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `each'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block in top_level'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/application.rb:125:in `run_with_threads'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/application.rb:110:in `top_level'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/application.rb:83:in `block in run'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/application.rb:186:in `standard_exception_handling'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/lib/rake/application.rb:80:in `run'
/opt/local/lib/ruby3.1/gems/3.1.0/gems/rake-13.0.6/exe/rake:27:in `<top (required)>'
/opt/local/bin/rake:25:in `load'
/opt/local/bin/rake:25:in `<main>'
/opt/local/bin/ruby_executable_hooks3.1:22:in `eval'
/opt/local/bin/ruby_executable_hooks3.1:22:in `<main>'
Tasks: TOP => db:seed

Solution

  • Ok, thanks to the amazing help by Xavier Noria I finally tracked it down. I'll include the answer here in case anyone else runs into the problem.

    Ultimately, the issues was that I had a rake task that was defined as follows:

    require 'rexml/document'
    include REXML
    
    namespace :db do
      desc "Delete all unconfirmed users after 1 day"
      task :import_journal_abbrevs => :environment do
    
      ...
    
    end
    

    Unfortunately, REXML defines a class named Comment which was conflicting with my model class of the same name. As this answer warns placing the include at the top level pollutes the Object namespace. Instead, I should have written

    require 'rexml/document'
    
    namespace :db do
      desc "Delete all unconfirmed users after 1 day"
      task :import_journal_abbrevs => :environment do
      include REXML
      ...
    end