class ActiveRecord::Associations::Association

:nodoc:
the reflection object represents a :has_many macro.
owner, the collection of its posts as target, and
The association of blog.posts has the object blog as its
blog = Blog.first
end
has_many :posts
class Blog < ActiveRecord::Base
For example, given
reflection, which is an instance of ActiveRecord::Reflection::AssociationReflection.
result set, known as the target. Association metadata is available in
holds the association, known as the owner, and the associated
Associations in Active Record are middlemen between the object that
HasManyThroughAssociation + ThroughAssociation
HasManyAssociation + ForeignAssociation
CollectionAssociation
BelongsToPolymorphicAssociation
BelongsToAssociation
HasOneThroughAssociation + ThroughAssociation
HasOneAssociation + ForeignAssociation
SingularAssociation
Association
This is the root class of all associations (‘+ Foo’ signifies an included module Foo):
= Active Record Associations

def association_scope

actually gets built.
by scope.scoping { ... } or unscoped { ... } etc, which affects the scope which
scope method is called. This is because at that point the call may be surrounded
Note that the association_scope is merged into the target_scope only when the

The scope for this association.
def association_scope
  if klass
    @association_scope ||= if disable_joins
      DisableJoinsAssociationScope.scope(self)
    else
      AssociationScope.scope(self)
    end
  end
end

def build_record(attributes)

def build_record(attributes)
  reflection.build_association(attributes) do |record|
    initialize_attributes(record, attributes)
    yield(record) if block_given?
  end
end

def create(attributes = nil, &block)

def create(attributes = nil, &block)
  _create_record(attributes, &block)
end

def create!(attributes = nil, &block)

def create!(attributes = nil, &block)
  _create_record(attributes, true, &block)
end

def enqueue_destroy_association(options)

def enqueue_destroy_association(options)
  job_class = owner.class.destroy_association_async_job
  if job_class
    owner._after_commit_jobs.push([job_class, options])
  end
end

def ensure_klass_exists!

when the association target class does not exist.
Reader and writer methods call this so that consistent errors are presented
def ensure_klass_exists!
  klass
end

def extensions

def extensions
  extensions = klass.default_extensions | reflection.extensions
  if reflection.scope
    extensions |= reflection.scope_for(klass.unscoped, owner).extensions
  end
  extensions
end

def find_target

def find_target
  if violates_strict_loading? && owner.validation_context.nil?
    Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
  end
  scope = self.scope
  return scope.to_a if skip_statement_cache?(scope)
  sc = reflection.association_scope_cache(klass, owner) do |params|
    as = AssociationScope.create { params.bind }
    target_scope.merge!(as.scope(self))
  end
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
  sc.execute(binds, klass.connection) do |record|
    set_inverse_instance(record)
    if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
      record.strict_loading!
    else
      record.strict_loading!(false, mode: owner.strict_loading_mode)
    end
  end
end

def find_target?

def find_target?
  !loaded? && (!owner.new_record? || foreign_key_present?) && klass
end

def foreign_key_for?(record)

Returns true if record contains the foreign_key
def foreign_key_for?(record)
  record._has_attribute?(reflection.foreign_key)
end

def foreign_key_present?

has_one/has_many :through associations which go through a belongs_to.
Currently implemented by belongs_to (vanilla and polymorphic) and

