Elegant way to structure models into subfolders without creating submodules
Ruby on-RailsActiverecordRuby 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
Most of my subdirs were STI classes so I've moved them to app/models_sti/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