Merge pull request #1006 from IndependentIP/1-8-sort-scope
[1.8] Backport support for sorting by scopes
This commit is contained in:
commit
159d7f01cb
|
@ -1,5 +1,10 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
* Add support for sorting by scopes
|
||||||
|
PR [973](https://github.com/activerecord-hackery/ransack/pull/973)
|
||||||
|
|
||||||
|
*Diego Borges*
|
||||||
|
|
||||||
## Version 1.8.9 - 2018-08-09
|
## Version 1.8.9 - 2018-08-09
|
||||||
|
|
||||||
* Locked Active Record compatibiity at 5.1.0
|
* Locked Active Record compatibiity at 5.1.0
|
||||||
|
|
17
README.md
17
README.md
|
@ -218,6 +218,23 @@ This example toggles the sort directions of both fields, by default
|
||||||
initially sorting the `last_name` field by ascending order, and the
|
initially sorting the `last_name` field by ascending order, and the
|
||||||
`first_name` field by descending order.
|
`first_name` field by descending order.
|
||||||
|
|
||||||
|
In the case that you wish to sort by some complex value, such as the result
|
||||||
|
of a SQL function, you may do so using scopes. In your model, define scopes
|
||||||
|
whose names line up with the name of the virtual field you wish to sort by,
|
||||||
|
as so:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class Person < ActiveRecord::Base
|
||||||
|
scope :sort_by_reverse_name_asc, lambda { order("REVERSE(name) ASC") }
|
||||||
|
scope :sort_by_reverse_name_desc, lambda { order("REVERSE(name) DESC") }
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
and you can then sort by this virtual field:
|
||||||
|
|
||||||
|
```erb
|
||||||
|
<%= sort_link(@q, :reverse_name) %>
|
||||||
|
```
|
||||||
|
|
||||||
The sort link order indicator arrows may be globally customized by setting a
|
The sort link order indicator arrows may be globally customized by setting a
|
||||||
`custom_arrows` option in an initializer file like
|
`custom_arrows` option in an initializer file like
|
||||||
|
|
|
@ -37,9 +37,29 @@ module Ransack
|
||||||
def evaluate(search, opts = {})
|
def evaluate(search, opts = {})
|
||||||
viz = Visitor.new
|
viz = Visitor.new
|
||||||
relation = @object.where(viz.accept(search.base))
|
relation = @object.where(viz.accept(search.base))
|
||||||
|
|
||||||
if search.sorts.any?
|
if search.sorts.any?
|
||||||
relation = relation.except(:order).reorder(viz.accept(search.sorts))
|
relation = relation.except(:order)
|
||||||
|
# Rather than applying all of the search's sorts in one fell swoop,
|
||||||
|
# as the original implementation does, we apply one at a time.
|
||||||
|
#
|
||||||
|
# If the sort (returned by the Visitor above) is a symbol, we know
|
||||||
|
# that it represents a scope on the model and we can apply that
|
||||||
|
# scope.
|
||||||
|
#
|
||||||
|
# Otherwise, we fall back to the applying the sort with the "order"
|
||||||
|
# method as the original implementation did. Actually the original
|
||||||
|
# implementation used "reorder," which was overkill since we already
|
||||||
|
# have a clean slate after "relation.except(:order)" above.
|
||||||
|
viz.accept(search.sorts).each do |scope_or_sort|
|
||||||
|
if scope_or_sort.is_a?(Symbol)
|
||||||
|
relation = relation.send(scope_or_sort)
|
||||||
|
else
|
||||||
|
relation = relation.order(scope_or_sort)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
opts[:distinct] ? relation.distinct : relation
|
opts[:distinct] ? relation.distinct : relation
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,15 @@ module Ransack
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit_Ransack_Nodes_Sort(object)
|
def visit_Ransack_Nodes_Sort(object)
|
||||||
return unless object.valid?
|
if object.valid?
|
||||||
if object.attr.is_a?(Arel::Attributes::Attribute)
|
if object.attr.is_a?(Arel::Attributes::Attribute)
|
||||||
object.attr.send(object.dir)
|
object.attr.send(object.dir)
|
||||||
|
else
|
||||||
|
ordered(object)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
ordered(object)
|
scope_name = :"sort_by_#{object.name}_#{object.dir}"
|
||||||
|
scope_name if object.context.object.respond_to?(scope_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -576,6 +576,13 @@ module Ransack
|
||||||
expect(search.result.to_sql).to match /ORDER BY .* ASC/
|
expect(search.result.to_sql).to match /ORDER BY .* ASC/
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'sorting by a scope' do
|
||||||
|
it 'applies the correct scope' do
|
||||||
|
search = Person.search(sorts: ['reverse_name asc'])
|
||||||
|
expect(search.result.to_sql).to include("ORDER BY REVERSE(name) ASC")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#ransackable_attributes' do
|
describe '#ransackable_attributes' do
|
||||||
|
|
|
@ -57,6 +57,9 @@ class Person < ActiveRecord::Base
|
||||||
of_age ? where("age >= ?", 18) : where("age < ?", 18)
|
of_age ? where("age >= ?", 18) : where("age < ?", 18)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope :sort_by_reverse_name_asc, lambda { order("REVERSE(name) ASC") }
|
||||||
|
scope :sort_by_reverse_name_desc, lambda { order("REVERSE(name) DESC") }
|
||||||
|
|
||||||
alias_attribute :full_name, :name
|
alias_attribute :full_name, :name
|
||||||
|
|
||||||
ransack_alias :term, :name_or_email
|
ransack_alias :term, :name_or_email
|
||||||
|
|
Loading…
Reference in New Issue