Elegant way to structure models into subfolders without creating submodules

Ruby on-RailsActiverecord

Ruby on-Rails Problem Overview


I have numerous models in my app/models folder. I'd like to clean this folder up a little bit. Move models that belong to each other in subfolders. The problem is that by convention the model class is namespaced into an according module.

E.g.

app/models/blog/post.rb
app/models/blog/comment.rb
app/models/user.rb

so that:

app/models/blog/post.rb

class Post < ActiveRecord
end

and not

class Blog::Post < ActiveRecord
end

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

Here is what I used for Rails 3:

config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]

This configuration tells Rails to scan all the app/models subfolders recursively and load all found models. No namespacing required.

Solution 2 - Ruby on-Rails

We needed to do this, and there is a very simple way.

move your models into the sub-folders, and then tell rails to load files from all subfolders in your environment.rb file:

config.load_paths += Dir["#{RAILS_ROOT}/app/models/*"].find_all { |f| File.stat(f).directory? }

No namespacing required, and the models can be referred to as normal in your app

Solution 3 - Ruby on-Rails

I also created subfolders, and then added the following to the application.rb file:

config.autoload_paths += Dir["#{config.root}/app/models/**/"]

But doing this alone isn't enough when subfolders are named using the same name as a model (e.g., a folder 'user' containing several files, one of which is 'user'). This was causing all kinds of errors in my code until I found that it could be solved by simply giving the folders names that are different from the models (e.g., 'user models') they contain. I found the suggestion at http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/, which actually points to this question.

Solution 4 - Ruby on-Rails

So I used to have in Rails 2 something like this:

config.autoload_paths += Dir["#{config.root}/app/models/**/"]

And the following files:

  • app/models/user/base.rb: class User::Base
  • app/models/user/admin.rb: class User::Admin

When I upgraded to Rails 3, I kept getting an error along these lines: Expected .../app/models/user/foo.rb to define Foo. This clearly seemed crazy since Rails 2 automatically assumed that what you put in user/foo.rb would be User::Foo not just Foo.

So the way I ended up solving this was getting rid of model subdirectories in autoload_paths and doing something like this:

I created app/models/user.rb with:

module User
  autoload :User, 'user/base'
  autoload :User, 'user/admin'
end

Solution 5 - Ruby on-Rails

Maybe you could look upon RailsEngines. It's not exactly what you need, but could gave you some ideas.

Other than that, if your script seems to work fine (you could also just read all the files on each subfolder on model and require them), I don't see any problem against it.

Solution 6 - Ruby on-Rails

this version of Tilendor's solution works with Rails 3

config.load_paths and RAILS_ROOT are deprecated in Rails 3, also you should put it in the config block of the config/application.rb, not environment.rb

config.autoload_paths += Dir["#{Rails.root.to_s}/app/models/*"].find_all { |f| File.stat(f).directory? }

Solution 7 - Ruby on-Rails

In my Rails 3.2.3 app, after i moved some models to subdirectories, i have stumbled into errors like

Expected /.../.../app/models/project/project_category.rb to define Project::ProjectCategory

for association calls (example: Project.first.project_category).

In the end, the workaround i found was to set :class_name for every association to model in subdirectory...

class Project < ActiveRecord::Base

  belongs_to :project_category, :class_name => "::ProjectCategory"

end

"::" part here points to Rails that ProjectCategory model has no namespace, despite the fact that is defined in a 'models/project' subdirectory.

Solution 8 - Ruby on-Rails

This worked for me in Rails 5.

Adding the following to application.rb

config.autoload_paths += Dir[ Rails.root.join('app/models/**/') ]

Beware though that you cannot have the same name on your folder as any of your models.

Solution 9 - Ruby on-Rails

Until I find a better solution I've created a init.rb in the app/models folder:

app/models/init.rb

%w[blog].each do |folder|
  path = [File.dirname(__FILE__), folder, "*.rb"].join('/')
  Dir[path].each {|file| require file }  
end

Servers the purpose until now.

Solution 10 - Ruby on-Rails

All the above answers didn't work for me. Somehow 'models' folder was loaded with subfolders, which resulted in 'Expected to contain ::.

Most of my subdirs were STI classes so I've moved them to app/models_sti//*. Then all I needed to do was to put in application.rb

#config.autoload_paths += Dir["#{Rails.root.to_s}/app/models/**/"]
# Loaded dynamically (cache_classes == false ?)
config.autoload_paths << Rails.root.join('app', 'models').to_s
config.autoload_paths += Dir["#{Rails.root.to_s}/app/models_sti/*"].find_all { |f| File.stat(f).directory? }

# Eager_load_paths - used only when cache_classes == true [rails spec says so]
config.eager_load_paths.delete_if do |path|
  # If I didn't delete it from eager_load_paths I got errors even in develop
  path == Rails.root.join('app', 'models_sti').to_s
end
config.eager_load_paths += config.autoload_paths

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsebView Question on Stackoverflow
Solution 1 - Ruby on-RailsIon Br.View Answer on Stackoverflow
Solution 2 - Ruby on-RailsTilendorView Answer on Stackoverflow
Solution 3 - Ruby on-RailspickwickView Answer on Stackoverflow
Solution 4 - Ruby on-RailsAaron GibralterView Answer on Stackoverflow
Solution 5 - Ruby on-RailsAlessandra PereyraView Answer on Stackoverflow
Solution 6 - Ruby on-Railschris_bView Answer on Stackoverflow
Solution 7 - Ruby on-RailsTim ZaripovView Answer on Stackoverflow
Solution 8 - Ruby on-RailsFellow StrangerView Answer on Stackoverflow
Solution 9 - Ruby on-RailssebView Answer on Stackoverflow
Solution 10 - Ruby on-RailsKangurView Answer on Stackoverflow