Search code examples
ruby-on-railsrubyruby-on-rails-4activerecordruby-on-rails-5

CollectionProxy vs AssociationRelation


I am wondering about the difference between ActiveRecord::Associations::CollectionProxy and ActiveRecord::AssociationRelation.

class Vehicle < ActiveRecord::Base
  has_many :wheels
end

class Wheel < ActiveRecord::Base
  belongs_to :vehicle
end

So if I do:

v = Vehicle.new

v.wheels # => #<ActiveRecord::Associations::CollectionProxy []>

v.wheels.all # => #<ActiveRecord::AssociationRelation []>

I have no idea what is the difference between them and why this is implemented this way?


Solution

  • ActiveRecord::Relation are simple query objects before being turned into a query and executed, CollectionProxy on the other hand are a little bit more sophisticated.

    First of all you get association extentions, you probably saw something that looks like this, assume a book store model that has many books

    class Store < ActiveRecord::Base
      has_many :books do
        def used
          where(is_used: true)
        end
      end
    end
    

    This way you get to call used books in a store using a syntax that looks like this

    Store.first.books.used
    

    But this is the most basic uses, you could use the attributes that are exposed to you in the collection proxy, which are owner, reflection and target

    Owner

    The owner provides a a reference to the parent object holding the association

    Reflection

    The reflection object is an instance of ActiveRecord::Reflection::AssocciationReflection and contains all the configuration options for the association.

    Target

    The target is the association collection objects (or single object when has_one and belongs_to).

    Using those methods you could do some conditons in your association extention, for example if we have a blog, we give access to all deleted posts to users who are admins (lame example I know)

    Class Publisher < ActiveRecord::Base
      has_many :posts do
        def deleted
          if owner.admin?
            Post.where(deleted: true)
          else
            where(deleted: true)
          end
        end
      end
    end
    

    You also get access to two more methods which are reset and reload, the first one (reset) clears the cached association objects, the second one (reload) is more common and is used to reset and then loads the associated objects from the database.

    I hope this explains how having a CollectionProxy class would be so useful