I'm working on upgrading a large legacy Rails application from 5.2 to 6.0. In the process of fixing the autoload paths, I've run into an issue where Rails/Zeitwerk seem to be breaking their own rules about how the names of constants are defined in relation to their filenames. I can't share actual code from this application, but the situation is essentially this:
In config/application.rb
:
config.autoload_paths << "#{config.root}/app/models/coupons"
In app/models/coupons/burgerfrenchfry_coupon.rb
:
class BurgerfrenchfryCoupon << ApplicationRecord
end
When another class in the application references the BurgerfrenchfryCoupon
class, a NameError
is thrown with BurgerFrenchfryCoupon
as a suggested classname (that class does not exist in the application). When I require the app/models/coupons/burgerfrenchfry_coupon
path directly in the file referencing BurgerfrenchfryCoupon
I get a Zeitwerk error: Zeitwerk::NameError: expected file /redacted/app/models/coupons/burgerfrenchfry_coupon.rb to define constant BurgerFrenchfryCoupon, but didn't
I've done a thorough search of the application to find anywhere where the expectation could have been customized and I've come up with nothing. Does anyone have any ideas about the follow:
- Why this is happening?
- Where or how an override on the constant name expectation might have been made?
- How I can configure Rails to recognize that this constant should be defined in this file without changing all of the references to it in the application to
BurgerFrenchfryCoupon
?
CodePudding user response:
The problem is that, for some reason, the autoloader's inflector is configured to camelize "burgerfrenchfry_coupon" as "BurgerFrenchfryCoupon". If using Active Support inflections (the default), there's some custom inflection rule somewhere that affects this.
You can fix this particular one without affecting the rest of the application by overriding this way:
# config/initializers/autoloading.rb
inflector = Rails.autoloaders.main.inflector
inflector.inflect("burgerfrenchfry_coupon" => "BurgerfrenchfryCoupon")
That sets a special mapping in the autoloader's inflector that ignores everything else.
CodePudding user response:
The answer here ended up being a custom inflection that had been added to activesupport.
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym 'BurgerFrenchfry'
end
As this inflection was necessary for other parts of the application,
to fix the Zeitwerk error I added the file config/initializers/zeitwerk.rb
with the following content:
Rails.autoloaders.each do |autoloader|
autoloader.inflector.inflect(
"burgerfrenchfry_coupon" => "BurgerfrenchfryCoupon"
)
end
Which overrides the inflection for this one file