Photo by Julian Hochgesang on Unsplash

I’ve just bumped DeskBeers up to Rails 6.0. One of the new things in this release of Rails is the new way files get autoloaded. zeitwerk is very cool, and you should check it out. I won’t go into the pros, cons, whys and wherefores of the change in this post — many folks smarter than I have attested to those — however I do want to bring up a gotcha and solution that bit me while rolling out the change.

Zeitwerk loads constants based on paths

Again, without wanting to go too much into how stuff works, you need to know that, basically, Zeitwerk sees the path app/models/user.rb and from that knows to autoload the constant (class) User . Which is fine, except when it isn’t.

DeskBeers charges people VAT, and is old enough to have lived through a change in the applicable VAT rate. So, for better or worse, we now have a model in a file called app/models/vat.rb . Zeitwerk sees this and tries to autoload the constant Vat . But because the concept being described is an acronym, the constant (class name) defined in the file isn’t Vat , it’s VAT .

After switching to zeitwerk, we get the error:

Zeitwerk::NameError (expected file /.../app/models/vat.rb to define constant Vat, but didn't)

That’s the error, but it’s easy enough to fix.

Make sure all your acronyms are properly inflected

Rails provides a simple way for you to let it know about acronyms used in your codebase. By adding them to config/initializers/inflections.rb , Zeitwerk will pick up any acronyms like this and load VAT instead of Vat . DeskBeers (now) defines the following:

ActiveSupport::Inflector.inflections do |inflect|

inflect.acronym 'VAT'

inflect.acronym 'CSV'

inflect.acronym 'PDF'

end

Once you’ve done this, you’ll need to grep your project for all occurrences of Vat and replace them with VAT . Note: this applies to partial class names, too, e.g. VatCalculator will need to be updated to VATCalculator and of course every reference to these classes need to be updated as well — time to break out that find and replace in project function.

I think this is a Good Thing, as it’s made us be deliberate about naming things. For instance, in our case, VAT was always inflected, but PDF wasn’t and CSV had clearly been inflected at some point before today but after it was first used, so we had a mix of CSVWhatever and CsvWhatever classes. After fixing this issue, all our acronyms are now properly inflected and consistently referenced across our codebase.

You can also create an initializer for Zeitwerk itself, and for some files that might be appropriate, but in the case where we’re using acronyms, using the Rails inflections initializer, of which Zeitwerk is aware out of the box, forces us to properly name classes so that they can be loaded, without the need for an extra initializer.

Finding problem classes

The best way to make sure Zeitwerk can load all your classes is to have Zeitwerk load all your classes. This won’t happen by default in development mode in Rails, but you can force it by booting up a console and running:

Zeitwerk::Loader.eager_load_all

If you have any sketchy classes, you’ll get an error like:

Zeitwerk::NameError (expected file /.../app/models/csv_whatever.rb to define constant CSVWhatever, but didn't)

Hope that helps.

🍻

UPDATE: Gotcha with acronyms that can be plural

Turns out that simply adding your acronym to the inflections initializer might not be enough if your acronym makes sense in the plural form. This is actually in the Rails docs — you need to also specify the plural form of the acronym in order to get the behaviour you expect:

Note: Acronyms that are passed to pluralize will no longer be recognized, since the acronym will not occur as a delimited unit in the pluralized result. To work around this, you must specify the pluralized form as an acronym as well: acronym 'API'

camelize(pluralize('api')) # => 'Apis' acronym 'APIs'

camelize(pluralize('api')) # => 'APIs'

This will then mean you can create e.g. APIsController and Zeitwerk will find it. Without the plural, Zeitwerk will complain along the lines of