Hello guys,
The Salsify Engineering team made a gem to automatic eager load associations on rails, take a look here: http://blog.salsify.com/engineering/automatic-eager-loading-rails
If you want to avoid to read the entire post, they made this:
blogs = Blogs.limit(5).to_a
SELECT * FROM blogs LIMIT 5
blogs.each { |blog| blog.posts.to_a }
SELECT * FROM posts WHERE blog_id IN (1,2,3,4,5)
Which I think that may make a huge sense for a lot of applications.
But active record seems to not provide a hook to do overwrite internals like that, they had to monkey patch a lot of methods here: https://github.com/salsify/goldiloader/blob/master/lib/goldiloader/active_record_patches.rb
I’m not pretty comfortable with active record source code but my suggestion is to make a accessor or a hook to use a custom collection proxy class.
Currently when we call blog.posts we receive a ActiveRecord::Associations::CollectionProxy object.
What if we can change active record to use a custom builder object, or a lambda, like that?
ActiveRecord::Base.association_builder = lambda do |source, instance, association|
SharedAssociationProxy.build(source: source, instance: instance, association: association)
end
class SharedAssociationProxy < ActiveRecord::Associations::CollectionProxy
def self.build(source:, instance:, association:)
registry[source, association] ||= new(source.klass, association)
registry[source, association].append(instance)
end
def to_a(instance)
objects.select do |object|
object[reflection.primary_key] == object.id
end
end
protected
def objects
@objects ||= reflection.klass.where(reflection.primary_key => instances.map(&:id))
end
end
blogs = Blogs.limit(5).to_a
blogs.first.posts # will use the blogs as source since it fetched a collection
=> SharedAssociationProxy.build(source: blogs, instance: blog, association: :posts)
blog = Blog.first
blog.posts # will use the blog as source since it was the only fetched
=> SharedAssociationProxy.build(source: blog, instance: blog, association: :posts)
Like I said, I’m not pretty comfortable with active record source code, then I don’t know the exactly details that need to be passed to the builder, lets focus just on the concept.
If we want a toggle option like the fully_load made by Salsify guys, we can just check for it on the lambda or the builder.
About the to_a method, it would be necessary to receive the model instance that is calling the to_a method to do the necessary work, it would happen for all methods that can have a custom implementation: first, last, to_a, etc.
What do you guys think about that?
Cheers,
Gabriel Sobrinho