Loading class descendants in rails development
I found the following post: http://avinmathew.com/using-rails-descendants-method-in-development/
In short it says the following:
In enviroments/development.rb
add the following:
config.eager_load_paths += Dir['path/to/files/*.rb']
ActionDispatch::Reloader.to_prepare do
Dir['path/to/files/*.rb'].each {|file| require_dependency file}
end
The first line adds the path that should be loaded when you start your app (or console)
and the rest tells rails to reload the classes on each request.
Look up all descendants of a class in Ruby
Here is an example:
class Parent
def self.descendants
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end
class Child < Parent
end
class GrandChild < Child
end
puts Parent.descendants
puts Child.descendants
puts Parent.descendants gives you:
GrandChild
Child
puts Child.descendants gives you:
GrandChild
ActiveSupport::DescendantsTracker.descendants not returning descendants
ActiveSupport::DescendantsTracker.descendants(Object)
will be returning blank in your console because the development console doesn't compile your application, it hasn't yet loaded all of the classes and therefore doesn't know about them to output them!
Take a look at this question: RoR: MyModel.descendants returns [] in a view after the first call?
Rails STI, subclasses don't show up unless used?
After doing some digging, it seems your hypothesis about subclasses not showing up due to lazy loading appears to be correct. Since you're running your application in development mode, all your classes are not loaded until they are specifically called. In production, you would not have this problem since everything is loaded at once and cached.
One way to get around this problem, according to this post, is to do something like this:
[Subclass1, Subclass2, Subclass3] if Rails.env == 'development'
You could put this at the top of your controller so that it loads the instant the controller classes is loaded, or in a before filter.
How to perform eager loading on model descendants using Rails and Ancestry
The easiest way to solve this (that I know of), would be to create an object that holds the entire subtree collection preloaded, and then just ask for children from that in-memory object...
class CachedAncestryCollection
def initialize(collection)
@collection = collection.to_a
end
def children_for(parent_id = nil)
@collection.select do |node|
parent_id ? (node.ancestry && node.ancestry.match(/#{parent_id}$/)) : node.ancestry.nil?
end
end
end
# ...
preloaded_subtree = Comment.where(:id => comments.map(&:subtree_ids))
cached = CachedAncestryCollection.new(preloaded_subtree)
def nested_comments_display(cached, parent_id = nil)
content_tag(:div) do
cached.children_for(parent_id).map do |child|
nested_comments_display(cached, child.id)
end
end
end
Rails force models to eager load
With Rails 6, Zeitwerk became the default code loader.
Try the following for eager loading:
Zeitwerk::Loader.eager_load_all
Is there a way to get a collection of all the Models in your Rails app?
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
Rails Load an array of subclasses from a folder automatically
config.autoload_paths += Dir[Rails.root.join('app', 'models', '**/')]
And all files from subfolder will be autoloaded
CustomClass.descendants
returns descendants of class
Related Topics
Create a Human-Readable List with "And" Inserted Before the Last Element from a Ruby List
Openssl Trouble with Ruby 1.9.3
Grouped Collection Select Alphabetical Order Rails
Ruby on Rails Map.Root Doesn't Seem to Be Working
Catching Timeout Errors with Ruby Mechanize
Spacing Around Parentheses in Ruby
How to Run All Ruby Scripts with Warnings
Ruby Errors with Os X Yosemite
Execjs::Programerror in Welcome#Index Typeerror: Object Doesn't Support This Property or Method
Rake Db:Migrate Is Being Aborted Due to Rake Version Difference
Devise Install from Existing Model/Database
Detecting Overlapping Ranges in Ruby
Trying to Set Up Amazon's S3 Bucket: 403 Forbidden Error & Setting Permissions
Ruby: Is Variable Is Object in Ruby
How to Use Escape Characters in Strings