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