Is there a way to get a collection of all the Models in your Rails app?
Solution 1
EDIT: Look at the comments and other answers. There are smarter answers than this one! Or try to improve this one as community wiki.
Models do not register themselves to a master object, so no, Rails does not have the list of models.
But you could still look in the content of the models directory of your application...
Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path|
# ...
end
EDIT: Another (wild) idea would be to use Ruby reflection to search for every classes that extends ActiveRecord::Base. Don't know how you can list all the classes though...
EDIT: Just for fun, I found a way to list all classes
Module.constants.select { |c| (eval c).is_a? Class }
EDIT: Finally succeeded in listing all models without looking at directories
Module.constants.select do |constant_name|
constant = eval constant_name
if not constant.nil? and constant.is_a? Class and constant.superclass == ActiveRecord::Base
constant
end
end
If you want to handle derived class too, then you will need to test the whole superclass chain. I did it by adding a method to the Class class:
class Class
def extend?(klass)
not superclass.nil? and ( superclass == klass or superclass.extend? klass )
end
end
def models
Module.constants.select do |constant_name|
constant = eval constant_name
if not constant.nil? and constant.is_a? Class and constant.extend? ActiveRecord::Base
constant
end
end
end
Solution 2
The whole answer for Rails 3, 4 and 5 is:
If cache_classes
is off (by default it's off in development, but on in production):
Rails.application.eager_load!
Then:
ActiveRecord::Base.descendants
This makes sure all models in your application, regardless of where they are, are loaded, and any gems you are using which provide models are also loaded.
This should also work on classes that inherit from ActiveRecord::Base
, like ApplicationRecord
in Rails 5, and return only that subtree of descendants:
ApplicationRecord.descendants
If you'd like to know more about how this is done, check out ActiveSupport::DescendantsTracker.
Solution 3
Just in case anyone stumbles on this one, I've got another solution, not relying on dir reading or extending the Class class...
ActiveRecord::Base.send :subclasses
This will return an array of classes. So you can then do
ActiveRecord::Base.send(:subclasses).map(&:name)
Solution 4
ActiveRecord::Base.connection.tables.map do |model|
model.capitalize.singularize.camelize
end
will return
["Article", "MenuItem", "Post", "ZebraStripePerson"]
Additional information If you want to call a method on the object name without model:string unknown method or variable errors use this
model.classify.constantize.attribute_names
Solution 5
For Rails5 models are now subclasses of ApplicationRecord
so to get list of all models in your app you do:
ApplicationRecord.descendants.collect { |type| type.name }
Or shorter:
ApplicationRecord.descendants.collect(&:name)
If you are in dev mode, you will need to eager load models before:
Rails.application.eager_load!
Related videos on Youtube
mr_urf
Ruby developer for a private invite-only, social network.
Updated on November 14, 2021Comments
-
mr_urf over 2 years
Is there a way that you can get a collection of all of the Models in your Rails app?
Basically, can I do the likes of: -
Models.each do |model| puts model.class.name end
-
Andrei over 13 yearsIf you need to collect all models including models of Rails engines/railties, see the answer by @jaime
-
aks about 6 yearsDoesn't work on rails 5.1
-
-
mr_urf over 15 yearsFunnily enough, I went the other way round on this! I started looking at reflections but couldn't figure out how to get the list of classes either! Just after I posted I had a headslap moment and went off to look at Dir. Thanks for the answer. Much appreciated.
-
mr_urf over 15 yearsWow! Kudos earned and then some! Thanks for this, I went with the Dir suggestion and ended up having to scan, strip and capitalize! the resulting file name. However I think that your solution is much more elegant so I'll use this instead. If I could vote you up again, I would! :)
-
mr_urf over 15 yearsThanks bhousel. I originally went with this style of approach but ended up using the solution that Vincent posted above as it meant that I didn't have to "Modelize" the file name as well (i.e. strip out any _, capitalize! each word and then join them again).
-
Edward Anderson almost 14 yearsFYI, I timed both methods just for fun. Looking up the directories is an order of magnitude faster than searching though the classes. That was probably obvious, but now you know :)
-
Edward Anderson almost 14 yearsAlso, it's important to note that searching for models via the constants methods will not include anything that hasn't been referenced since the app started, since it only loads the models on demand.
-
nonopolarity over 13 yearswhy don't you use
ActiveRecord::Base.subclasses
but have to usesend
? Also, it seems like you have to "touch" the model before it will show up, for examplec = Category.new
and it will show up. Otherwise, it won't. -
nonopolarity over 13 yearsI think there has got to be a better way than to use the filenames in a directory. When we use a model, Rails know it is valid or not, so the info must be kept some where.
-
hraada over 13 yearsI prefer 'Kernel.const_get constant_name' to 'eval constant_name'.
-
kikito over 13 yearsI don't remember why I used send anymore, but probably there was a reason. Maybe on the rails version I was using at the time subclasses was private. You were right about the "having to touch" part.
-
Tobias Cohen over 13 yearsIn Rails 3, this has been changed to
ActiveRecord::Base.descendants
-
Andrei over 13 yearsThis is the only way I can get ALL models, including models of Rails engines used in the app. Thanks for the tip!
-
Andrei over 13 yearsA few useful methods:
ActiveRecord::Base.connection.tables.each{|t| begin puts "%s: %d" % [t.humanize, t.classify.constantize.count] rescue nil end}
Some of the models may be not activated therefore you need to rescue it. -
wbharding over 13 yearsThis one is nice since, in Rails 3, your models aren't auto-loaded by default, so many of the above methods won't return all possible models. My permutation also captures models in plugins and subdirectories:
Dir['**/models/**/*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
-
artemave about 13 yearsThere is build in
kind_of?
method that does exactly what yourextend?
does. -
artemave about 13 yearswith subdirectories:
...'/app/models/**/*.rb'
-
Kevin Rood about 13 yearsYou have to use "send" because the :subclasses member is protected.
-
Max Williams almost 13 yearsAdapting @Andrei's a bit:
model_classes = ActiveRecord::Base.connection.tables.collect{|t| t.classify.constantize rescue nil }.compact
-
Chris almost 13 yearsThank you so much! That saved my bacon that tip.
-
Charles Enrick Cajan almost 13 yearsThanks for the Rails 3 tip. For anyone else who comes along, you still need to "touch" the models before
ActiveRecord::Base.descendants
will list them. -
so_mv over 12 yearsFor rails 2.3.x, this seems a reliable approach over the list of tables. list of tables returns schame_migrations tables as well, which is not a model, and also it does not include STI models.
-
courtsimas about 12 yearsThis will get you all the tables though, not just the models, since some tables don't always have associated models.
-
fanaugen about 12 years
RAILS_ROOT
is no longer available in Rails 3. Instead, useDir.glob(Rails.root.join('app/models/*'))
-
sj26 about 12 yearsTechnically in Rails 3 you have subclasses and descendants, they mean different things.
-
David J. almost 12 yearsObject.subclasses_of is deprecated after v2.3.8.
-
Mark Locklear almost 12 yearsAlso a nice followup is <table_name>.column_names to list all columns in the table. So for your user table you would execute User.column_names
-
Jo Liss almost 12 yearsAwesome! This should be the accepted answer. For anybody using this in a rake task: Make your task depend on
:environment
for theeager_load!
to work. -
sj26 almost 12 yearsActually, the models do register themselves as descendants of
ActiveRecord::Base
now, so if you eager load all models then you can iterate them easily—see my answer below. -
Ajedi32 almost 12 yearsOr, as a slightly faster alternative to
Rails.application.eager_load!
, you can just load the models:Dir.glob(Rails.root.join('app/models/*')).each do |x| require x end
-
Ajedi32 almost 12 years@wbharding That's pretty nice, but it errors out when it tries to constantize the names of my rspec model tests. ;-)
-
lightyrs over 11 yearsThat's awesome @Aditya Sanghi. I didn't know about
safe_constantize
. -
Joe Goggins over 11 yearsI agree with Jo Liss: this should be the top answer! 2 lines instead of 14 is 7 times more awesome.
-
sj26 over 11 years@Ajedi32 that is not complete, models can be defined outside those directories, especially when using engines with models. Slightly better, at least glob all
Rails.paths["app/models"].existent
directories. Eager loading the whole application is a more complete answer and will make sure there is absolutely nowhere left for models to be defined. -
rcd about 11 yearsDefinitely the Rails 3 answer :] Great job.
-
user1558501 about 11 yearsAdded another answer to a similar, but what feels like a better targeted question at which may actually get accepted: stackoverflow.com/q/5236932/15585
-
courtsimas about 11 yearsThat doesn't show all the models for me. Not sure why. It's a couple short, in fact.
-
courtsimas about 11 yearsThis will get you all the tables though, not just the models, since some tables don't always have associated models.
-
Marcus Mansur over 10 years@wbharding nice solution but it breaks when you have namespaced models
-
masciugo over 10 yearsI got what sj26 means but maybe there is a little mistake: as far as I know in development environment cache_classes is off (false) that's why you need to manually eager load the application to access all models. explained here
-
boulder_ruby over 10 yearsworked for me. 'just a little late to answer thats all. give it time.
-
sj26 over 10 yearsThanks @masciugo, fixed.
-
sj26 over 10 years@Ajedi32 again, not the complete answer. If you want to eager load only models then try:
Rails.application.paths["app/models"].eager_load!
-
snowangel over 10 yearsthrows TypeError: no implicit conversion of Symbol into String in the console.
-
Admin about 10 years@sj26 Is absolutely right. For large apps, "edge cases" and guessing become major headaches and wrong answers.
-
sunsations almost 10 yearsJust what I wanted. Thank!
-
iheggie over 9 yearsFor rails 2.3.x, use: ActiveRecord::Base.connection.tables.map{|x|x.classify.constantize rescue nil}.compact
-
Pokechu22 over 9 years@iheggie It is generally better to post that as a separate answer than editing it into the existing post.
-
ianstarz over 9 yearsI agree this should be the answer. Here's the one liner I used for my purposes:
(Rails.application.eager_load!) && (ActiveRecord::Base.descendants.map {|klass| [klass.name, klass.count]})
-
PJSCopeland over 9 yearsNot quite, @artemave - only instances of the class will be a
kind_of
the superclass. -
newdark-it over 9 yearsActiveRecord::Base.descendants.map(&:name) this has helped me get the named spaced Model Names
-
illusionist about 9 yearsthanks, I found you answer best suited for me #adiya
-
equivalent8 about 9 years
and
not
are terrible choice for logical operators. They sound like they are logical operators but they are not. Please don't use them in boolean operations. use rather&&
,!
stackoverflow.com/questions/1426826/… -
equivalent8 about 9 yearsupvolt for that Rails.application.eager_load! idea
-
equivalent8 about 9 yearsin case of
class MyConstant < ActiveRecord::Base
don't compareMyConstant.superclass == ActiveRecord::Base
as that is just checking direct ancestor. So For example in case of STIMyAnother < MyConstant
the sperclass isMyConstant
therefore you will miss this model to avoid this check rather entire ancestor treeMyConstant.ancestors.include?(ActiveRecord::Base)
-
denis.peplin over 8 yearsIt's probably needs
Rails.application.eager_load!
before execution in development mode. -
Nuno Costa over 8 yearscalling
map
withputs
? I don't get the point should beActiveRecord::Base.descendants.map(&:model_name)
-
Jordan Michael Rushing over 8 yearsYou can do it that way, but they'll be in a single array, instead of line by line, in a much easier to read format.
-
shadowbq over 8 yearsconfig.eager_load = true
-
lorefnon about 8 yearsI take it that this would require that classes are already loaded and would give incomplete results in development environment with autoloading enabled. I will not downvote but perhaps this should be mentioned in the answer.
-
lorefnon about 8 yearsThis answer should be considered incorrect as it is feasible (and common in legacy setups) to configure the name of the table to be something other than pluralized name of the model. This answer gives the correct answer even when the setup deviates from the default configuration.
-
Nimir about 8 yearsfare enough, updating
-
Marklar over 7 yearsWhat about in Rails 5 where all models will inherit from ApplicationRecord?
-
sj26 over 7 years
ApplicationRecord
still inheritsActiveRecord::Base
soActiveRecord::Base.descendants
still works, but you can also useApplicationRecord.descendants
for only descendants ofApplicationRecord
, too. -
gtournie about 7 yearsIt shouldn't depend on the environment but on the cache_classes flag:
Rails.application.eager_load! unless Rails.application.config.cache_classes
-
sj26 about 7 yearsThat's what the answer says? "If cache_classes is off (by default it's off in development, but on in production)" (the environment is just a hint).
-
Tilo about 7 yearsin some cases this works better than
ActiveRecord::Base.send :subclasses
- looking for the table names is a good idea. Automatically generating the model names might be problematic as lorefnon mentioned. -
Maxim about 7 years
.capitalize.singularize.camelize
can be replaced to.classify
. -
ypresto almost 7 yearsI tried this but one of the models is missing from result... (even it extends ApplicationRecord)
-
Yakob Ubaidi over 6 yearsthe first one is the best for me because I have namespaced models.
-
Ibrahim Tencer almost 5 yearsBe careful though, this may also return ActiveRecord::SchemaMigration when it exists. It also doesn't seem to be loaded by eager_load.
-
sj26 almost 5 years@IbrahimTencer to ignore the schema migrations model and only return your application's models then you might like to use
ApplicationRecord.descendants
:-) -
jgomo3 over 4 yearsI'm on Rails 6.0.2 and the eager_load! didn't make the descendants method to return anything but an empty array.
-
Mohamed AbuIssa about 3 yearsBetter to use
c.constantize
instead ofeval c
. -
PKul over 2 yearsGreat, this work with mongodb as well as those activerecord.