Search code examples
ruby-on-railsrails-engines

Why explicitly require application controller in Rails engines?


I am building a Rails engine and I noticed that if I generate a controller like so rails g controller test it gives me the following output:

require_dependency "my_app/application_controller"

module MyApp
 class TestController < ApplicationController
 end
end

Why is it necessary to require the engine's application controller, the nesting under module MyApp should already disambiguate the ApplicationControlller from the main app's.


Solution

  • So I did some digging through the rails source and found the commit who introduced this feature (sha: 7c95be54)

    Fix generators to help with ambiguous ApplicationController issue

    In development mode, dependencies are loaded dynamically at runtime, using const_missing. Because of that, when one of the constants is already loaded and const_missing is not triggered, user can end up with unexpected results.

    Given such file in an Engine:

    module Blog
    class PostsController < ApplicationController
       end
    end
    

    If you load it first, before loading any application files, it will correctly load Blog::ApplicationController, because second line will hit const_missing. However if you load ApplicationController first, the constant will be loaded already, const_missing hook will not be fired and in result PostsController will inherit from ApplicationController instead of Blog::ApplicationController.

    Since it can't be fixed in AS::Dependencies, the easiest fix is to just explicitly load application controller.

    So as one could have guessed, this comes down to an autoloading issue! So remember kids, always make explicit what you import when dealing with potentially ambiguous code. What is really disconcerting is that the namespacing introduced by engines, really doesn't provide too much isolation, imagine you reference some other non-rails class in your engine and the including main app happens to have a constant of the same name, the same issue would apply.