Created
January 10, 2014 13:18
-
-
Save Bertg/8351770 to your computer and use it in GitHub Desktop.
This version of spawn methods removes the dangerous behaviour where rails 3.2 (after 3.2.13) clobbers conditions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
raise "Remove patch after rails 3.2" if Rails.version =~ /\A4/ | |
# copy of lib/active_record/relation/spawn_methods.rb | |
# Because updating to this version 3.2.16 might change the behaviour of some of our queries, | |
# we took a copy of this class and remove the behaviour where | |
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 | |
merge_relation_method(merged_relation, method, value) | |
end | |
end | |
end | |
(Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method| | |
value = r.send(:"#{method}_values") | |
merge_relation_method(merged_relation, method, value) if value.present? | |
end | |
merge_joins(merged_relation, r) | |
merged_wheres = @where_values + r.where_values | |
# -- PATCH | |
# unless @where_values.empty? | |
# # Remove duplicate ARel attributes. Last one wins. | |
# seen = Hash.new { |h,table| h[table] = Hash.new { |i,name| i[name] = [] } } | |
# merged_wheres = merged_wheres.reverse.reject { |w| | |
# nuke = false | |
# if w.respond_to?(:operator) && w.operator == :== && | |
# w.left.respond_to?(:relation) | |
# name = w.left.name | |
# table = w.left.relation.name | |
# nuke = !seen[table][name].empty? | |
# seen[table][name] << w.right | |
# end | |
# nuke | |
# }.reverse | |
# end | |
# -- END PATCH | |
merged_relation.where_values = merged_wheres | |
(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 | |
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+. | |
# | |
# Example: | |
# | |
# Post.order('id asc').except(:order) # discards the order condition | |
# Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order | |
# | |
def except(*skips) | |
result = self.class.new(@klass, table) | |
result.default_scoped = default_scoped | |
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - skips).each do |method| | |
result.send(:"#{method}_values=", send(:"#{method}_values")) | |
end | |
(Relation::SINGLE_VALUE_METHODS - skips).each do |method| | |
result.send(:"#{method}_value=", send(:"#{method}_value")) | |
end | |
# Apply scope extension modules | |
result.send(:apply_modules, extensions) | |
result | |
end | |
# Removes any condition from the query other than the one(s) specified in +onlies+. | |
# | |
# Example: | |
# | |
# Post.order('id asc').only(:where) # discards the order condition | |
# Post.order('id asc').only(:where, :order) # uses the specified order | |
# | |
def only(*onlies) | |
result = self.class.new(@klass, table) | |
result.default_scoped = default_scoped | |
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) & onlies).each do |method| | |
result.send(:"#{method}_values=", send(:"#{method}_values")) | |
end | |
(Relation::SINGLE_VALUE_METHODS & onlies).each do |method| | |
result.send(:"#{method}_value=", send(:"#{method}_value")) | |
end | |
# Apply scope extension modules | |
result.send(:apply_modules, extensions) | |
result | |
end | |
VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend, | |
:order, :select, :readonly, :group, :having, :from, :lock ] | |
def apply_finder_options(options) | |
relation = clone | |
return relation unless options | |
options.assert_valid_keys(VALID_FIND_OPTIONS) | |
finders = options.dup | |
finders.delete_if { |key, value| value.nil? && key != :limit } | |
([:joins, :select, :group, :order, :having, :limit, :offset, :from, :lock, :readonly] & finders.keys).each do |finder| | |
relation = relation.send(finder, finders[finder]) | |
end | |
relation = relation.where(finders[:conditions]) if options.has_key?(:conditions) | |
relation = relation.includes(finders[:include]) if options.has_key?(:include) | |
relation = relation.extending(finders[:extend]) if options.has_key?(:extend) | |
relation | |
end | |
private | |
def merge_joins(relation, other) | |
values = other.joins_values | |
return if values.blank? | |
if other.klass >= relation.klass | |
relation.joins_values += values | |
else | |
joins_dependency, rest = values.partition do |join| | |
case join | |
when Hash, Symbol, Array | |
true | |
else | |
false | |
end | |
end | |
join_dependency = ActiveRecord::Associations::JoinDependency.new( | |
other.klass, | |
joins_dependency, | |
[] | |
) | |
relation.joins_values += join_dependency.join_associations + rest | |
end | |
end | |
def merge_relation_method(relation, method, value) | |
relation.send(:"#{method}_values=", relation.send(:"#{method}_values") + value) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here's the same patch, updated for
rails 3.2.17
and remove already defined constants warnings