Search code examples
ruby-on-rails-4exceptionsuperclassrails-enginessubclassing

Rails 4: Superclass mismatch exception in production environment that can't be


I am currently working on a bigger rails application, that uses engines for sub applications, with the following folder structure:

- app
  \- controllers
     \- global
        \- base_controller.rb
        \- configurations_controller.rb
     \- application_controller.rb
- ...
- engines
  \- engine_1
     \- app
        \- controllers
           \- engine_1
              \- application_controller.rb
              \- global
                 \- base_controller.rb
                 \- configurations_controller.rb

- ...

The setup of the controllers is as follows:

# app/controllers/global/base_controller.rb
class Global::BaseController < ApplicationController
end


# app/controllers/global/configurations_controller.rb
class Global::BaseController < Global::BaseController
end


# engines/engine_1/app/controllers/engine_1/application_controller.rb
module Engine1
  class ApplicationController < ::ApplicationController
  end
end


# engines/engine_1/app/controllers/engine_1/global/base_controller.rb
require_dependency "engine_1/application_controller"

module Engine1
  class Global::BaseController < ApplicationController
  end
end


# engines/engine_1/app/controllers/engine_1/global/configurations_controller.rb

module Engine1
  class Global::ConfigurationsController < Global::BaseController
  end
end

My routes files

# config/routes.rb
Rails.application.routes.draw do
  namespace :global do
    resource :configuration, only: [:show, :update]
  end

  mount Engine1::Engine, at: '/engine_1', as: :engine_1
end


# engines/engine_1/config/routes.rb
Engine1::Engine.routes.draw do
  namespace :global do
    resource :configuration, only: [:show, :update]
  end
end

In development environment everything works as expected. But in production environment, when the application gets started and classes are being eager loaded, I get a Superclass mismatch exception for Global::BaseController when it tries to load the Global::ConfigurationsController in module Engine1.

I overcame the problem (temporarily) in renaming the Engine1::Global::BaseController but I don't understand why this is not working in production mode only?


Solution

  • The Superclass mismatch error happens when there is a re-definition of a class. According to your question, the error is on BaseController. Here's my theory looking at what you've posted. BaseController is being defined more than once, so that when this code is loaded the BaseController is ambiguous:

    module Engine1
      class Global::ConfigurationsController < Global::BaseController
      end
    end
    

    It appears that at load time there are 2 definitions extant of BaseController.

    # app/controllers/global/base_controller.rb
    class Global::BaseController < ApplicationController
    end
    
    
    # app/controllers/global/configurations_controller.rb
    class Global::BaseController < Global::BaseController
    end
    

    So which BaseController should be loaded? To you it is obvious what should be done here, and I think I understand what you're trying to do with adding a level of indirection to your configuration. But Rails sees these two definitions as the same name at load time and throws the superclass error.


    These are my thoughts about your question. I'm curious what you think.