Home > Software engineering >  Zeitwerk::NameError due to Module name in rails 7
Zeitwerk::NameError due to Module name in rails 7

Time:09-16

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
  • Related