diff options
author | Jon Leighton <j@jonathanleighton.com> | 2012-04-12 16:29:18 +0100 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2012-04-13 13:17:42 +0100 |
commit | 3b863366947c063e19bac1504274ef22831e37d9 (patch) | |
tree | 03397a6f0acdcc197f49166c7799b49e0749d105 /activerecord/lib | |
parent | 4e3e5138b537e999b5bdf6fbb1243890123ada5d (diff) | |
download | rails-3b863366947c063e19bac1504274ef22831e37d9.tar.gz rails-3b863366947c063e19bac1504274ef22831e37d9.tar.bz2 rails-3b863366947c063e19bac1504274ef22831e37d9.zip |
Extract clusterfuck method for surgery
Diffstat (limited to 'activerecord/lib')
-rw-r--r-- | activerecord/lib/active_record/relation/merger.rb | 84 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/spawn_methods.rb | 79 |
2 files changed, 92 insertions, 71 deletions
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb new file mode 100644 index 0000000000..2f50b9a492 --- /dev/null +++ b/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 diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 19ec41e5ca..cdea847fb8 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/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+. |