class ActiveRecord::Associations::JoinDependency::JoinAssociation

def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)

def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
  joins = []
  chain = []
  reflection_chain = reflection.chain
  reflection_chain.each_with_index do |reflection, index|
    table, terminated = yield reflection, reflection_chain[index..]
    @table ||= table
    if terminated
      foreign_table, foreign_klass = table, reflection.klass
      break
    end
    chain << [reflection, table]
  end
  # The chain starts with the target table, but we want to end with it here (makes
  # more sense in this context), so we reverse
  chain.reverse_each do |reflection, table|
    klass = reflection.klass
    scope = reflection.join_scope(table, foreign_table, foreign_klass)
    unless scope.references_values.empty?
      associations = scope.eager_load_values | scope.includes_values
      unless associations.empty?
        scope.joins! scope.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
      end
    end
    arel = scope.arel(alias_tracker.aliases)
    nodes = arel.constraints.first
    if nodes.is_a?(Arel::Nodes::And)
      others = nodes.children.extract! do |node|
        !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
      end
    end
    joins << join_type.new(table, Arel::Nodes::On.new(nodes))
    if others && !others.empty?
      joins.concat arel.join_sources
      append_constraints(joins.last, others)
    end
    # The current table in this iteration becomes the foreign table in the next
    foreign_table, foreign_klass = table, klass
  end
  joins
end