ransack 1.7.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/.travis.yml +16 -48
- data/CHANGELOG.md +409 -26
- data/CONTRIBUTING.md +48 -20
- data/Gemfile +9 -13
- data/README.md +352 -92
- data/Rakefile +6 -25
- data/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +2 -0
- data/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +2 -0
- data/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +32 -0
- data/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +112 -0
- data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +32 -0
- data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +113 -0
- data/lib/polyamorous/activerecord_5.2.1_ruby_2/join_association.rb +31 -0
- data/lib/polyamorous/activerecord_5.2.1_ruby_2/join_dependency.rb +57 -0
- data/lib/polyamorous/join.rb +70 -0
- data/lib/polyamorous/swapping_reflection_class.rb +11 -0
- data/lib/polyamorous/tree_node.rb +7 -0
- data/lib/polyamorous.rb +25 -0
- data/lib/ransack/adapters/active_record/base.rb +23 -2
- data/lib/ransack/adapters/active_record/context.rb +210 -135
- data/lib/ransack/adapters/active_record/ransack/constants.rb +53 -53
- data/lib/ransack/adapters/active_record/ransack/context.rb +11 -15
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +33 -30
- data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
- data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
- data/lib/ransack/adapters/active_record.rb +11 -10
- data/lib/ransack/adapters.rb +45 -23
- data/lib/ransack/configuration.rb +91 -4
- data/lib/ransack/constants.rb +14 -26
- data/lib/ransack/context.rb +29 -18
- data/lib/ransack/helpers/form_builder.rb +27 -12
- data/lib/ransack/helpers/form_helper.rb +75 -70
- data/lib/ransack/locale/ar.yml +70 -0
- data/lib/ransack/locale/az.yml +70 -0
- data/lib/ransack/locale/bg.yml +70 -0
- data/lib/ransack/locale/da.yml +70 -0
- data/lib/ransack/locale/el.yml +70 -0
- data/lib/ransack/locale/id.yml +70 -0
- data/lib/ransack/locale/it.yml +70 -0
- data/lib/ransack/locale/ja.yml +70 -0
- data/lib/ransack/locale/nl.yml +4 -4
- data/lib/ransack/locale/pt-BR.yml +70 -0
- data/lib/ransack/locale/ru.yml +70 -0
- data/lib/ransack/locale/tr.yml +70 -0
- data/lib/ransack/locale/{zh.yml → zh-CN.yml} +13 -13
- data/lib/ransack/locale/zh-TW.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +5 -2
- data/lib/ransack/nodes/bindable.rb +18 -6
- data/lib/ransack/nodes/condition.rb +77 -28
- data/lib/ransack/nodes/grouping.rb +16 -10
- data/lib/ransack/nodes/sort.rb +9 -5
- data/lib/ransack/nodes/value.rb +74 -68
- data/lib/ransack/nodes.rb +1 -1
- data/lib/ransack/predicate.rb +15 -19
- data/lib/ransack/search.rb +14 -7
- data/lib/ransack/translate.rb +3 -3
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +1 -12
- data/lib/ransack.rb +7 -7
- data/logo/ransack-h.png +0 -0
- data/logo/ransack-h.svg +34 -0
- data/logo/ransack-v.png +0 -0
- data/logo/ransack-v.svg +34 -0
- data/logo/ransack.png +0 -0
- data/logo/ransack.svg +21 -0
- data/ransack.gemspec +9 -10
- data/spec/console.rb +4 -0
- data/spec/helpers/polyamorous_helper.rb +24 -0
- data/spec/ransack/adapters/active_record/base_spec.rb +365 -74
- data/spec/ransack/adapters/active_record/context_spec.rb +14 -19
- data/spec/ransack/configuration_spec.rb +87 -14
- data/spec/ransack/helpers/form_builder_spec.rb +2 -11
- data/spec/ransack/helpers/form_helper_spec.rb +481 -113
- data/spec/ransack/join_association_spec.rb +28 -0
- data/spec/ransack/join_dependency_spec.rb +86 -0
- data/spec/ransack/join_spec.rb +19 -0
- data/spec/ransack/nodes/condition_spec.rb +24 -0
- data/spec/ransack/nodes/grouping_spec.rb +56 -0
- data/spec/ransack/predicate_spec.rb +27 -5
- data/spec/ransack/search_spec.rb +84 -70
- data/spec/spec_helper.rb +4 -0
- data/spec/support/schema.rb +86 -41
- metadata +60 -81
- data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
- data/lib/ransack/adapters/active_record/3.0/context.rb +0 -201
- data/lib/ransack/adapters/active_record/3.1/context.rb +0 -215
- data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
- data/lib/ransack/adapters/active_record/compat.rb +0 -14
- data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
- data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
- data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
- data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
- data/lib/ransack/adapters/mongoid/base.rb +0 -130
- data/lib/ransack/adapters/mongoid/context.rb +0 -208
- data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
- data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
- data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
- data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
- data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
- data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
- data/lib/ransack/adapters/mongoid/table.rb +0 -35
- data/lib/ransack/adapters/mongoid.rb +0 -13
- data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -276
- data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
- data/spec/mongoid/configuration_spec.rb +0 -102
- data/spec/mongoid/dependencies_spec.rb +0 -8
- data/spec/mongoid/helpers/ransack_helper.rb +0 -11
- data/spec/mongoid/nodes/condition_spec.rb +0 -34
- data/spec/mongoid/nodes/grouping_spec.rb +0 -13
- data/spec/mongoid/predicate_spec.rb +0 -155
- data/spec/mongoid/search_spec.rb +0 -446
- data/spec/mongoid/support/mongoid.yml +0 -6
- data/spec/mongoid/support/schema.rb +0 -128
- data/spec/mongoid/translate_spec.rb +0 -14
- data/spec/mongoid_spec_helper.rb +0 -59
- data/spec/ransack/dependencies_spec.rb +0 -12
data/README.md
CHANGED
@@ -1,26 +1,25 @@
|
|
1
|
-
# Ransack
|
1
|
+
# ![Ransack](./logo/ransack-h.png "Ransack")
|
2
2
|
|
3
|
-
[![Build Status](https://travis-ci.org/activerecord-hackery/ransack.svg)]
|
4
|
-
(https://
|
5
|
-
[![
|
6
|
-
(
|
7
|
-
[![Code Climate](https://codeclimate.com/github/activerecord-hackery/ransack/badges/gpa.svg)]
|
8
|
-
(https://codeclimate.com/github/activerecord-hackery/ransack)
|
3
|
+
[![Build Status](https://travis-ci.org/activerecord-hackery/ransack.svg)](https://travis-ci.org/activerecord-hackery/ransack)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/ransack.svg)](http://badge.fury.io/rb/ransack)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/activerecord-hackery/ransack/badges/gpa.svg)](https://codeclimate.com/github/activerecord-hackery/ransack)
|
6
|
+
[![Backers on Open Collective](https://opencollective.com/ransack/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/ransack/sponsors/badge.svg)](#sponsors)
|
9
7
|
|
10
|
-
Ransack is a rewrite of [MetaSearch]
|
11
|
-
(https://github.com/activerecord-hackery/meta_search)
|
8
|
+
Ransack is a rewrite of [MetaSearch](https://github.com/activerecord-hackery/meta_search)
|
12
9
|
created by [Ernie Miller](http://twitter.com/erniemiller)
|
13
|
-
and maintained
|
14
|
-
[Jon Atack](http://twitter.com/jonatack) and
|
15
|
-
(
|
10
|
+
and developed/maintained for years by
|
11
|
+
[Jon Atack](http://twitter.com/jonatack) and
|
12
|
+
[Ryan Bigg](http://twitter.com/ryanbigg) with the help of a great group of
|
13
|
+
[contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors). Ransack's logo is designed by [Anıl Kılıç](https://github.com/anilkilic).
|
16
14
|
While it supports many of the same features as MetaSearch, its underlying
|
17
15
|
implementation differs greatly from MetaSearch,
|
18
16
|
and backwards compatibility is not a design goal.
|
19
17
|
|
20
|
-
Ransack enables the creation of both
|
21
|
-
[
|
22
|
-
|
23
|
-
|
18
|
+
Ransack enables the creation of both
|
19
|
+
[simple](http://ransack-demo.herokuapp.com) and
|
20
|
+
[advanced](http://ransack-demo.herokuapp.com/users/advanced_search) search forms
|
21
|
+
for your Ruby on Rails application
|
22
|
+
([demo source code here](https://github.com/activerecord-hackery/ransack_demo)).
|
24
23
|
If you're looking for something that simplifies query generation at the model
|
25
24
|
or controller layer, you're probably not looking for Ransack (or MetaSearch,
|
26
25
|
for that matter). Try [Squeel](https://github.com/activerecord-hackery/squeel)
|
@@ -29,18 +28,17 @@ instead.
|
|
29
28
|
If you're viewing this at
|
30
29
|
[github.com/activerecord-hackery/ransack](https://github.com/activerecord-hackery/ransack),
|
31
30
|
you're reading the documentation for the master branch with the latest features.
|
32
|
-
[View documentation for the last release (
|
31
|
+
[View documentation for the last release (2.0.0).](https://github.com/activerecord-hackery/ransack/tree/v2.0.0)
|
33
32
|
|
34
33
|
## Getting started
|
35
34
|
|
36
|
-
Ransack is compatible with Rails
|
37
|
-
|
38
|
-
[this](https://github.com/activerecord-hackery/polyamorous/issues/17)).
|
35
|
+
Ransack is compatible with Rails 5.0, 5.1 and 5.2 on Ruby 2.2 and later.
|
36
|
+
If you are using Rails <5.0 use the 1.8 line of Ransack.
|
39
37
|
If you are using Ruby 1.8 or an earlier JRuby and run into compatibility
|
40
38
|
issues, you can use an earlier version of Ransack, say, up to 1.3.0.
|
41
39
|
|
42
40
|
Ransack works out-of-the-box with Active Record and also features limited
|
43
|
-
support for Mongoid 4
|
41
|
+
support for Mongoid 4 and 5 (without associations, further details
|
44
42
|
[below](https://github.com/activerecord-hackery/ransack#mongoid)).
|
45
43
|
|
46
44
|
In your Gemfile, for the last officially released gem:
|
@@ -49,7 +47,8 @@ In your Gemfile, for the last officially released gem:
|
|
49
47
|
gem 'ransack'
|
50
48
|
```
|
51
49
|
|
52
|
-
|
50
|
+
If you would like to use the latest updates (recommended), use the `master`
|
51
|
+
branch:
|
53
52
|
|
54
53
|
```ruby
|
55
54
|
gem 'ransack', github: 'activerecord-hackery/ransack'
|
@@ -60,6 +59,7 @@ gem 'ransack', github: 'activerecord-hackery/ransack'
|
|
60
59
|
* Before filing an issue, please read the [Contributing Guide](CONTRIBUTING.md).
|
61
60
|
* File an issue if a bug is caused by Ransack, is new (has not already been reported), and _can be reproduced from the information you provide_.
|
62
61
|
* Contributions are welcome, but please do not add "+1" comments to issues or pull requests :smiley:
|
62
|
+
* Please do not use the issue tracker for personal support requests. Stack Overflow is a better place for that where a wider community can help you!
|
63
63
|
|
64
64
|
## Usage
|
65
65
|
|
@@ -75,8 +75,7 @@ If you're coming from MetaSearch, things to note:
|
|
75
75
|
1. The default param key for search params is now `:q`, instead of `:search`.
|
76
76
|
This is primarily to shorten query strings, though advanced queries (below)
|
77
77
|
will still run afoul of URL length limits in most browsers and require a
|
78
|
-
switch to HTTP POST requests. This key is [configurable]
|
79
|
-
(https://github.com/activerecord-hackery/ransack/wiki/Configuration).
|
78
|
+
switch to HTTP POST requests. This key is [configurable](https://github.com/activerecord-hackery/ransack/wiki/Configuration).
|
80
79
|
|
81
80
|
2. `form_for` is now `search_form_for`, and validates that a Ransack::Search
|
82
81
|
object is passed to it.
|
@@ -86,23 +85,7 @@ If you're coming from MetaSearch, things to note:
|
|
86
85
|
ActiveRecord::Relation in the case of the ActiveRecord adapter) via a call to
|
87
86
|
`Ransack#result`.
|
88
87
|
|
89
|
-
|
90
|
-
avoid returning duplicate rows, even if conditions on a join would otherwise
|
91
|
-
result in some. It generates the same SQL as calling `uniq` on the relation.
|
92
|
-
|
93
|
-
Please note that for many databases, a sort on an associated table's columns
|
94
|
-
may result in invalid SQL with `distinct: true` -- in those cases, you're on
|
95
|
-
your own, and will need to modify the result as needed to allow these queries
|
96
|
-
to work.
|
97
|
-
|
98
|
-
If `distinct: true` or `uniq` is causing invalid SQL, another way to remove
|
99
|
-
duplicates is to call `to_a.uniq` on the collection at the end (see the next
|
100
|
-
section below) -- with the caveat that the de-duping is taking place in Ruby
|
101
|
-
instead of in SQL, which is potentially slower and uses more memory, and that
|
102
|
-
it may display awkwardly with pagination if the number of results is greater
|
103
|
-
than the page size.
|
104
|
-
|
105
|
-
####In your controller
|
88
|
+
#### In your controller
|
106
89
|
|
107
90
|
```ruby
|
108
91
|
def index
|
@@ -110,7 +93,7 @@ def index
|
|
110
93
|
@people = @q.result(distinct: true)
|
111
94
|
end
|
112
95
|
```
|
113
|
-
or without `distinct:true`, for sorting on an associated table's columns (in
|
96
|
+
or without `distinct: true`, for sorting on an associated table's columns (in
|
114
97
|
this example, with preloading each Person's Articles and pagination):
|
115
98
|
|
116
99
|
```ruby
|
@@ -123,13 +106,13 @@ def index
|
|
123
106
|
end
|
124
107
|
```
|
125
108
|
|
126
|
-
####In your view
|
109
|
+
#### In your view
|
127
110
|
|
128
111
|
The two primary Ransack view helpers are `search_form_for` and `sort_link`,
|
129
112
|
which are defined in
|
130
113
|
[Ransack::Helpers::FormHelper](lib/ransack/helpers/form_helper.rb).
|
131
114
|
|
132
|
-
####Ransack's `search_form_for` helper replaces `form_for` for creating the view search form
|
115
|
+
#### Ransack's `search_form_for` helper replaces `form_for` for creating the view search form
|
133
116
|
|
134
117
|
```erb
|
135
118
|
<%= search_form_for @q do |f| %>
|
@@ -150,6 +133,11 @@ which are defined in
|
|
150
133
|
<% end %>
|
151
134
|
```
|
152
135
|
|
136
|
+
The argument of `f.search_field` has to be in this form:
|
137
|
+
`attribute_name[_or_attribute_name]..._predicate`
|
138
|
+
|
139
|
+
where `[_or_another_attribute_name]...` means any repetition of `_or_` plus the name of the attribute.
|
140
|
+
|
153
141
|
`cont` (contains) and `start` (starts with) are just two of the available
|
154
142
|
search predicates. See
|
155
143
|
[Constants](https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/constants.rb)
|
@@ -165,7 +153,7 @@ The `search_form_for` answer format can be set like this:
|
|
165
153
|
<%= search_form_for(@q, format: :json) do |f| %>
|
166
154
|
```
|
167
155
|
|
168
|
-
####Ransack's `sort_link` helper creates table headers that are sortable links
|
156
|
+
#### Ransack's `sort_link` helper creates table headers that are sortable links
|
169
157
|
|
170
158
|
```erb
|
171
159
|
<%= sort_link(@q, :name) %>
|
@@ -177,6 +165,14 @@ column title or a default sort order:
|
|
177
165
|
<%= sort_link(@q, :name, 'Last Name', default_order: :desc) %>
|
178
166
|
```
|
179
167
|
|
168
|
+
You can use a block if the link markup is hard to fit into the label parameter:
|
169
|
+
|
170
|
+
```erb
|
171
|
+
<%= sort_link(@q, :name) do %>
|
172
|
+
<strong>Player Name</strong>
|
173
|
+
<% end %>
|
174
|
+
```
|
175
|
+
|
180
176
|
With a polymorphic association, you may need to specify the name of the link
|
181
177
|
explicitly to avoid an `uninitialized constant Model::Xxxable` error (see issue
|
182
178
|
[#421](https://github.com/activerecord-hackery/ransack/issues/421)):
|
@@ -206,13 +202,76 @@ This example toggles the sort directions of both fields, by default
|
|
206
202
|
initially sorting the `last_name` field by ascending order, and the
|
207
203
|
`first_name` field by descending order.
|
208
204
|
|
209
|
-
|
210
|
-
|
205
|
+
In the case that you wish to sort by some complex value, such as the result
|
206
|
+
of a SQL function, you may do so using scopes. In your model, define scopes
|
207
|
+
whose names line up with the name of the virtual field you wish to sort by,
|
208
|
+
as so:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
class Person < ActiveRecord::Base
|
212
|
+
scope :sort_by_reverse_name_asc, lambda { order("REVERSE(name) ASC") }
|
213
|
+
scope :sort_by_reverse_name_desc, lambda { order("REVERSE(name) DESC") }
|
214
|
+
...
|
215
|
+
```
|
216
|
+
|
217
|
+
and you can then sort by this virtual field:
|
218
|
+
|
219
|
+
```erb
|
220
|
+
<%= sort_link(@q, :reverse_name) %>
|
221
|
+
```
|
222
|
+
|
223
|
+
The sort link order indicator arrows may be globally customized by setting a
|
224
|
+
`custom_arrows` option in an initializer file like
|
225
|
+
`config/initializers/ransack.rb`.
|
226
|
+
|
227
|
+
You can also enable a `default_arrow` which is displayed on all sortable fields
|
228
|
+
which are not currently used in the sorting. This is disabled by default so
|
229
|
+
nothing will be displayed:
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
Ransack.configure do |c|
|
233
|
+
c.custom_arrows = {
|
234
|
+
up_arrow: '<i class="custom-up-arrow-icon"></i>',
|
235
|
+
down_arrow: 'U+02193',
|
236
|
+
default_arrow: '<i class="default-arrow-icon"></i>'
|
237
|
+
}
|
238
|
+
end
|
239
|
+
```
|
240
|
+
|
241
|
+
All sort links may be displayed without the order indicator
|
242
|
+
arrows by setting `hide_sort_order_indicators` to true in the initializer file.
|
243
|
+
Note that this hides the arrows even if they were customized:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
Ransack.configure do |c|
|
247
|
+
c.hide_sort_order_indicators = true
|
248
|
+
end
|
249
|
+
```
|
250
|
+
|
251
|
+
Without setting it globally, individual sort links may be displayed without
|
252
|
+
the order indicator arrow by passing `hide_indicator: true` in the sort link:
|
211
253
|
|
212
254
|
```erb
|
213
255
|
<%= sort_link(@q, :name, hide_indicator: true) %>
|
214
256
|
```
|
215
257
|
|
258
|
+
#### Ransack's `sort_url` helper is like a `sort_link` but returns only the url
|
259
|
+
|
260
|
+
`sort_url` has the same API as `sort_link`:
|
261
|
+
|
262
|
+
```erb
|
263
|
+
<%= sort_url(@q, :name, default_order: :desc) %>
|
264
|
+
```
|
265
|
+
|
266
|
+
```erb
|
267
|
+
<%= sort_url(@q, :last_name, [:last_name, 'first_name asc']) %>
|
268
|
+
```
|
269
|
+
|
270
|
+
```erb
|
271
|
+
<%= sort_url(@q, :last_name, %i(last_name first_name),
|
272
|
+
default_order: { last_name: 'asc', first_name: 'desc' }) %>
|
273
|
+
```
|
274
|
+
|
216
275
|
### Advanced Mode
|
217
276
|
|
218
277
|
"Advanced" searches (ab)use Rails' nested attributes functionality in order to
|
@@ -250,11 +309,12 @@ end
|
|
250
309
|
|
251
310
|
Once you've done so, you can make use of the helpers in [Ransack::Helpers::FormBuilder](lib/ransack/helpers/form_builder.rb) to
|
252
311
|
construct much more complex search forms, such as the one on the
|
253
|
-
[demo
|
312
|
+
[demo app](http://ransack-demo.herokuapp.com/users/advanced_search)
|
313
|
+
(source code [here](https://github.com/activerecord-hackery/ransack_demo)).
|
254
314
|
|
255
315
|
### Ransack #search method
|
256
316
|
|
257
|
-
Ransack will try to
|
317
|
+
Ransack will try to make the class method `#search` available in your
|
258
318
|
models, but if `#search` has already been defined elsewhere, you can always use
|
259
319
|
the default `#ransack` class method. So the following are equivalent:
|
260
320
|
|
@@ -264,7 +324,7 @@ Article.search(params[:q])
|
|
264
324
|
```
|
265
325
|
|
266
326
|
Users have reported issues of `#search` name conflicts with other gems, so
|
267
|
-
the `#search` method alias
|
327
|
+
the `#search` method alias will be deprecated in the next major version of
|
268
328
|
Ransack (2.0). It's advisable to use the default `#ransack` instead.
|
269
329
|
|
270
330
|
For now, if Ransack's `#search` method conflicts with the name of another
|
@@ -335,25 +395,184 @@ end
|
|
335
395
|
...
|
336
396
|
<%= content_tag :table do %>
|
337
397
|
<%= content_tag :th, sort_link(@q, :last_name) %>
|
338
|
-
<%= content_tag :th, sort_link(@q,
|
339
|
-
<%= content_tag :th, sort_link(@q,
|
398
|
+
<%= content_tag :th, sort_link(@q, :department_title) %>
|
399
|
+
<%= content_tag :th, sort_link(@q, :employees_last_name) %>
|
400
|
+
<% end %>
|
401
|
+
```
|
402
|
+
|
403
|
+
If you have trouble sorting on associations, try using an SQL string with the
|
404
|
+
pluralized table (`'departments.title'`,`'employees.last_name'`) instead of the
|
405
|
+
symbolized association (`:department_title)`, `:employees_last_name`).
|
406
|
+
|
407
|
+
### Ransack Aliases
|
408
|
+
|
409
|
+
You can customize the attribute names for your Ransack searches by using a
|
410
|
+
`ransack_alias`. This is particularly useful for long attribute names that are
|
411
|
+
necessary when querying associations or multiple columns.
|
412
|
+
|
413
|
+
```ruby
|
414
|
+
class Post < ActiveRecord::Base
|
415
|
+
belongs_to :author
|
416
|
+
|
417
|
+
# Abbreviate :author_first_name_or_author_last_name to :author
|
418
|
+
ransack_alias :author, :author_first_name_or_author_last_name
|
419
|
+
end
|
420
|
+
```
|
421
|
+
|
422
|
+
Now, rather than using `:author_first_name_or_author_last_name_cont` in your
|
423
|
+
form, you can simply use `:author_cont`. This serves to produce more expressive
|
424
|
+
query parameters in your URLs.
|
425
|
+
|
426
|
+
```erb
|
427
|
+
<%= search_form_for @q do |f| %>
|
428
|
+
<%= f.label :author_cont %>
|
429
|
+
<%= f.search_field :author_cont %>
|
340
430
|
<% end %>
|
341
431
|
```
|
342
432
|
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
433
|
+
### Search Matchers
|
434
|
+
|
435
|
+
List of all possible predicates
|
436
|
+
|
437
|
+
|
438
|
+
| Predicate | Description | Notes |
|
439
|
+
| ------------- | ------------- |-------- |
|
440
|
+
| `*_eq` | equal | |
|
441
|
+
| `*_not_eq` | not equal | |
|
442
|
+
| `*_matches` | matches with `LIKE` | e.g. `q[email_matches]=%@gmail.com`|
|
443
|
+
| `*_does_not_match` | does not match with `LIKE` | |
|
444
|
+
| `*_matches_any` | Matches any | |
|
445
|
+
| `*_matches_all` | Matches all | |
|
446
|
+
| `*_does_not_match_any` | Does not match any | |
|
447
|
+
| `*_does_not_match_all` | Does not match all | |
|
448
|
+
| `*_lt` | less than | |
|
449
|
+
| `*_lteq` | less than or equal | |
|
450
|
+
| `*_gt` | greater than | |
|
451
|
+
| `*_gteq` | greater than or equal | |
|
452
|
+
| `*_present` | not null and not empty | Only compatible with string columns. Example: `q[name_present]=1` (SQL: `col is not null AND col != ''`) |
|
453
|
+
| `*_blank` | is null or empty. | (SQL: `col is null OR col = ''`) |
|
454
|
+
| `*_null` | is null | |
|
455
|
+
| `*_not_null` | is not null | |
|
456
|
+
| `*_in` | match any values in array | e.g. `q[name_in][]=Alice&q[name_in][]=Bob` |
|
457
|
+
| `*_not_in` | match none of values in array | |
|
458
|
+
| `*_lt_any` | Less than any | SQL: `col < value1 OR col < value2` |
|
459
|
+
| `*_lteq_any` | Less than or equal to any | |
|
460
|
+
| `*_gt_any` | Greater than any | |
|
461
|
+
| `*_gteq_any` | Greater than or equal to any | |
|
462
|
+
| `*_matches_any` | `*_does_not_match_any` | same as above but with `LIKE` |
|
463
|
+
| `*_lt_all` | Less than all | SQL: `col < value1 AND col < value2` |
|
464
|
+
| `*_lteq_all` | Less than or equal to all | |
|
465
|
+
| `*_gt_all` | Greater than all | |
|
466
|
+
| `*_gteq_all` | Greater than or equal to all | |
|
467
|
+
| `*_matches_all` | Matches all | same as above but with `LIKE` |
|
468
|
+
| `*_does_not_match_all` | Does not match all | |
|
469
|
+
| `*_not_eq_all` | none of values in a set | |
|
470
|
+
| `*_start` | Starts with | SQL: `col LIKE 'value%'` |
|
471
|
+
| `*_not_start` | Does not start with | |
|
472
|
+
| `*_start_any` | Starts with any of | |
|
473
|
+
| `*_start_all` | Starts with all of | |
|
474
|
+
| `*_not_start_any` | Does not start with any of | |
|
475
|
+
| `*_not_start_all` | Does not start with all of | |
|
476
|
+
| `*_end` | Ends with | SQL: `col LIKE '%value'` |
|
477
|
+
| `*_not_end` | Does not end with | |
|
478
|
+
| `*_end_any` | Ends with any of | |
|
479
|
+
| `*_end_all` | Ends with all of | |
|
480
|
+
| `*_not_end_any` | | |
|
481
|
+
| `*_not_end_all` | | |
|
482
|
+
| `*_cont` | Contains value | uses `LIKE` |
|
483
|
+
| `*_cont_any` | Contains any of | |
|
484
|
+
| `*_cont_all` | Contains all of | |
|
485
|
+
| `*_not_cont` | Does not contain |
|
486
|
+
| `*_not_cont_any` | Does not contain any of | |
|
487
|
+
| `*_not_cont_all` | Does not contain all of | |
|
488
|
+
| `*_true` | is true | |
|
489
|
+
| `*_false` | is false | |
|
490
|
+
|
491
|
+
|
492
|
+
(See full list: https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/locale/en.yml#L15 and [wiki](https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching))
|
347
493
|
|
348
494
|
### Using Ransackers to add custom search functions via Arel
|
349
495
|
|
350
496
|
The main premise behind Ransack is to provide access to
|
351
497
|
**Arel predicate methods**. Ransack provides special methods, called
|
352
498
|
_ransackers_, for creating additional search functions via Arel. More
|
353
|
-
information about `ransacker` methods can be found [here in the wiki]
|
354
|
-
(https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers).
|
499
|
+
information about `ransacker` methods can be found [here in the wiki](https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers).
|
355
500
|
Feel free to contribute working `ransacker` code examples to the wiki!
|
356
501
|
|
502
|
+
### Problem with DISTINCT selects
|
503
|
+
|
504
|
+
If passed `distinct: true`, `result` will generate a `SELECT DISTINCT` to
|
505
|
+
avoid returning duplicate rows, even if conditions on a join would otherwise
|
506
|
+
result in some. It generates the same SQL as calling `uniq` on the relation.
|
507
|
+
|
508
|
+
Please note that for many databases, a sort on an associated table's columns
|
509
|
+
may result in invalid SQL with `distinct: true` -- in those cases, you
|
510
|
+
will need to modify the result as needed to allow these queries to work.
|
511
|
+
|
512
|
+
For example, you could call joins and includes on the result which has the
|
513
|
+
effect of adding those tables columns to the select statement, overcoming
|
514
|
+
the issue, like so:
|
515
|
+
|
516
|
+
```ruby
|
517
|
+
def index
|
518
|
+
@q = Person.ransack(params[:q])
|
519
|
+
@people = @q.result(distinct: true)
|
520
|
+
.includes(:articles)
|
521
|
+
.joins(:articles)
|
522
|
+
.page(params[:page])
|
523
|
+
end
|
524
|
+
```
|
525
|
+
|
526
|
+
If the above doesn't help, you can also use ActiveRecord's `select` query
|
527
|
+
to explicitly add the columns you need, which brute force's adding the
|
528
|
+
columns you need that your SQL engine is complaining about, you need to
|
529
|
+
make sure you give all of the columns you care about, for example:
|
530
|
+
|
531
|
+
```ruby
|
532
|
+
def index
|
533
|
+
@q = Person.ransack(params[:q])
|
534
|
+
@people = @q.result(distinct: true)
|
535
|
+
.select('people.*, articles.name, articles.description')
|
536
|
+
.page(params[:page])
|
537
|
+
end
|
538
|
+
```
|
539
|
+
|
540
|
+
Another method to approach this when using Postgresql is to use ActiveRecords's `.includes` in combination with `.group` instead of `distinct: true`.
|
541
|
+
|
542
|
+
For example:
|
543
|
+
```ruby
|
544
|
+
def index
|
545
|
+
@q = Person.ransack(params[:q])
|
546
|
+
@people = @q.result
|
547
|
+
.group('persons.id')
|
548
|
+
.includes(:articles)
|
549
|
+
.page(params[:page])
|
550
|
+
end
|
551
|
+
|
552
|
+
```
|
553
|
+
|
554
|
+
A final way of last resort is to call `to_a.uniq` on the collection at the end
|
555
|
+
with the caveat that the de-duping is taking place in Ruby instead of in SQL,
|
556
|
+
which is potentially slower and uses more memory, and that it may display
|
557
|
+
awkwardly with pagination if the number of results is greater than the page size.
|
558
|
+
|
559
|
+
For example:
|
560
|
+
|
561
|
+
```ruby
|
562
|
+
def index
|
563
|
+
@q = Person.ransack(params[:q])
|
564
|
+
@people = @q.result.includes(:articles).page(params[:page]).to_a.uniq
|
565
|
+
end
|
566
|
+
```
|
567
|
+
|
568
|
+
#### `PG::UndefinedFunction: ERROR: could not identify an equality operator for type json`
|
569
|
+
|
570
|
+
If you get the above error while using `distinct: true` that means that
|
571
|
+
one of the columns that Ransack is selecting is a `json` column.
|
572
|
+
PostgreSQL does not provide comparison operators for the `json` type. While
|
573
|
+
it is possible to work around this, in practice it's much better to convert those
|
574
|
+
to `jsonb`, as [recommended by the PostgreSQL documentation](https://www.postgresql.org/docs/9.6/static/datatype-json.html).
|
575
|
+
|
357
576
|
### Authorization (whitelisting/blacklisting)
|
358
577
|
|
359
578
|
By default, searching and sorting are authorized on any column of your model
|
@@ -409,16 +628,12 @@ for an `auth_object` key in the options hash which can be used by your own
|
|
409
628
|
overridden methods.
|
410
629
|
|
411
630
|
Here is an example that puts all this together, adapted from
|
412
|
-
[this blog post by Ernie Miller]
|
413
|
-
(http://erniemiller.org/2012/05/11/why-your-ruby-class-macros-might-suck-mine-did/).
|
631
|
+
[this blog post by Ernie Miller](http://erniemiller.org/2012/05/11/why-your-ruby-class-macros-might-suck-mine-did/).
|
414
632
|
In an `Article` model, add the following `ransackable_attributes` class method
|
415
633
|
(preferably private):
|
416
634
|
|
417
635
|
```ruby
|
418
636
|
class Article < ActiveRecord::Base
|
419
|
-
|
420
|
-
private
|
421
|
-
|
422
637
|
def self.ransackable_attributes(auth_object = nil)
|
423
638
|
if auth_object == :admin
|
424
639
|
# whitelist all attributes for admin
|
@@ -428,6 +643,8 @@ class Article < ActiveRecord::Base
|
|
428
643
|
super & %w(title body)
|
429
644
|
end
|
430
645
|
end
|
646
|
+
|
647
|
+
private_class_method :ransackable_attributes
|
431
648
|
end
|
432
649
|
```
|
433
650
|
|
@@ -435,7 +652,6 @@ Here is example code for the `articles_controller`:
|
|
435
652
|
|
436
653
|
```ruby
|
437
654
|
class ArticlesController < ApplicationController
|
438
|
-
|
439
655
|
def index
|
440
656
|
@q = Article.ransack(params[:q], auth_object: set_ransack_auth_object)
|
441
657
|
@articles = @q.result
|
@@ -483,7 +699,7 @@ scope accepts a value:
|
|
483
699
|
|
484
700
|
```ruby
|
485
701
|
class Employee < ActiveRecord::Base
|
486
|
-
scope :
|
702
|
+
scope :activated, ->(boolean = true) { where(active: boolean) }
|
487
703
|
scope :salary_gt, ->(amount) { where('salary > ?', amount) }
|
488
704
|
|
489
705
|
# Scopes are just syntactical sugar for class methods, which may also be used:
|
@@ -492,29 +708,55 @@ class Employee < ActiveRecord::Base
|
|
492
708
|
where('start_date >= ?', date)
|
493
709
|
end
|
494
710
|
|
495
|
-
private
|
496
|
-
|
497
711
|
def self.ransackable_scopes(auth_object = nil)
|
498
712
|
if auth_object.try(:admin?)
|
499
713
|
# allow admin users access to all three methods
|
500
|
-
%i(
|
714
|
+
%i(activated hired_since salary_gt)
|
501
715
|
else
|
502
|
-
# allow other users to search on
|
503
|
-
%i(
|
716
|
+
# allow other users to search on `activated` and `hired_since` only
|
717
|
+
%i(activated hired_since)
|
504
718
|
end
|
505
719
|
end
|
720
|
+
|
721
|
+
private_class_method :ransackable_scopes
|
506
722
|
end
|
507
723
|
|
508
|
-
Employee.ransack({
|
724
|
+
Employee.ransack({ activated: true, hired_since: '2013-01-01' })
|
509
725
|
|
510
726
|
Employee.ransack({ salary_gt: 100_000 }, { auth_object: current_user })
|
511
727
|
```
|
512
728
|
|
513
|
-
|
514
|
-
that will convert it to a string
|
515
|
-
|
516
|
-
|
517
|
-
|
729
|
+
In Rails 3 and 4, if the `true` value is being passed via url params or some
|
730
|
+
other mechanism that will convert it to a string, the true value may not be
|
731
|
+
passed to the ransackable scope unless you wrap it in an array
|
732
|
+
(i.e. `activated: ['true']`). Ransack will take care of changing 'true' into a
|
733
|
+
boolean. This is currently resolved in Rails 5 :smiley:
|
734
|
+
|
735
|
+
However, perhaps you have `user_id: [1]` and you do not want Ransack to convert
|
736
|
+
1 into a boolean. (Values sanitized to booleans can be found in the
|
737
|
+
[constants.rb](https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/constants.rb#L28)).
|
738
|
+
To turn this off globally, and handle type conversions yourself, set
|
739
|
+
`sanitize_custom_scope_booleans` to false in an initializer file like
|
740
|
+
config/initializers/ransack.rb:
|
741
|
+
|
742
|
+
```ruby
|
743
|
+
Ransack.configure do |c|
|
744
|
+
c.sanitize_custom_scope_booleans = false
|
745
|
+
end
|
746
|
+
```
|
747
|
+
|
748
|
+
To turn this off on a per-scope basis Ransack adds the following method to
|
749
|
+
`ActiveRecord::Base` that you can redefine to selectively override sanitization:
|
750
|
+
|
751
|
+
`ransackable_scopes_skip_sanitize_args`
|
752
|
+
|
753
|
+
Add the scope you wish to bypass this behavior to ransackable_scopes_skip_sanitize_args:
|
754
|
+
|
755
|
+
```ruby
|
756
|
+
def ransackable_scopes_skip_sanitize_args
|
757
|
+
[:scope_to_skip_sanitize_args]
|
758
|
+
end
|
759
|
+
```
|
518
760
|
|
519
761
|
Scopes are a recent addition to Ransack and currently have a few caveats:
|
520
762
|
First, a scope involving child associations needs to be defined in the parent
|
@@ -523,8 +765,7 @@ argument are not easily usable yet, because the array currently needs to be
|
|
523
765
|
wrapped in an array to function (see
|
524
766
|
[this issue](https://github.com/activerecord-hackery/ransack/issues/404)),
|
525
767
|
which is not compatible with Ransack form helpers. For this use case, it may be
|
526
|
-
better for now to use [ransackers]
|
527
|
-
(https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers) instead,
|
768
|
+
better for now to use [ransackers](https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers) instead,
|
528
769
|
where feasible. Pull requests with solutions and tests are welcome!
|
529
770
|
|
530
771
|
### Grouping queries by OR instead of AND
|
@@ -648,9 +889,9 @@ en:
|
|
648
889
|
|
649
890
|
## Mongoid
|
650
891
|
|
651
|
-
|
652
|
-
with Mongoid
|
653
|
-
|
892
|
+
Mongoid support has been moved to its own gem at [ransack-mongoid](https://github.com/activerecord-hackery/ransack-mongoid).
|
893
|
+
Ransack works with Mongoid in the same way as Active Record, except that with
|
894
|
+
Mongoid, associations are not currently supported. Demo source code may be found
|
654
895
|
[here](https://github.com/Zhomart/ransack-mongodb-demo). A `result` method
|
655
896
|
called on a `ransack` search returns a `Mongoid::Criteria` object:
|
656
897
|
|
@@ -662,16 +903,10 @@ called on a `ransack` search returns a `Mongoid::Criteria` object:
|
|
662
903
|
@people = @q.result.active.order_by(updated_at: -1).limit(10)
|
663
904
|
```
|
664
905
|
|
665
|
-
|
906
|
+
NOTE: Ransack currently works with either Active Record or Mongoid, but not
|
666
907
|
both in the same application. If both are present, Ransack will default to
|
667
|
-
Active Record only.
|
668
|
-
|
669
|
-
```ruby
|
670
|
-
@current_adapters ||= {
|
671
|
-
:active_record => defined?(::ActiveRecord::Base),
|
672
|
-
:mongoid => defined?(::Mongoid) && !defined?(::ActiveRecord::Base)
|
673
|
-
}
|
674
|
-
```
|
908
|
+
Active Record only. The logic is contained in
|
909
|
+
`Ransack::Adapters#instantiate_object_mapper` should you need to override it.
|
675
910
|
|
676
911
|
## Semantic Versioning
|
677
912
|
|
@@ -700,6 +935,31 @@ directly related to bug reports, pull requests, or documentation improvements.
|
|
700
935
|
to you. The more people who are using the project, the quicker we can find and
|
701
936
|
fix bugs!
|
702
937
|
|
703
|
-
##
|
938
|
+
## Contributors
|
939
|
+
|
940
|
+
This project exists thanks to all the people who contribute. <img src="https://opencollective.com/ransack/contributors.svg?width=890&button=false" />
|
941
|
+
|
942
|
+
|
943
|
+
## Backers
|
944
|
+
|
945
|
+
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/ransack#backer)]
|
946
|
+
|
947
|
+
<a href="https://opencollective.com/ransack#backers" target="_blank"><img src="https://opencollective.com/ransack/backers.svg?width=890"></a>
|
948
|
+
|
949
|
+
|
950
|
+
## Sponsors
|
951
|
+
|
952
|
+
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/ransack#sponsor)]
|
953
|
+
|
954
|
+
<a href="https://opencollective.com/ransack/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/0/avatar.svg"></a>
|
955
|
+
<a href="https://opencollective.com/ransack/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/1/avatar.svg"></a>
|
956
|
+
<a href="https://opencollective.com/ransack/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/2/avatar.svg"></a>
|
957
|
+
<a href="https://opencollective.com/ransack/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/3/avatar.svg"></a>
|
958
|
+
<a href="https://opencollective.com/ransack/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/4/avatar.svg"></a>
|
959
|
+
<a href="https://opencollective.com/ransack/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/5/avatar.svg"></a>
|
960
|
+
<a href="https://opencollective.com/ransack/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/6/avatar.svg"></a>
|
961
|
+
<a href="https://opencollective.com/ransack/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/7/avatar.svg"></a>
|
962
|
+
<a href="https://opencollective.com/ransack/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/8/avatar.svg"></a>
|
963
|
+
<a href="https://opencollective.com/ransack/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/9/avatar.svg"></a>
|
964
|
+
|
704
965
|
|
705
|
-
Copyright © 2011-2015 [Ernie Miller](http://twitter.com/erniemiller)
|