class ActiveRecord::Associations::AssociationProxy
:nodoc:
instantiation of the actual post records.
is computed directly through SQL and does not trigger by itself the
blog.posts.count
The @target
object is not loaded until needed. For example,
ActiveRecord::Associations::HasManyAssociation.
though the object behind blog.posts
is not an Array, but an
blog.posts.class # => Array
corner case, it even removes the class
method and that’s why you get
unknown methods to @target
via method_missing
. As a
This class has most of the basic instance methods removed, and delegates
the @reflection
object represents a :has_many
macro.@owner
, the collection of its posts as @target
, and
the association proxy in blog.posts
has the object in blog
as
blog = Blog.find(:first)
end
has_many :posts
class Blog < ActiveRecord::Base
For example, given
ActiveRecord::Reflection::AssociationReflection.
about is available in @reflection
. That’s an instance of the class
object, known as the @target
. The kind of association any proxy is
holds the association, known as the @owner
, and the actual associated
Association proxies in Active Record are middlemen between the object that
HasOneThroughAssociation
HasManyThroughAssociation
HasManyAssociation
HasAndBelongsToManyAssociation
AssociationCollection
BelongsToPolymorphicAssociation
HasOneAssociation
BelongsToAssociation
AssociationProxy
This is the root class of all association proxies:
def ===(other)
Forwards === explicitly to the \target because the instance method
def ===(other) load_target other === @target end
def aliased_table_name
post.comments.aliased_table_name # => "comments"
Returns the name of the table of the related class:
def aliased_table_name @reflection.klass.table_name end
def conditions
Returns the SQL string that corresponds to the :conditions
def conditions @conditions ||= interpolate_sql(@reflection.sanitized_conditions) if @reflection.sanitized_conditions end
def dependent?
def dependent? @reflection.options[:dependent] end
def flatten_deeper(array)
Array#flatten has problems with recursive arrays. Going one level
def flatten_deeper(array) array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten end
def foreign_key_present
still being a new record). Currently, only +belongs_to+ presents
available for an association without having the object itself (and
Can be overwritten by associations that might have the foreign key
def foreign_key_present false end
def initialize(owner, reflection)
def initialize(owner, reflection) @owner, @reflection = owner, reflection reflection.check_validity! Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) } reset end
def inspect
def inspect load_target @target.inspect end
def interpolate_sql(sql, record = nil)
def interpolate_sql(sql, record = nil) @owner.send(:interpolate_sql, sql, record) end
def load_target
ActiveRecord::RecordNotFound is rescued within the method, and it is
+load_target+ unconditionally to get the \target.
If the \target is already \loaded it is just returned. Thus, you can call
which is expected to be provided by descendants.
This method is abstract in the sense that it relies on +find_target+,
Loads the \target if needed and returns it.
def load_target return nil unless defined?(@loaded) if !loaded? and (!@owner.new_record? || foreign_key_present) @target = find_target end @loaded = true @target rescue ActiveRecord::RecordNotFound reset end
def loaded
def loaded @loaded = true end
def loaded?
def loaded? @loaded end
def merge_options_from_reflection!(options)
def merge_options_from_reflection!(options) options.reverse_merge!( :group => @reflection.options[:group], :having => @reflection.options[:having], :limit => @reflection.options[:limit], :offset => @reflection.options[:offset], :joins => @reflection.options[:joins], :include => @reflection.options[:include], :select => @reflection.options[:select], :readonly => @reflection.options[:readonly] ) end
def method_missing(method, *args, &block)
def method_missing(method, *args, &block) if load_target if @target.respond_to?(method) @target.send(method, *args, &block) else super end end end
def owner_quoted_id
def owner_quoted_id @owner.quoted_id end
def proxy_owner
def proxy_owner @owner end
def proxy_reflection
Returns the reflection object that represents the association handled
def proxy_reflection @reflection end
def proxy_target
def proxy_target @target end
def quoted_record_ids(records)
quoted_record_ids(records) # => "23,56,58,67"
if needed. The result is ready to be inserted into a SQL IN clause.
Returns a string with the IDs of +records+ joined with a comma, quoted
def quoted_record_ids(records) records.map { |record| record.quoted_id }.join(',') end
def raise_on_type_mismatch(record)
the kind of the class of the associated objects. Meant to be used as
Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
def raise_on_type_mismatch(record) unless record.is_a?(@reflection.klass) || record.is_a?(@reflection.class_name.constantize) message = "#{@reflection.class_name}(##{@reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})" raise ActiveRecord::AssociationTypeMismatch, message end end
def reload
def reload reset load_target self unless @target.nil? end
def reset
def reset @loaded = false @target = nil end
def respond_to?(*args)
def respond_to?(*args) proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args)) end
def sanitize_sql(sql, table_name = @reflection.klass.quoted_table_name)
def sanitize_sql(sql, table_name = @reflection.klass.quoted_table_name) @reflection.klass.send(:sanitize_sql, sql, table_name) end
def send(method, *args)
def send(method, *args) if proxy_respond_to?(method) super else load_target @target.send(method, *args) end end
def set_belongs_to_association_for(record)
Assigns the ID of the owner to the corresponding foreign key in +record+.
def set_belongs_to_association_for(record) if @reflection.options[:as] record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record? record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s else unless @owner.new_record? primary_key = @reflection.options[:primary_key] || :id record[@reflection.primary_key_name] = @owner.send(primary_key) end end end
def set_inverse_instance(record, instance)
def set_inverse_instance(record, instance) return if record.nil? || !we_can_set_the_inverse_on_this?(record) inverse_relationship = @reflection.inverse_of unless inverse_relationship.nil? record.send(:"set_#{inverse_relationship.name}_target", instance) end end
def target
def target @target end
def target=(target)
def target=(target) @target = target loaded end
def we_can_set_the_inverse_on_this?(record)
def we_can_set_the_inverse_on_this?(record) false end
def with_scope(*args, &block)
def with_scope(*args, &block) @reflection.klass.send :with_scope, *args, &block end