class ActiveRecord::Reflection::ThroughReflection

:nodoc:
in the Active Record class.
Holds all the meta-data about a :through association as it was specified

def actual_source_reflection # FIXME: this is a horrible name

FIXME: this is a horrible name
def actual_source_reflection # FIXME: this is a horrible name
  source_reflection.actual_source_reflection
end

def association_primary_key(klass = nil)

need to respect the source_reflection's :primary_key option, though.
the source_reflection, because the source_reflection may be polymorphic. We still
We want to use the klass from this reflection, rather than just delegate straight to
def association_primary_key(klass = nil)
  # Get the "actual" source reflection if the immediate source reflection has a
  # source reflection itself
  actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
end

def chain


]
# => [:taggings}, @active_record=Post>,
tags_reflection.chain
tags_reflection = Post.reflect_on_association(:tags)

end
has_many :tags, through: :taggings
has_many :taggings
class Post < ActiveRecord::Base

[self] as its #chain.
reflection. The base case for the recursion is a normal association, which just returns
The chain is built by recursively calling #chain on the source reflection and the through

array corresponds to a table which will be part of the query for this association.
Returns an array of reflections which are involved in this association. Each item in the
def chain
  @chain ||= begin
    a = source_reflection.chain
    b = through_reflection.chain
    chain = a + b
    chain[0] = self # Use self so we don't lose the information from :source_type
    chain
  end
end

def check_validity!

def check_validity!
  if through_reflection.nil?
    raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
  end
  if through_reflection.options[:polymorphic]
    raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
  end
  if source_reflection.nil?
    raise HasManyThroughSourceAssociationNotFoundError.new(self)
  end
  if options[:source_type] && source_reflection.options[:polymorphic].nil?
    raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
  end
  if source_reflection.options[:polymorphic] && options[:source_type].nil?
    raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
  end
  if macro == :has_one && through_reflection.collection?
    raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
  end
  check_validity_of_inverse!
end

def derive_class_name

def derive_class_name
  # get the class_name of the belongs_to association of the through reflection
  options[:source_type] || source_reflection.class_name
end

def initialize(macro, name, scope, options, active_record)

def initialize(macro, name, scope, options, active_record)
  super
  @source_reflection_name = options[:source]
end

def nested?

A through association is nested if there would be more than one join table
def nested?
  chain.length > 2
end

def scope_chain

of scopes corresponding to the chain.
but only Comment.tags will be represented in the #chain. So this method creates an array
There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,

end
has_many :tags
class Comment

end
has_many :comment_tags, through: :comments, source: :tags
has_many :comments
class Article

end
has_many :comment_tags, through: :articles
has_many :articles
class Person

Consider the following example:
def scope_chain
  @scope_chain ||= begin
    scope_chain = source_reflection.scope_chain.map(&:dup)
    # Add to it the scope from this reflection (if any)
    scope_chain.first << scope if scope
    through_scope_chain = through_reflection.scope_chain.map(&:dup)
    if options[:source_type]
      through_scope_chain.first <<
        through_reflection.klass.where(foreign_type => options[:source_type])
    end
    # Recursively fill out the rest of the array from the through reflection
    scope_chain + through_scope_chain
  end
end

def source_macro

The macro used by the source association
def source_macro
  source_reflection.source_macro
end

def source_options

def source_options
  source_reflection.options
end

def source_reflection


# =>
tags_reflection.source_reflection
tags_reflection = Post.reflect_on_association(:tags)

end
belongs_to :tag
belongs_to :post
class Tagging < ActiveRecord::Base

end
has_many :tags, through: :taggings
has_many :taggings
class Post < ActiveRecord::Base

and pluralized form for :belongs_to or :has_many.
Returns the source of the through reflection. It checks both a singularized
def source_reflection
  through_reflection.klass.reflect_on_association(source_reflection_name)
end

def source_reflection_name # :nodoc:

:nodoc:
def source_reflection_name # :nodoc:
  return @source_reflection_name if @source_reflection_name
  names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
  names = names.find_all { |n|
    through_reflection.klass.reflect_on_association(n)
  }
  if names.length > 1
    example_options = options.dup
    example_options[:source] = source_reflection_names.first
    ActiveSupport::Deprecation.warn <<-eowarn
ous source reflection for through association.  Please specify a :source
ive on your declaration like:
s #{active_record.name} < ActiveRecord::Base
macro} :#{name}, #{example_options}
    eowarn
  end
  @source_reflection_name = names.first
end

def source_reflection_names


# => [:tag, :tags]
tags_reflection.source_reflection_names
tags_reflection = Post.reflect_on_association(:tags)

end
has_many :tags, through: :taggings
has_many :taggings
class Post < ActiveRecord::Base

Gets an array of possible :through source reflection names in both singular and plural form.
def source_reflection_names
  (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }.uniq
end

def through_options

def through_options
  through_reflection.options
end

def through_reflection


# =>
tags_reflection.through_reflection
tags_reflection = Post.reflect_on_association(:tags)

end
has_many :tags, through: :taggings
has_many :taggings
class Post < ActiveRecord::Base

of a HasManyThrough or HasOneThrough association.
Returns the AssociationReflection object specified in the :through option
def through_reflection
  active_record.reflect_on_association(options[:through])
end