be present in order to load target.
without a key). If the owner is a new record then foreign_key must
the target if the owner is currently a new record (and therefore
references the target. This is used to determine whether we can load
Returns true if there is a foreign key present on the owner which
def foreign_key_present?
  false
end

def initialize(owner, reflection)

def initialize(owner, reflection)
  reflection.check_validity!
  @owner, @reflection = owner, reflection
  @disable_joins = @reflection.options[:disable_joins] || false
  reset
  reset_scope
end

def initialize_attributes(record, except_from_scope_attributes = nil) # :nodoc:

:nodoc:
def initialize_attributes(record, except_from_scope_attributes = nil) # :nodoc:
  except_from_scope_attributes ||= {}
  skip_assign = [reflection.foreign_key, reflection.type].compact
  assigned_keys = record.changed_attribute_names_to_save
  assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
  attributes = scope_for_create.except!(*(assigned_keys - skip_assign))
  record.send(:_assign_attributes, attributes) if attributes.any?
  set_inverse_instance(record)
end

def inversable?(record)

def inversable?(record)
  record &&
    ((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
end

def inverse_association_for(record)

def inverse_association_for(record)
  if invertible_for?(record)
    record.association(inverse_reflection_for(record).name)
  end
end

def inverse_reflection_for(record)

the association in the specific class of the record.
The record parameter is necessary to support polymorphic inverses as we must check for
Can be redefined by subclasses, notably polymorphic belongs_to
def inverse_reflection_for(record)
  reflection.inverse_of
end

def inversed_from(record)

def inversed_from(record)
  self.target = record
end

def inversed_from_queries(record)

def inversed_from_queries(record)
  if inversable?(record)
    self.target = record
  end
end

def invertible_for?(record)

This method is redefined by subclasses.
Returns true if inverse association on the given record needs to be set.
def invertible_for?(record)
  foreign_key_for?(record) && inverse_reflection_for(record)
end

def klass

polymorphic_type field on the owner.
Returns the class of the target. belongs_to polymorphic overrides this to look at the
def klass
  reflection.klass
end

def load_target

not reraised. The proxy is \reset and +nil+ is the return value.
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
  @target = find_target if (@stale_state && stale_target?) || find_target?
  loaded! unless loaded?
  target
rescue ActiveRecord::RecordNotFound
  reset
end

def loaded!

Asserts the \target has been loaded setting the \loaded flag to +true+.
def loaded!
  @loaded = true
  @stale_state = stale_state
end

def loaded?

Has the \target been already \loaded?
def loaded?
  @loaded
end

def marshal_dump

We can't dump @reflection and @through_reflection since it contains the scope proc
def marshal_dump
  ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] }
  [@reflection.name, ivars]
end

def marshal_load(data)

def marshal_load(data)
  reflection_name, ivars = data
  ivars.each { |name, val| instance_variable_set(name, val) }
  @reflection = @owner.class._reflect_on_association(reflection_name)
end

def matches_foreign_key?(record)

def matches_foreign_key?(record)
  if foreign_key_for?(record)
    record.read_attribute(reflection.foreign_key) == owner.id ||
      (foreign_key_for?(owner) && owner.read_attribute(reflection.foreign_key) == record.id)
  else
    owner.read_attribute(reflection.foreign_key) == record.id
  end
end

def raise_on_type_mismatch!(record)

a safety check when you are about to assign an associated 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)
    fresh_class = reflection.class_name.safe_constantize
    unless fresh_class && record.is_a?(fresh_class)
      message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, "\
        "got #{record.inspect} which is an instance of #{record.class}(##{record.class.object_id})"
      raise ActiveRecord::AssociationTypeMismatch, message
    end
  end
end

def reload(force = false)

The QueryCache is cleared if +force+ is true.
Reloads the \target and returns +self+ on success.
def reload(force = false)
  klass.connection.clear_query_cache if force && klass
  reset
  reset_scope
  load_target
  self unless target.nil?
end

def remove_inverse_instance(record)

Remove the inverse association, if possible
def remove_inverse_instance(record)
  if inverse = inverse_association_for(record)
    inverse.inversed_from(nil)
  end
end

def reset

Resets the \loaded flag to +false+ and sets the \target to +nil+.
def reset
  @loaded = false
  @target = nil
  @stale_state = nil
end

def reset_negative_cache # :nodoc:

:nodoc:
def reset_negative_cache # :nodoc:
  reset if loaded? && target.nil?
end

def reset_scope

def reset_scope
  @association_scope = nil
end

def scope

def scope
  if disable_joins
    DisableJoinsAssociationScope.create.scope(self)
  elsif (scope = klass.current_scope) && scope.try(:proxy_association) == self
    scope.spawn
  elsif scope = klass.global_current_scope
    target_scope.merge!(association_scope).merge!(scope)
  else
    target_scope.merge!(association_scope)
  end
end

def scope_for_create

def scope_for_create
  scope.scope_for_create
end

def set_inverse_instance(record)

Set the inverse association, if possible
def set_inverse_instance(record)
  if inverse = inverse_association_for(record)
    inverse.inversed_from(owner)
  end
  record
end

def set_inverse_instance_from_queries(record)

def set_inverse_instance_from_queries(record)
  if inverse = inverse_association_for(record)
    inverse.inversed_from_queries(owner)
  end
  record
end

def skip_statement_cache?(scope)

Returns true if statement cache should be skipped on the association reader.
def skip_statement_cache?(scope)
  reflection.has_scope? ||
    scope.eager_loading? ||
    klass.scope_attributes? ||
    reflection.source_reflection.active_record.default_scopes.any?
end

def stale_state

This is only relevant to certain associations, which is why it returns +nil+ by default.

the target is stale.
so that when stale_state is different from the value stored on the last find_target,
This should be implemented to return the values of the relevant key(s) on the owner,
def stale_state
end

def stale_target?

Note that if the target has not been loaded, it is not considered stale.

stale_state method if relevant.
on the owner will reload the target. It's up to subclasses to implement the
relevant foreign_key(s) refers to. If stale, the association accessor method
The target is stale if the target no longer points to the record(s) that the
def stale_target?
  loaded? && @stale_state != stale_state
end

def target=(target)

Sets the target of this association to \target, and the \loaded flag to +true+.
def target=(target)
  @target = target
  loaded!
end

def target_scope

through association's scope)
Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
def target_scope
  AssociationRelation.create(klass, self).merge!(klass.scope_for_association)
end

def violates_strict_loading?

def violates_strict_loading?
  return reflection.strict_loading? if reflection.options.key?(:strict_loading)
  owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
end