module ArelHelpers::JoinAssociation

Public Class Methods

join_association(table, association, join_type = Arel::Nodes::InnerJoin, options = {}, &block) click to toggle source

activerecord uses JoinDependency to automagically generate inner join statements for any type of association (belongs_to, has_many, and has_and_belongs_to_many). For example, for HABTM associations, two join statements are required. This method encapsulates that functionality and yields an intermediate object for chaining. It also allows you to use an outer join instead of the default inner via the join_type arg.

# File lib/arel-helpers/join_association.rb, line 20
def join_association(table, association, join_type = Arel::Nodes::InnerJoin, options = {}, &block)
  if version >= "7.2.0"
    join_association_7_2_0(table, association, join_type, options, &block)
  elsif version >= '6.1.0'
    join_association_6_1_0(table, association, join_type, options, &block)
  elsif version >= '6.0.0'
    join_association_6_0_0(table, association, join_type, options, &block)
  elsif version >= '5.2.1'
    join_association_5_2_1(table, association, join_type, options, &block)
  elsif version >= '5.2.0'
    join_association_5_2(table, association, join_type, options, &block)
  elsif version >= '5.0.0'
    join_association_5_0(table, association, join_type, options, &block)
  elsif version >= '4.2.0'
    join_association_4_2(table, association, join_type, options, &block)
  elsif version >= '4.1.0'
    join_association_4_1(table, association, join_type, options, &block)
  else
    join_association_3_1(table, association, join_type, options, &block)
  end
end

Private Class Methods

apply_aliases(node, aliases) click to toggle source
# File lib/arel-helpers/join_association.rb, line 294
def apply_aliases(node, aliases)
  case node
  when Arel::Nodes::Join
    node.left = aliases[node.left.name] || node.left
    apply_aliases(node.right, aliases)
  when Arel::Attributes::Attribute
    node.relation = aliases[node.relation.name] || node.relation
  when Arel::Nodes::And
    node.children.each { |child| apply_aliases(child, aliases) }
  when Arel::Nodes::Unary
    apply_aliases(node.value, aliases)
  when Arel::Nodes::Binary
    apply_aliases(node.left, aliases)
    apply_aliases(node.right, aliases)
  end
end
join_association_3_1(table, association, join_type, options = {}) { |left.to_sym, right| ... } click to toggle source
# File lib/arel-helpers/join_association.rb, line 48
def join_association_3_1(table, association, join_type, options = {})
  aliases = options.fetch(:aliases, []).index_by(&:table_name)
  associations = association.is_a?(Array) ? association : [association]
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
  manager = Arel::SelectManager.new(table)

  join_dependency.join_associations.each do |assoc|
    assoc.join_type = join_type
    assoc.join_to(manager)
  end

  manager.join_sources.map do |assoc|
    assoc.left.table_alias = aliases[assoc.left.name].name if aliases.key?(assoc.left.name)

    if block_given?
      right = yield assoc.left.name.to_sym, assoc.right
      assoc.class.new(assoc.left, right)
    else
      assoc
    end
  end
end
join_association_4_1(table, association, join_type, options = {}) { |left.to_sym, right| ... } click to toggle source
# File lib/arel-helpers/join_association.rb, line 71
def join_association_4_1(table, association, join_type, options = {})
  aliases = options.fetch(:aliases, []).index_by(&:table_name)
  associations = association.is_a?(Array) ? association : [association]
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])

  join_dependency.join_constraints([]).map do |constraint|
    right = if block_given?
      yield constraint.left.name.to_sym, constraint.right
    else
      constraint.right
    end

    constraint.left.table_alias = aliases[constraint.left.name].name if aliases.key?(constraint.left.name)

    join_type.new(constraint.left, right)
  end
end
join_association_4_2(table, association, join_type, options = {}) { |left.to_sym, right| ... } click to toggle source

ActiveRecord 4.2 moves bind variables out of the join classes and into the relation. For this reason, a method like join_association isn't able to add to the list of bind variables dynamically. To get around the problem, this method must return a string.

# File lib/arel-helpers/join_association.rb, line 94
def join_association_4_2(table, association, join_type, options = {})
  aliases = options.fetch(:aliases, []).index_by(&:table_name)
  associations = association.is_a?(Array) ? association : [association]
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])

  constraints = join_dependency.join_constraints([])

  binds = constraints.flat_map do |info|
    info.binds.map { |bv| table.connection.quote(*bv.reverse) }
  end

  joins = constraints.flat_map do |constraint|
    constraint.joins.map do |join|
      right = if block_given?
        yield join.left.name.to_sym, join.right
      else
        join.right
      end

      join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)

      join_type.new(join.left, right)
    end
  end

  join_strings = joins.map do |join|
    to_sql(join, table, binds)
  end

  join_strings.join(' ')
