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.
For example, 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.
🍻
Top comments (0)