

with_scope and named_scopes ignoring stacked :order clauses
source link: https://mauricio.github.io/2009/06/04/with_scope-and-named_scopes-ignoring-stacked-order-clauses.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

with_scope and named_scopes ignoring stacked :order clauses
If you’ve been using with_scope and named_scopes a lot with ActiveRecord you have probably noticed that the :order clauses defined at the scopes are lost and only the first :order clause is used. If you defined an :order clause you’d like to have it merged with the other ones already provided. Here’s a simple example:
class User
named_scope :by_first_name, :order => "#{quoted_table_name}.first_name ASC"
named_scope :by_last_name, :order => "#{quoted_table_name}.last_name ASC"
end
Our user has two named scopes defined and both of them define an :order clause, if we try to run a finder like this:
User.by_first_name.by_last_name.all
This is the generated query:
SELECT * FROM `users` ORDER BY `users`.first_name ASC
As you’ve noticed, only the first :order clause was used, the last one was lost. Our ideal SQL query would have to look like this, with both :order clauses being used:
SELECT * FROM `users` ORDER BY `users`.last_name ASC , `users`.first_name ASC
That’s why we’re going to hack the with_scope method a litle bit to reach our goal. This issue was already reported to the Rails issue tracker but there’s no fix yet so our only hope is to monkeypatch Rails to behave as we expect it to, so here’s a really simple fix for the problem:
ActiveRecord::Base.class_eval do
class << self
def merge_orders( *orders )
orders.map! do |o|
if o.blank?
nil
else
o.strip!
o
end
end
orders.compact!
orders.join( ' , ' )
end
def with_scope_with_hack(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
if key == :conditions && 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])
elsif key == :order && merge
hash[method][key] = merge_orders(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
alias_method_chain :with_scope, :hack
end
end
You can place this code at an initializer (maybe called with_scope_fix.rb) or at your lib folder and require it in your initializers. And now all your :order clauses defined by named_scope or with_scope calls will be correctly merged and will not be lost in your code.
Recommend
-
82
There is a lot of debate on how the if-statements should be used for better code clarity and readability. Most of them boil down to an opinion that its completely subjective and aesthetic. And…
-
30
-
54
An overview of the execution context of blocks Mehdi Farsi
-
21
Lately, I’ve been hacking on the next version of luminance , luminance-0.40. It should be out “soon-ish” but in the meantime, I’ve been struggling a bit wit...
-
10
Rust pattern: Precise closure capture clauses Apr 24, 2018 This is the second in a series of posts about Rust compiler errors. Each one will talk about a particular error that I got recently and try to e...
-
11
SQL functions in WHERE clauses are evil Once we get up an running with the basic SQL syntax, doing inserts, updates, deletes and simple selects we start to learn about the SQL functions, the default ones...
-
4
Tutorial How To Use WHERE Clauses in SQL DatabasesSQL
-
9
Charm, goodwill, and executive orders — “Bad mergers” and noncompete clauses targeted in Biden executive order Sweeping order tries to counter rising corporate consolidation....
-
3
-
7
Kotlin: An Illustrated Guide • Chapter 11 Scopes and Scope Functions
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK