Created
July 1, 2010 23:40
-
-
Save woahdae/460725 to your computer and use it in GitHub Desktop.
This file contains 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
# If I have two named scopes with a :having clause, ex: | |
# | |
# class Product < ActiveRecord::Base | |
# named_scope :qty_ordered_gt, lambda { |number| | |
# { :group => "products.id", | |
# :joins => :line_items, | |
# :having => ["SUM(line_items.qty) > ?", number]} | |
# } | |
# named_scope :revenue_gt, lambda { |number| | |
# { :group => "products.id", | |
# :joins => :line_items, | |
# :having => ["SUM(line_items.price) > ?", number]} | |
# } | |
# end | |
# | |
# (ps, made these up, don't know why you'd want this particular scope combo, | |
# but that's beside the point) | |
# | |
# Seems like the combined scope should produce: | |
# | |
# Product.qty_ordered_gt(5).revenue_gt(10).current_scoped_methods[:find][:having] | |
# # => ["SUM(line_items.qty) > ? AND SUM(line_items.price) > ?", 5, 10] | |
# | |
# Instead, it would produce: | |
# | |
# # => ["SUM(line_items.price) > ?", 10] | |
# | |
# The documentation for ActiveRecord::Base#with_scope specifies this behavior, | |
# noting that it will only combine :conditions, :include, and :joins. There's | |
# gotta be a reason why :having isn't included on that list, but personally | |
# I don't see it. :limit, :offset, and :order don't make sense to combine, | |
# but I have a real-world case of needing to combine :having. | |
# | |
# This is a one-line change, copied straight out of ActiveRecord::Base. | |
# Obviously this doesn't come with tests, but I went red-green in | |
# cucumber and rspec in the project this was needed for. Since then I've | |
# also applied the patch to rails' 2-3-stable branch and all the mysql | |
# and sqlite3 tests pass. | |
# | |
# I'll update it if I come across anything, and if someone else agrees with | |
# me (or if I can't sleep at night after copy-pasting this much code out of AR) | |
# I'll submit a patch to Rails. | |
class ActiveRecord::Base | |
# Copied straight out of ActiveRecord::Base, sadly. | |
class << self | |
def with_scope(method_scoping = {}, action = :merge, &block) | |
method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping) | |
# Dup first and second level of hash (method and params). | |
method_scoping = method_scoping.inject({}) do |hash, (method, params)| | |
hash[method] = (params == true) ? params : params.dup | |
hash | |
end | |
method_scoping.assert_valid_keys([ :find, :create ]) | |
if f = method_scoping[:find] | |
f.assert_valid_keys(VALID_FIND_OPTIONS) | |
set_readonly_option! f | |
end | |
# Merge scopings | |
if [:merge, :reverse_merge].include?(action) && current_scoped_methods | |
method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)| | |
case hash[method] | |
when Hash | |
if method == :find | |
(hash[method].keys + params.keys).uniq.each do |key| | |
merge = hash[method][key] && params[key] # merge if both scopes have the same key | |
################## Added key == :having case #################### | |
if (key == :conditions || key == :having) && merge | |
if params[key].is_a?(Hash) && hash[method][key].is_a?(Hash) | |
hash[method][key] = merge_conditions(hash[method][key].deep_merge(params[key])) | |
else | |
hash[method][key] = merge_conditions(params[key], hash[method][key]) | |
end | |
elsif key == :include && merge | |
hash[method][key] = merge_includes(hash[method][key], params[key]).uniq | |
elsif key == :joins && merge | |
hash[method][key] = merge_joins(params[key], hash[method][key]) | |
else | |
hash[method][key] = hash[method][key] || params[key] | |
end | |
end | |
else | |
if action == :reverse_merge | |
hash[method] = hash[method].merge(params) | |
else | |
hash[method] = params.merge(hash[method]) | |
end | |
end | |
else | |
hash[method] = params | |
end | |
hash | |
end | |
end | |
self.scoped_methods << method_scoping | |
begin | |
yield | |
ensure | |
self.scoped_methods.pop | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment