Gabriel Sobrinho
2014-Jun-26 00:28 UTC
ActiveRecord hook to implement an automatic eager loading feature
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 gabrielsobrinho.com -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-core+unsubscribe@googlegroups.com. To post to this group, send email to rubyonrails-core@googlegroups.com. Visit this group at http://groups.google.com/group/rubyonrails-core. For more options, visit https://groups.google.com/d/optout.