Auto-loading consumers from a Rails 6 app requires using classic autoloader
Summary
Currently Hutch does not work with Rails 6 (rc1) out-of-the-box since the latter defaults to using Zeitwerk for autoloading paths.
Steps to Reproduce
- Generate a new Rails 6 application.
- Add
hutchas a dependency. - Create a consumer under
<rails_root>/app/consumers/foo_consumer.rb. - Ensure RabbitMQ is running.
- Run Hutch from the Rails root directory like so:
bundle exec hutch --autoload-rails
Expected Result
The output should include lines such as these:
2019-07-01T15:54:24Z 6 INFO -- found rails project (.), booting app in development environment
<...>
2019-07-01T15:54:24Z 6 INFO -- setting up queues
2019-07-01T15:54:24Z 6 INFO -- setting up queue: consumers:foo_consumer
Actual Result
The output looks more like this:
2019-07-01T15:47:48Z 6 INFO -- found rails project (.), booting app in development environment
<...>
2019-07-01T15:47:48Z 6 WARN -- no consumer loaded, ensure there's no configuration issue
2019-07-01T15:47:48Z 6 INFO -- setting up queues
Known Workaround
Add the following to <rails_root>/config/application.rb:
config.autoloader = :classic
What can Hutch do about this?
I've got this issue, too, and I see that the consumers are registered when the Hutch::Consumer mixin is included and this doesn't seem to play well with the development environment's lazy loading.
As a temporary fix one can set config.eager_load to true or trigger the consumers' autoloading in an initializer, but I'm wondering if there are better ways to handle this issue.
I'm not familiar with this Rails autoloader but in general, you have one lazy init code path that's unaware of another lazy init code path. Something (such as eager load) has to kick the tires. In case of Rails, it probably should be driven by Rails and not Hutch?
I guess so, or the consumers registration could happen differently - I'm not sure what would work best, though. After some study I've found a better workaround for a Rails 6 app using the new zeitwerk loader, ie an initializer like this:
autoloader = Rails.autoloaders.main
Dir.glob(File.join("**", "*_consumer.rb")).each do |consumer|
autoloader.preload(consumer)
end
Would you be interested in submitting a documentation PR?
On Thu, Feb 13, 2020 at 11:31 AM Paolo Zaccagnini [email protected] wrote:
I guess so, or the consumers registration could happen differently - I'm not sure what would work best, though. After some study I've found a better workaround for a Rails 6 app using the new zeitwerk loader, ie an initializer like this:
autoloader = Rails.autoloaders.main Dir.glob(File.join("**", "*_consumer.rb")).each do |consumer| autoloader.preload(consumer)end
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/gocardless/hutch/issues/331?email_source=notifications&email_token=AAAAIQTTCH6JU6OMOUG6PADRCUAOXA5CNFSM4H4T3AAKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOELT3LDY#issuecomment-585610639, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAAIQXY77UX6UXOXYY4RHLRCUAOXANCNFSM4H4T3AAA .
-- MK
Staff Software Engineer, Pivotal/RabbitMQ
Will do, and would be glad to have some zeitwerk expert to chime in if there are better ways to tackle this issue
Here it is, feel free to edit/improve/close: https://github.com/gocardless/hutch/pull/342
That preload method is deprecated and was removed in Zeitwerk 2.5, released last October: https://github.com/fxn/zeitwerk/blob/eec3fb6b66394c9f04b15cff3821aca68470d1aa/CHANGELOG.md#250-20-october-2021
I'm not sure if the on_setup block is a sufficient replacement?
If you're on Zeitwerk >2.5 and Ruby >=2.7 (I think that's when descendants was introduced?) and have a class that all your consumers descend from, adding this to your hutch.rb initializer seems to work:
Rails.autoloaders.main.on_setup do
CustomConsumerClass.descendants.each { |cname| const_get(cname) }
end
Although I can't figure out how to get code reloading to work, I need to reboot the hutch process entirely for changes to get picked up (that was true before this change as well).
I'd happily adopt a PR that focuses on Rails 6+ and retains compatibility with the previous autoloader if, say, the user opts-in by requiring a specific Hutch file.