Organize models in subfolders

[From the issue I opened about it: https://github.com/rails/rails/issues/28152]

I don’t know if this is more a bug report or a feature request, or even an SO question :flushed: anyway:

I have a model Activity which has a lot of sti types, so a lot of related class files. I would like to organize them into app/models/activities, in order to separate them from the other models. I can actually accomplish it using config.eager_load_paths += %W(#{config.root}/app/models/activities) directive, but then in development mode the following exception is raised:

ArgumentError: A copy of SubFolderModel has been removed from the module tree but is still active!

I guess the reason is that app/models/subfolder conflicts with app/models autoload. Anyway, is there a way to organize models using different directories?

Steps to reproduce

  • Create an app/models subfolder
  • Put some models inside
  • Add config.eager_load_paths += %W(#{config.root}/app/models/subfolder) to config/application.rb
  • Start the application in development mode

Expected behavior

Everything works fine

Actual behavior

Sometimes the following exception is raised:

ArgumentError: A copy of SubFolderModel has been removed from the module tree but is still active!

System configuration

Rails version: 5.0.1

Ruby version: 2.4.0

I believe everything under app/models is loaded by default, so you shouldn’t need to add a subdirectory.

I think this question might be better for Rails-talk or Stack Overflow, as Rails-core is designated for discussion of the core itself.

However, I will answer it anyway....

Unless I'm mistaken, OP is wanting to segregate his models into subfolders.

"The Rails way" philosophy on this, which I will voice objection too as someone who works primarily on very large apps, is typical to optimize for small apps and easy-of-entry into the ecosystem but not particularly optimized for larger apps.

On a larger apps, you could have hundreds and hundreds of models. Separating into subfolders is perfectly appropriate.

OP -- I think perhaps all you need is to namespace your models themselves. For example, we have a folder in app/models called platform (a generic name, I know)

All of the models inside of app/models/platform are namespaced using

Platform::

so we have a model called Platform::Block (the file name is app/models/platform/platform/block.rb)

You can define this using

class Platform::Block < ActiveRecord::Base

# your class code here
end

alternatively, another Ruby syntax for the same thing is

module Platform
  class Block < ActiveRecord::Base
  # your class code here
  end
end

I believe the only way to put models into subfolder is to rename them using namespaces. Renaming is a good idea, but of course affects things like all the relationships and STI ('type' column in a polymorphic implementation), which all have to be renamed. (So it's not quite as simply as dragging them into a folder)

A foreign key to this model also usually then needs a class_name on it, except I think in cases where the foreign key is coming from another model that is also in the same namespace.

-Jason

I think this question might be better for Rails-talk or Stack Overflow, as Rails-core is designated for discussion of the core itself.

Uh, sorry!

So you say I could do for example something like:

app/models/activity.rb

class Activity < ApplicationRecord

end

app/models/activity/activity_1.rb

class Activity

class Activity1 < Activity

end

end

And get rid from

ArgumentError: A copy of SubFolderModel has been removed from the module tree but is still active!

errors? It would be suitable for me!

not a Class within a Class, a Class within a Module, like so:

module Activity

class Activity1 < ActivityBase

end

end

or, alternatively…

class Activity::Activity1 < ActivityBase

Note in my example I renamed your base class to “ActivityBase” because I believe if you continue to reference it as simply “Activity” you’ll run into namespace problems.

-Jason

This seems to break the STI :frowning: Activity1 is not found as an STI class

Yes, as I had explained in my previous email, you will absolutely need to rename the objects in the ‘type’ column when using STI/polymorphism. There are several other things to consider as well when making this kind of an object name change.

You can do this directly in SQL (it would be fastest, and works around the impossibility of loading these records). I would suggest doing it in SQL in a migration.

-Jason

It works!!! In many years I’ve been working with Rails I wasn’t aware about this way to organize models. Thank you!

For the namespace instead of defining an Activity module I just used the model Activity:

app/models/activity.rb

class Activity < ApplicationRecord
end

app/models/activity/activity_1.rb

class Activity::Activity1 < Activity
end

And it works like a charm! Thank you!

I’d still be interested in how to make this possible without name-spacing the models. With STI the type column is more presentable to an end user without it. Activity::Something vs. Something. It’d be nice if this were possible (if not the default).

Thanks,

Tom