Add support for sorting by scopes
For when we want to sort by something complex such as the result of a SQL function.
This commit is contained in:
parent
7145471d20
commit
bdd1c245ce
|
@ -2,6 +2,11 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
* Add support for sorting by scopes
|
||||
PR [973](https://github.com/activerecord-hackery/ransack/pull/973)
|
||||
|
||||
*Diego Borges*
|
||||
|
||||
## Version 2.0.1 - 2018-08-18
|
||||
|
||||
* Don't return association if table is nil
|
||||
|
|
25
README.md
25
README.md
|
@ -9,7 +9,7 @@ created by [Ernie Miller](http://twitter.com/erniemiller)
|
|||
and developed/maintained for years by
|
||||
[Jon Atack](http://twitter.com/jonatack) and
|
||||
[Ryan Bigg](http://twitter.com/ryanbigg) with the help of a great group of
|
||||
[contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors). Ransack's logo is designed by [Anıl Kılıç](https://github.com/anilkilic).
|
||||
[contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors). Ransack's logo is designed by [Anıl Kılıç](https://github.com/anilkilic).
|
||||
While it supports many of the same features as MetaSearch, its underlying
|
||||
implementation differs greatly from MetaSearch,
|
||||
and backwards compatibility is not a design goal.
|
||||
|
@ -133,8 +133,8 @@ which are defined in
|
|||
```
|
||||
|
||||
The argument of `f.search_field` has to be in this form:
|
||||
`attribute_name[_or_attribute_name]..._predicate`
|
||||
|
||||
`attribute_name[_or_attribute_name]..._predicate`
|
||||
|
||||
where `[_or_another_attribute_name]...` means any repetition of `_or_` plus the name of the attribute.
|
||||
|
||||
`cont` (contains) and `start` (starts with) are just two of the available
|
||||
|
@ -201,6 +201,23 @@ This example toggles the sort directions of both fields, by default
|
|||
initially sorting the `last_name` field by ascending order, and the
|
||||
`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
|
||||
`custom_arrows` option in an initializer file like
|
||||
|
@ -727,7 +744,7 @@ Ransack.configure do |c|
|
|||
end
|
||||
```
|
||||
|
||||
To turn this off on a per-scope basis Ransack adds the following method to
|
||||
To turn this off on a per-scope basis Ransack adds the following method to
|
||||
`ActiveRecord::Base` that you can redefine to selectively override sanitization:
|
||||
|
||||
`ransackable_scopes_skip_sanitize_args`
|
||||
|
|
|
@ -31,9 +31,29 @@ module Ransack
|
|||
def evaluate(search, opts = {})
|
||||
viz = Visitor.new
|
||||
relation = @object.where(viz.accept(search.base))
|
||||
|
||||
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
|
||||
|
||||
opts[:distinct] ? relation.distinct : relation
|
||||
end
|
||||
|
||||
|
|
|
@ -21,11 +21,15 @@ module Ransack
|
|||
end
|
||||
|
||||
def visit_Ransack_Nodes_Sort(object)
|
||||
return unless object.valid?
|
||||
if object.attr.is_a?(Arel::Attributes::Attribute)
|
||||
object.attr.send(object.dir)
|
||||
if object.valid?
|
||||
if object.attr.is_a?(Arel::Attributes::Attribute)
|
||||
object.attr.send(object.dir)
|
||||
else
|
||||
ordered(object)
|
||||
end
|
||||
else
|
||||
ordered(object)
|
||||
scope_name = :"sort_by_#{object.name}_#{object.dir}"
|
||||
scope_name if object.context.object.respond_to?(scope_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -605,6 +605,13 @@ module Ransack
|
|||
expect(search.result.to_sql).to match /ORDER BY .* ASC/
|
||||
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
|
||||
|
||||
describe '#ransackable_attributes' do
|
||||
|
|
|
@ -43,6 +43,9 @@ class Person < ActiveRecord::Base
|
|||
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
|
||||
|
||||
ransack_alias :term, :name_or_email
|
||||
|
|
Loading…
Reference in New Issue