class Delta::SetOperator::ActiveRecord

Public Class Methods

compatible?(a, b) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 4
def self.compatible?(a, b)
  a.is_a?(b.class) && a.class.name.include?("ActiveRecord")
end
new(a:, b:, identifiers: nil, changes:) click to toggle source
Calls superclass method Delta::SetOperator::new
# File lib/delta/set_operator/active_record.rb, line 8
def initialize(a:, b:, identifiers: nil, changes:)
  super

  self.identifiers = identifiers || [:id]
end

Private Instance Methods

arel_table(scope, name) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 73
def arel_table(scope, name)
  Arel::Table.new(scope.arel_table.name, as: name)
end
attribute_clause(a, b) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 61
def attribute_clause(a, b)
  changes.map { |k| a[k].not_eq(b[k]) }.inject(&:or)
end
build_query(a_scope, b_scope, join_type) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 42
def build_query(a_scope, b_scope, join_type)
  a_query = nested_query(a_scope, "a")
  b_query = nested_query(b_scope, "b")

  a = arel_table(a_scope, "a")
  b = arel_table(b_scope, "b")

  query = a.from(a_query)
  query = query.join(b.join(b_query).join_sources, join_type)
  query = query.on(*identity_clauses(a, b))
  query = query.group(*group_clauses(a, b))

  query
end
execute(query, scope) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 69
def execute(query, scope)
  model_for_scope(scope).find_by_sql(query.to_sql)
end
group_clauses(a, _b) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 65
def group_clauses(a, _b)
  identifiers.map { |k| a[k] }
end
identity_clauses(a, b) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 57
def identity_clauses(a, b)
  identifiers.map { |k| a[k].eq(b[k]) }
end
inner_join() click to toggle source
# File lib/delta/set_operator/active_record.rb, line 85
def inner_join
  Arel::Nodes::InnerJoin
end
intersect(a_scope, b_scope) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 28
def intersect(a_scope, b_scope)
  return [] if changes.empty?

  a = arel_table(a_scope, "a")
  b = arel_table(b_scope, "b")

  query = build_query(a_scope, b_scope, inner_join)

  query = query.project(Arel.star)
  query = query.where(attribute_clause(a, b))

  execute(query, a_scope)
end
left_join() click to toggle source
# File lib/delta/set_operator/active_record.rb, line 81
def left_join
  Arel::Nodes::OuterJoin
end
model_for_scope(scope) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 89
def model_for_scope(scope)
  scope.respond_to?(:model) ? scope.model : scope.klass
end
nested_query(scope, name) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 77
def nested_query(scope, name)
  Arel.sql("(#{scope.to_sql}) as #{name}")
end
subtract(a_scope, b_scope) click to toggle source
# File lib/delta/set_operator/active_record.rb, line 16
def subtract(a_scope, b_scope)
  a = arel_table(a_scope, "a")
  b = arel_table(b_scope, "b")

  query = build_query(a_scope, b_scope, left_join)

  query = query.project(a[Arel.star])
  query = query.where(b[identifiers.first].eq(nil))

  execute(query, a_scope)
end