module ActiveRecord module Associations # Association proxies in Active Record are middlemen between the object that # holds the association, known as the @owner, and the actual associated # object, known as the @target. The kind of association any proxy is # about is available in @reflection. That's an instance of the class # ActiveRecord::Reflection::AssociationReflection. # # For example, given # # class Blog < ActiveRecord::Base # has_many :posts # end # # blog = Blog.first # # the association proxy in blog.posts has the object in +blog+ as # @owner, the collection of its posts as @target, and # the @reflection object represents a :has_many macro. # # This class has most of the basic instance methods removed, and delegates # unknown methods to @target via method_missing. As a # corner case, it even removes the +class+ method and that's why you get # # blog.posts.class # => Array # # though the object behind blog.posts is not an Array, but an # ActiveRecord::Associations::HasManyAssociation. # # The @target object is not \loaded until needed. For example, # # blog.posts.count # # is computed directly through SQL and does not trigger by itself the # instantiation of the actual post records. class CollectionProxy < Relation # :nodoc: delegate :target, :load_target, :loaded?, :to => :@association delegate :select, :find, :first, :last, :build, :create, :create!, :concat, :replace, :delete_all, :destroy_all, :delete, :destroy, :uniq, :sum, :count, :size, :length, :empty?, :any?, :many?, :include?, :to => :@association def initialize(association) @association = association super association.klass, association.klass.arel_table merge! association.scoped end alias_method :new, :build def proxy_association @association end # We don't want this object to be put on the scoping stack, because # that could create an infinite loop where we call an @association # method, which gets the current scope, which is this object, which # delegates to @association, and so on. def scoping @association.scoped.scoping { yield } end def spawn scoped end def scoped(options = nil) association = @association super.extending! do define_method(:proxy_association) { association } end end def ==(other) load_target == other end def to_ary load_target.dup end alias_method :to_a, :to_ary def <<(*records) proxy_association.concat(records) && self end alias_method :push, :<< def clear delete_all self end def reload proxy_association.reload self end end end end