lib/polyamorous/activerecord/join_dependency.rb
module Polyamorous
module JoinDependencyExtensions
# Replaces ActiveRecord::Associations::JoinDependency#build
def build(associations, base_klass)
associations.map do |name, right|
if name.is_a? Join
reflection = find_reflection base_klass, name.name
reflection.check_validity!
reflection.check_eager_loadable!
klass = if reflection.polymorphic?
name.klass || base_klass
else
reflection.klass
end
JoinAssociation.new(reflection, build(right, klass), name.klass, name.type)
else
reflection = find_reflection base_klass, name
reflection.check_validity!
reflection.check_eager_loadable!
if reflection.polymorphic?
raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
end
JoinAssociation.new(reflection, build(right, reflection.klass))
end
end
end
def join_constraints(joins_to_add, alias_tracker, references)
@alias_tracker = alias_tracker
@joined_tables = {}
@references = {}
references.each do |table_name|
@references[table_name.to_sym] = table_name if table_name.is_a?(String)
end
joins = make_join_constraints(join_root, join_type)
joins.concat joins_to_add.flat_map { |oj|
if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
walk join_root, oj.join_root, oj.join_type
else
make_join_constraints(oj.join_root, oj.join_type)
end
}
end
def construct_tables_for_association!(join_root, association)
tables = table_aliases_for(join_root, association)
association.table = tables.first
tables
end
private
def table_aliases_for(parent, node)
@joined_tables ||= {}
node.reflection.chain.map { |reflection|
table, terminated = @joined_tables[reflection]
root = reflection == node.reflection
if table && (!root || !terminated)
@joined_tables[reflection] = [table, true] if root
table
else
table = alias_tracker.aliased_table_for(reflection.klass.arel_table) do
name = reflection.alias_candidate(parent.table_name)
root ? name : "#{name}_join"
end
@joined_tables[reflection] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
table
end
}
end
module ClassMethods
# Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
#
def walk_tree(associations, hash)
case associations
when TreeNode
associations.add_to_tree(hash)
when Hash
associations.each do |k, v|
cache =
if TreeNode === k
k.add_to_tree(hash)
else
hash[k] ||= {}
end
walk_tree(v, cache)
end
else
super(associations, hash)
end
end
end
end
end