Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 6+, zeitwerk autoloader and namedspaced constants

The Rails 6+ default autoloader is zeitwerk, which seems like a great improvement over previous approaches.

However, zeitwork follows the convention for Rails projects that anything in app/* is autoloaded and doesn't need to be namespaced.

This works great for app/models/user.rb because you don't have to use Models::User but can just reference User.

However, I added my own app/services directory and I namespace my service objects as Services::Users::Create, which would map to app/services/users/create.rb.

Zeitwork is throwing errors that my class constants don't exist, since it is expecting Users::Create (without the Services:: prefix).

Is there anyway to configure zeitwork to require the Services:: namespace in these instances? In my opinion, it is a much cleaner to read the code as Services::Users::Create and know that you're looking in the app/services/users/create.rb file.

If you just had Users::Create, an average Rails developer would probably look for the app/models/users/create.rb file.

I don't like the approach of naming it Users::CreateService, it just seems very inelegant to me.

I can't be the only one who uses conventions like this; has anyone else come across a solution? I'm still going through all the zeitwerk documentation looking for a solution but haven't found one yet.

like image 349
Dan L Avatar asked Nov 21 '19 18:11

Dan L


People also ask

What is Zeitwerk mode in rails?

Starting with Rails 6, Rails ships with a new and better way to autoload, which delegates to the Zeitwerk gem. This is zeitwerk mode. By default, applications loading the 6.0 and 6.1 framework defaults run in zeitwerk mode, and this is the only mode available in Rails 7.

What does Zeitwerk mean?

The ZEITWERK is the first mechanical wristwatch that displays hours and minutes with jumping numerals. Its trailblazing design expresses the innovative concept.

How does autoload work in Rails?

Rails automatically reloads classes and modules if application files in the autoload paths change. More precisely, if the web server is running and application files have been modified, Rails unloads all autoloaded constants managed by the main autoloader just before the next request is processed.

What is autoload in Ruby?

The Module#autoload method registers a file path to be loaded the first time that a specified module or class is accessed in the namespace of the calling module or class.


1 Answers

https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#having-app-in-the-autoload-paths

Some projects want something like app/api/base.rb to define API::Base, and add app to the autoload paths to accomplish that in classic mode. Since Rails adds all subdirectories of app to the autoload paths automatically, we have another situation in which there are nested root directories, so that setup no longer works. Similar principle we explained above with concerns.

If you want to keep that structure, you'll need to delete the subdirectory from the autoload paths in an initializer:

ActiveSupport::Dependencies.autoload_paths.delete("#{Rails.root}/app/api")
like image 196
Cantin Xu Avatar answered Oct 21 '22 07:10

Cantin Xu