Loading Class Descendants in Rails Development

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



Leave a reply



Submit