Refactor a bit (in anticipation of more set operations)

This commit is contained in:
Brian Hempel 2015-03-29 21:16:48 -04:00
parent 5c88391609
commit 08d97ad695
1 changed files with 24 additions and 15 deletions

View File

@ -2,21 +2,29 @@ module ActiveRecord
class Relation
module Union
SET_OPERATION_TO_AREL_CLASS = {
"UNION" => Arel::Nodes::Union,
"UNION ALL" => Arel::Nodes::UnionAll
}
def union(relation_or_where_arg, *args)
union_with_class(Arel::Nodes::Union, relation_or_where_arg, *args)
set_operation("UNION", relation_or_where_arg, *args)
end
def union_all(relation_or_where_arg, *args)
union_with_class(Arel::Nodes::UnionAll, relation_or_where_arg, *args)
set_operation("UNION ALL", relation_or_where_arg, *args)
end
private
def union_with_class(union_class, relation_or_where_arg, *args)
other = relation_or_where_arg if args.size == 0 && Relation === relation_or_where_arg
other ||= @klass.where(relation_or_where_arg, *args)
def set_operation(operation, relation_or_where_arg, *args)
other = if args.size == 0 && Relation === relation_or_where_arg
relation_or_where_arg
else
@klass.where(relation_or_where_arg, *args)
end
verify_union_relations!(self, other)
verify_relations_for_set_operation!(operation, self, other)
# Postgres allows ORDER BY in the UNION subqueries if each subquery is surrounded by parenthesis
# but SQLite does not allow parens around the subqueries; you will have to explicitly do `relation.reorder(nil)` in SQLite
@ -26,9 +34,9 @@ module ActiveRecord
left, right = Arel::Nodes::Grouping.new(self.ast), Arel::Nodes::Grouping.new(other.ast)
end
union = union_class.new(left, right)
set = SET_OPERATION_TO_AREL_CLASS[operation].new(left, right)
from = Arel::Nodes::TableAlias.new(
union,
set,
Arel::Nodes::SqlLiteral.new(@klass.arel_table.name)
)
@ -37,20 +45,21 @@ module ActiveRecord
relation
end
def verify_union_relations!(*args)
includes_relations = args.select { |r| r.includes_values.any? }
def verify_relations_for_set_operation!(operation, *relations)
includes_relations = relations.select { |r| r.includes_values.any? }
if includes_relations.any?
raise ArgumentError.new("Cannot union relation with includes.")
raise ArgumentError.new("Cannot #{operation} relation with includes.")
end
preload_relations = args.select { |r| r.preload_values.any? }
preload_relations = relations.select { |r| r.preload_values.any? }
if preload_relations.any?
raise ArgumentError.new("Cannot union relation with preload.")
raise ArgumentError.new("Cannot #{operation} relation with preload.")
end
eager_load_relations = args.select { |r| r.eager_load_values.any? }
eager_load_relations = relations.select { |r| r.eager_load_values.any? }
if eager_load_relations.any?
raise ArgumentError.new("Cannot union relation with eager load.")
raise ArgumentError.new("Cannot #{operation} relation with eager load.")
end
end
end