end
join_association_5_0(table, association, join_type, options = {}) { |left.to_sym, right| ... } click to toggle source
# File lib/arel-helpers/join_association.rb, line 126
def join_association_5_0(table, association, join_type, options = {})
  aliases = options.fetch(:aliases, []).index_by(&:table_name)
  associations = association.is_a?(Array) ? association : [association]
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])

  constraints = join_dependency.join_constraints([], join_type)

  binds = constraints.flat_map do |info|
    prepared_binds = info.binds.map(&:value_for_database)
    prepared_binds.map { |value| table.connection.quote(value) }
  end

  joins = constraints.flat_map do |constraint|
    constraint.joins.map do |join|
      right = if block_given?
        yield join.left.name.to_sym, join.right
      else
        join.right
      end

      join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)

      join_type.new(join.left, right)
    end
  end

  join_strings = joins.map do |join|
    to_sql(join, table, binds)
  end

  join_strings.join(' ')
end
join_association_5_2(table, association, join_type, options = {}) { |left.to_sym, right| ... } click to toggle source
# File lib/arel-helpers/join_association.rb, line 159
def join_association_5_2(table, association, join_type, options = {})
  aliases = options.fetch(:aliases, []).index_by(&:table_name)
  associations = association.is_a?(Array) ? association : [association]

  alias_tracker = ActiveRecord::Associations::AliasTracker.create(
    table.connection, table.name, {}
  )

  join_dependency = ActiveRecord::Associations::JoinDependency.new(
    table, table.arel_table, associations, alias_tracker
  )

  constraints = join_dependency.join_constraints([], join_type)

  constraints.map do |join|
    right = if block_given?
      yield join.left.name.to_sym, join.right
    else
      join.right
    end

    join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)

    join_type.new(join.left, right)
  end
end
join_association_5_2_1(table, association, join_type, options = {}) { |left.to_sym, right| ... } click to toggle source
# File lib/arel-helpers/join_association.rb, line 186
def join_association_5_2_1(table, association, join_type, options = {})
  aliases = options.fetch(:aliases, []).index_by(&:table_name)
  associations = association.is_a?(Array) ? association : [association]

  alias_tracker = ActiveRecord::Associations::AliasTracker.create(
    table.connection, table.name, {}
  )

  join_dependency = ActiveRecord::Associations::JoinDependency.new(
    table, table.arel_table, associations
  )

  constraints = join_dependency.join_constraints([], join_type, alias_tracker)

  constraints.map do |join|
    right = if block_given?
      yield join.left.name.to_sym, join.right
    else
      join.right
    end

    join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)

    join_type.new(join.left, right)
  end
end
join_association_6_0_0(table, association, join_type, options = {}) { |left.to_sym, right| ... } click to toggle source
# File lib/arel-helpers/join_association.rb, line 213
def join_association_6_0_0(table, association, join_type, options = {})
  aliases = options.fetch(:aliases, []).index_by(&:table_name)
  associations = association.is_a?(Array) ? association : [association]

  alias_tracker = ActiveRecord::Associations::AliasTracker.create(
    table.connection, table.name, {}
  )

  join_dependency = ActiveRecord::Associations::JoinDependency.new(
    table, table.arel_table, associations, join_type
  )

  constraints = join_dependency.join_constraints([], alias_tracker)

  constraints.map do |join|
    right = if block_given?
      yield join.left.name.to_sym, join.right
    else
      join.right
    end

    join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)

    join_type.new(join.left, right)
  end
end
join_association_6_1_0(table, association, join_type, options = {}) { |left.to_sym, right| ... } click to toggle source
# File lib/arel-helpers/join_association.rb, line 240
def join_association_6_1_0(table, association, join_type, options = {})
  aliases = options.fetch(:aliases, []).index_by(&:table_name)
  associations = association.is_a?(Array) ? association : [association]

  alias_tracker = ActiveRecord::Associations::AliasTracker.create(
    table.connection, table.name, {}
  )

  join_dependency = ActiveRecord::Associations::JoinDependency.new(
    table, table.arel_table, associations, join_type
  )

  constraints = join_dependency.join_constraints([], alias_tracker, [])

  constraints.map do |join|
    apply_aliases(join, aliases)

    right = if block_given?
              yield join.left.name.to_sym, join.right
            else
              join.right
            end

    join_type.new(join.left, right)
  end
end
join_association_7_2_0(table, association, join_type, options = {}) { |left.to_sym, right| ... } click to toggle source
# File lib/arel-helpers/join_association.rb, line 267
def join_association_7_2_0(table, association, join_type, options = {})
  aliases = options.fetch(:aliases, []).index_by(&:table_name)
  associations = association.is_a?(Array) ? association : [association]

  alias_tracker = ActiveRecord::Associations::AliasTracker.create(
    table.connection_pool, table.name, {}
  )

  join_dependency = ActiveRecord::Associations::JoinDependency.new(
    table, table.arel_table, associations, join_type
  )

  constraints = join_dependency.join_constraints([], alias_tracker, [])

  constraints.map do |join|
    apply_aliases(join, aliases)

    right = if block_given?
      yield join.left.name.to_sym, join.right
    else
      join.right
    end

    join_type.new(join.left, right)
  end
end
to_sql(node, table, binds) click to toggle source
# File lib/arel-helpers/join_association.rb, line 313
def to_sql(node, table, binds)
  visitor = table.connection.visitor
  collect = visitor.accept(node, Arel::Collectors::Bind.new)
  collect.substitute_binds(binds).join
end
version() click to toggle source
# File lib/arel-helpers/join_association.rb, line 44
def version
  ActiveRecord::VERSION::STRING
end