Skip to content

Commit

Permalink
Extract clusterfuck method for surgery
Browse files Browse the repository at this point in the history
  • Loading branch information
jonleighton committed Apr 13, 2012
1 parent 4e3e513 commit 3b86336
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 71 deletions.
84 changes: 84 additions & 0 deletions activerecord/lib/active_record/relation/merger.rb
@@ -0,0 +1,84 @@
module ActiveRecord
class Relation
class Merger
attr_reader :relation, :other

def initialize(relation, other)
@relation = relation

if other.default_scoped? && other.klass != relation.klass
@other = other.with_default_scope
else
@other = other
end
end

def merge
Relation::ASSOCIATION_METHODS.each do |method|
value = other.send(:"#{method}_values")

unless value.empty?
relation.send("#{method}!", value)
end
end

(Relation::MULTI_VALUE_METHODS - [:joins, :where, :order, :binds]).each do |method|
value = other.send(:"#{method}_values")
next if value.empty?

value += relation.send(:"#{method}_values")
relation.send :"#{method}_values=", value
end

relation.joins_values += other.joins_values

merged_wheres = relation.where_values + other.where_values

merged_binds = (relation.bind_values + other.bind_values).uniq(&:first)

unless relation.where_values.empty?
# Remove duplicates, last one wins.
seen = Hash.new { |h,table| h[table] = {} }
merged_wheres = merged_wheres.reverse.reject { |w|
nuke = false
if w.respond_to?(:operator) && w.operator == :==
name = w.left.name
table = w.left.relation.name
nuke = seen[table][name]
seen[table][name] = true
end
nuke
}.reverse
end

relation.where_values = merged_wheres
relation.bind_values = merged_binds

(Relation::SINGLE_VALUE_METHODS - [:lock, :create_with, :reordering]).each do |method|
value = other.send(:"#{method}_value")
relation.send(:"#{method}_value=", value) unless value.nil?
end

relation.lock_value = other.lock_value unless relation.lock_value

unless other.create_with_value.empty?
relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
end

if other.reordering_value
# override any order specified in the original relation
relation.reordering_value = true
relation.order_values = other.order_values
else
# merge in order_values from r
relation.order_values += other.order_values
end

# Apply scope extension modules
relation.send :apply_modules, other.extensions

relation
end
end
end
end
79 changes: 8 additions & 71 deletions activerecord/lib/active_record/relation/spawn_methods.rb
@@ -1,81 +1,18 @@
require 'active_support/core_ext/object/blank'
require 'active_record/relation/merger'

module ActiveRecord
module SpawnMethods
def merge(r)
return self unless r
return to_a & r if r.is_a?(Array)

merged_relation = clone

r = r.with_default_scope if r.default_scoped? && r.klass != klass

Relation::ASSOCIATION_METHODS.each do |method|
value = r.send(:"#{method}_values")

unless value.empty?
if method == :includes
merged_relation = merged_relation.includes(value)
else
merged_relation.send(:"#{method}_values=", value)
end
def merge(other)
if other
if other.is_a?(Array)
to_a & other
else
ActiveRecord::Relation::Merger.new(clone, other).merge
end
end

(Relation::MULTI_VALUE_METHODS - [:joins, :where, :order, :binds]).each do |method|
value = r.send(:"#{method}_values")
next if value.empty?

value += merged_relation.send(:"#{method}_values")
merged_relation.send :"#{method}_values=", value
end

merged_relation.joins_values += r.joins_values

merged_wheres = @where_values + r.where_values

merged_binds = (@bind_values + r.bind_values).uniq(&:first)

unless @where_values.empty?
# Remove duplicates, last one wins.
seen = Hash.new { |h,table| h[table] = {} }
merged_wheres = merged_wheres.reverse.reject { |w|
nuke = false
if w.respond_to?(:operator) && w.operator == :==
name = w.left.name
table = w.left.relation.name
nuke = seen[table][name]
seen[table][name] = true
end
nuke
}.reverse
end

merged_relation.where_values = merged_wheres
merged_relation.bind_values = merged_binds

(Relation::SINGLE_VALUE_METHODS - [:lock, :create_with, :reordering]).each do |method|
value = r.send(:"#{method}_value")
merged_relation.send(:"#{method}_value=", value) unless value.nil?
end

merged_relation.lock_value = r.lock_value unless merged_relation.lock_value

merged_relation = merged_relation.create_with(r.create_with_value) unless r.create_with_value.empty?

if (r.reordering_value)
# override any order specified in the original relation
merged_relation.reordering_value = true
merged_relation.order_values = r.order_values
else
# merge in order_values from r
merged_relation.order_values += r.order_values
self
end

# Apply scope extension modules
merged_relation.send :apply_modules, r.extensions

merged_relation
end

# Removes from the query the condition(s) specified in +skips+.
Expand Down

0 comments on commit 3b86336

Please sign in to comment.