What /IS/ the proper way to name-space, place, and configure models/modules in Rails 7?

This is related to my post from yesterday, but approaching the issue differently:

Given the massive breakages with module configuration and loading, between Rails 6 and Rails 7, and big change in loaders from classic to Zeitwerk, what is the new way of organizing models and modules?

I need to have models loaded in models/foo/bar.rb on boot.

How do I configure bar.rb, config/application.rb and Zeitwerk, to autoload “class Bar” in Rails 7.1.x and 7.2.x ?

Code from 6.1 is now breaking, where I previously abided by this nesting:

module Foo
  class Bar
    Stuff
  end
end

I was also referencing the above from controller and elsewhere with Foo::Bar.new()

Nesting one level deeper than that was also possible…

Rails 7.2 does not seem to load models in subdirectories of models/extras

require ‘foo/bar’ no longer works, but did work in v6.

please help! “everything” is miserably and mysteriously broken! (the paths and nested references)

Do you happen to have the same name in application.rb as you root model namespace?

So class Foo in application.rb And a model with namespace

module Foo
  class Bar

  end
end

And this causes things in your models/foo.rb to not kick in in models/foo/bar.rb?

No. in my config/application.rb I use a different, unique name for my module – the module that wraps around “class Application < Rails::Application”

config/application.rb :

module Scrappy
    class Application < Rails::Application
       ...
    end
end

(name has been changed, but it is globally unique, for sure : - ))

I just want to know what is the right way to load modules in subdirectory of app/models , in Rails 7

I am also trying:

config.autoload_paths << "#{Rails.root}/app/models"

config.autoload_paths << "#{Rails.root}/app/models/foo"

as well as:

config.eager_load_paths << Rails.root.join('models')

config.eager_load_paths << Rails.root.join('models/foo')

in config/application.rb

that doesn’t seem to help

Hello, forum

I made some progress – but it is actually a retrogress

I made my old Rails 6.1 modules and their naming work again, without editing the files containing them, simply by switching back to the 6.1 load_defaults in application.rb

config.load_defaults 6.1

To make it work I also had to get rid of my "config.autoload_paths << “#{Rails.root}/app/models/foo” lines (mentioned previously) in application.rb .

When I add the full directory path (“models/foo”) to config.autoload_paths, the loader does load those files but does not construct the reference “Foo::Bar”.

(“Foo::Bar.new()”) throws an error as a nonexistent reference)

The loader treats model files in models/foo as top-level classes/modules (top level namespace), and does not build the “Foo::Bar” reference. Even with load_defaults 6.1

When you remove the full path, the 6.1 defaults do their classic thing


Questions:

  1. The original question: What is the proper way to do the naming in 7.1 ?

  2. What am I forfeiting and/or risking by reverting to the 6.1 load_defaults at this point?

EDIT: I have come to like the classic convention – helps better organize one’s modules… and track them… where the directory structure/hierarchy is reflected in Rails object the reference… The references themselves remind you where things are.

1 Like

You do not need to add autoload paths for anything within /app. /app/* are in the autoload path already. By adding app/models/foo to the autoload path, you are instructing zeitwerk to then look for app/models/foo/foo/bar.rb to find Foo::Bar.

Going from there, you would have to share the actual error messages you are receiving to be able to help you resolve them. Also, is there a particular reason for auto-loading this specific model?

To partially answer Zachery’s post:

I installed a new instance of Rails 7.2.2.1, since the time I posted here, and used a different boot configuration, with application.rb, etc. – less cluttered and closer to install defaults. Zeitwerk is quiet – not even sure if it’s engaged.

The problem with Foo::Bar.new is no longer there. The reason this was important was that this is a rails upgrade, for an existing Rails app, with existing code that implements the models/foo/bar.rb => Foo::Bar.new constructs

Would have had to rename all the references, and I wasn’t sure if the renaming was mandatory or not. So now, apparently, 7.2.2.1 behaves like the old 6.1…

Let me try to clear up some of the confusion.

Everything under app/models doesn’t need to be added into any “load paths” configurations, as long as your directories are modules.

  • app/models/foo/bar.rb expects class Foo::Bar.
  • app/models/foo/bar/baz.rb expects class Foo::Bar::Baz
  • and so on

If you add a sub-directory of app/models into autoload paths, then it becomes the new “starting directory” for rails. This could be useful if you specifically don’t want the directory to become a namespace.

For example, you could do this:

config.autoload_paths << 'app/models/reports'

And then create: app/models/reports/monthly_report.rb. Because the reports directory is now considered one of the “base directories”, the class must be without a namespace: class MonthlyReport.

However, if you are going to do that, I recommend putting this kind of directory directly under app.

app/reports/monthly_report.rb

With this approach you don’t need to mess with autoload_paths at all. Any directory under app/ automatically becomes a “base directory” in which all files are expected without namespaces.