I recently upgraded my application from rails 6 to 7 and I am facing one issue due to zeitwerk naming convention. I have a below file which I want to autoload:
app/models/dw/hospital.rb
module DW
class Hospital < DataWarehouse
def self.columns
super.reject{|column| column.name == 'tableau_user' }
end
end
end
I tried autoloading this file by adding the following line in my application.rb
file:
config.autoload_once_paths << 'app/models/dw'
But when I am starting the server I am getting the following error:
expected file app/models/dw/hospital.rb to define constant Hospital, but didn't (Zeitwerk::NameError)
I'm not sure why this is throwing such error since the constant is already defined. I suspect it is because the module I have defined before the class. Please let me know if anybody how to fix this. I have been stuck at this far too long.
CodePudding user response:
Looks like the problem with uppercase
Normally PascalCase
(aka CamelCase
) class name must correspond to the snake_case
file name. The same rule applies to namespaces and folders
But you have DW
in dw
folder (not Dw
)
You can create some initializer like this
# config/initializers/zeitwerk.rb
Rails.autoloaders.each do |autoloader|
autoloader.inflector = Zeitwerk::Inflector.new
autoloader.inflector.inflect(
'dw' => 'DW',
# other special cases
)
end
CodePudding user response:
Because you've add app/models/dw
to autoload paths, you have to define Hospital
but your definition is namespaced DW::Hospital
. You don't need to touch autoload config, app/models
is already in autoload_paths
:
>> ActiveSupport::Dependencies.autoload_paths
=>
...
"/home/alex/code/stackoverflow/app/jobs",
"/home/alex/code/stackoverflow/app/mailers",
"/home/alex/code/stackoverflow/app/models", # <======
...
These are so called root directories. It means file structure relative to app/models
have to correspond to module/class names.
So if you have dw/hospital.rb
in any of the root directories you have to define Dw::Hospital
, which you've defined already. You have to watch for inflections as well, it should be Dw
, unless you have an acronym inflection rule or zeitwerk inflection:
>> "dw".camelize
=> "Dw"
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym "DW"
end
>> "dw".camelize
=> "DW"
If you must nest root directories:
# you want it to be reloadable, don't you?
config.autoload_paths << "app/models/dw"
# app/models/dw/hospital.rb
class Hospital
end