zeitwerk: Failed to get origin class after including gem using zeitwerk

I am upgrading rails. My project has a custom gem and has a controller inside (with namespace). But if I include the gem I can not get the controller (with same namespace) in the rails main project successfully (which I can get that controller without including the gem). Any idea or clue about this?

Many Thanks. 😭

Detail:

(In Gem) https://github.com/crokobit/at-gem-controllers-for-test/ lib/at-gem-controllers/api_v1/base_controller.rb

Setting of zeitwerk in gem: https://github.com/crokobit/at-gem-controllers-for-test/blob/master/lib/at-gem-controllers.rb

class Controllers
end

require 'rubygems'
require 'bundler/setup'
Bundler.require(:default)
require 'zeitwerk'

loader = Zeitwerk::Loader.for_gem
loader.inflector.inflect(
  "at-gem-controllers"   => "Controllers",
)
loader.push_dir("#{__dir__}/at-gem-controllers")
loader.setup

(In Rails) https://github.com/crokobit/test-zeitwerk

has controller: app/controllers/api_v1/ping_controller.rb

I found that when I am including the at-gem-controllers gem, I will not get the ApiV1::PingController but I can get the Api::BaseController. however, if I remove the gem, I will get the ApiV1::PingController.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 21 (13 by maintainers)

Most upvoted comments

OK, I finally consider this to be a bug. Let me explain.

Documentation says you should load shared namespaces beforehand, and your application is doing so by loading the gem. The gem provides the namespace, and as far as client code is concerned, the namespace is there. It does not matter if the constant exists as such, or it only has an autoload defined for it. Zeitwerk supported the first case, but not the second one.

If the gem says all you need to do to function is requiring the top-level file, that should be it. It does not matter whether that’s true because it uses Zeitwerk, or because it issues manual require calls, or because it issues manual autoload calls. The fact is that by requireing the top-level file the namespace is available.

So, I have added support for this use case in https://github.com/fxn/zeitwerk/commit/1862b6feadd564f833dd124834647c2401add310.

By now, loader.eager_load is a workaround, but in the next release you’ll be able to delete it.

Thanks!

loader.eager_load only eager loads the files managed by loader.

It all works out of the box if different loaders manage different namespaces, which is the common case. You only need to be careful in the documented case of shared namespaces, and this case does not depend on lazy/eager loading, it has to be addressed anyway.