ransack 2.4.2 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/cronjob.yml +6 -9
  3. data/.github/workflows/deploy.yml +35 -0
  4. data/.github/workflows/rubocop.yml +1 -1
  5. data/.github/workflows/test-deploy.yml +29 -0
  6. data/.github/workflows/test.yml +16 -40
  7. data/.nojekyll +0 -0
  8. data/CHANGELOG.md +137 -11
  9. data/CONTRIBUTING.md +4 -3
  10. data/Gemfile +2 -2
  11. data/README.md +45 -973
  12. data/docs/.gitignore +19 -0
  13. data/docs/.nojekyll +0 -0
  14. data/docs/babel.config.js +3 -0
  15. data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
  16. data/docs/docs/getting-started/_category_.json +4 -0
  17. data/docs/docs/getting-started/advanced-mode.md +46 -0
  18. data/docs/docs/getting-started/configuration.md +47 -0
  19. data/docs/docs/getting-started/search-matches.md +67 -0
  20. data/docs/docs/getting-started/simple-mode.md +284 -0
  21. data/docs/docs/getting-started/sorting.md +79 -0
  22. data/docs/docs/getting-started/using-predicates.md +282 -0
  23. data/docs/docs/going-further/_category_.json +4 -0
  24. data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
  25. data/docs/docs/going-further/associations.md +70 -0
  26. data/docs/docs/going-further/custom-predicates.md +52 -0
  27. data/docs/docs/going-further/documentation.md +43 -0
  28. data/docs/docs/going-further/exporting-to-csv.md +49 -0
  29. data/docs/docs/going-further/external-guides.md +57 -0
  30. data/docs/docs/going-further/form-customisation.md +63 -0
  31. data/docs/docs/going-further/i18n.md +53 -0
  32. data/docs/docs/going-further/merging-searches.md +41 -0
  33. data/docs/docs/going-further/other-notes.md +428 -0
  34. data/docs/docs/going-further/polymorphic-search.md +40 -0
  35. data/docs/docs/going-further/ransackers.md +331 -0
  36. data/docs/docs/going-further/release_process.md +36 -0
  37. data/docs/docs/going-further/saving-queries.md +82 -0
  38. data/docs/docs/going-further/searching-postgres.md +57 -0
  39. data/docs/docs/going-further/wiki-contributors.md +82 -0
  40. data/docs/docs/intro.md +99 -0
  41. data/docs/docusaurus.config.js +107 -0
  42. data/docs/package.json +37 -0
  43. data/docs/sidebars.js +31 -0
  44. data/docs/src/components/HomepageFeatures/index.js +64 -0
  45. data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  46. data/docs/src/css/custom.css +39 -0
  47. data/docs/src/pages/index.module.css +23 -0
  48. data/docs/src/pages/markdown-page.md +7 -0
  49. data/docs/static/.nojekyll +0 -0
  50. data/docs/static/img/docusaurus.png +0 -0
  51. data/docs/static/img/favicon.ico +0 -0
  52. data/docs/static/img/logo.svg +1 -0
  53. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  54. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  55. data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  56. data/docs/static/img/undraw_docusaurus_react.svg +170 -0
  57. data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  58. data/docs/yarn.lock +7671 -0
  59. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -4
  60. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -1
  61. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -1
  62. data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
  63. data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
  64. data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
  65. data/lib/polyamorous.rb +1 -0
  66. data/lib/ransack/adapters/active_record/base.rb +1 -3
  67. data/lib/ransack/adapters/active_record/context.rb +24 -51
  68. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +2 -9
  69. data/lib/ransack/configuration.rb +16 -2
  70. data/lib/ransack/constants.rb +0 -3
  71. data/lib/ransack/helpers/form_helper.rb +11 -3
  72. data/lib/ransack/locale/sv.yml +70 -0
  73. data/lib/ransack/nodes/sort.rb +2 -2
  74. data/lib/ransack/search.rb +4 -3
  75. data/lib/ransack/translate.rb +1 -1
  76. data/lib/ransack/version.rb +1 -1
  77. data/ransack.gemspec +5 -5
  78. data/spec/helpers/polyamorous_helper.rb +2 -8
  79. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  80. data/spec/polyamorous/join_association_spec.rb +1 -6
  81. data/spec/polyamorous/join_dependency_spec.rb +0 -16
  82. data/spec/ransack/adapters/active_record/base_spec.rb +28 -11
  83. data/spec/ransack/configuration_spec.rb +14 -0
  84. data/spec/ransack/helpers/form_helper_spec.rb +57 -2
  85. data/spec/ransack/nodes/condition_spec.rb +13 -0
  86. data/spec/ransack/search_spec.rb +140 -27
  87. data/spec/support/schema.rb +49 -0
  88. metadata +80 -29
  89. data/docs/release_process.md +0 -20
  90. data/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +0 -24
  91. data/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -79
  92. data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -11
  93. data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -1
  94. data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +0 -80
  95. data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -1
  96. /data/docs/{img → docs/going-further/img}/create_release.png +0 -0
  97. /data/{logo → docs/static/logo}/ransack-h.png +0 -0
  98. /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
  99. /data/{logo → docs/static/logo}/ransack-v.png +0 -0
  100. /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
  101. /data/{logo → docs/static/logo}/ransack.png +0 -0
  102. /data/{logo → docs/static/logo}/ransack.svg +0 -0
  103. /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_association.rb +0 -0
  104. /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_dependency.rb +0 -0
  105. /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/reflection.rb +0 -0
data/docs/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ # Dependencies
2
+ /node_modules
3
+
4
+ # Production
5
+ /build
6
+
7
+ # Generated files
8
+ .docusaurus
9
+ .cache-loader
10
+
11
+ # Misc
12
+ .DS_Store
13
+ .env.local
14
+ .env.development.local
15
+ .env.test.local
16
+ .env.production.local
17
+
18
+ yarn-debug.log*
19
+ yarn-error.log*
data/docs/.nojekyll ADDED
File without changes
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3
+ };
@@ -0,0 +1,20 @@
1
+ ---
2
+ slug: ransack-3-0-0
3
+ title: Ransack 3.0.0
4
+ authors:
5
+ name: Sean Carroll
6
+ title: Ransack Core Team
7
+ tags: [ransack, release]
8
+ ---
9
+
10
+ Ransack has been a part of many Rubyists toolboxes for years and 3.0.0 is a major release. We have a number of new features and one breaking change. As part of 3.0.0, we decided to launch this documentation website, merging in the Wiki and the content from the README.
11
+
12
+ With 3.0.0 we are hoping to re-energise the community, we need help on closing out old issues, refactoring the codebase and even some design work.
13
+
14
+ I also wanted to let you know that Ernie Miller (creator of Ransack) has decided to leave the project completely, he has this message for the community:
15
+
16
+ > While my own personal development efforts have been spent elsewhere as of late, I'm keenly aware of how many people still depend on some of the software I originally wrote all those years ago.
17
+
18
+ > That's why I'm grateful to be able to step away from the ActiveRecord Hackery organization (and, specifically, maintenance of Ransack) without impacting those users. I'm thankful that Sean, David, Greg, and others will continue to support users, and wish them the best as they move forward without me!
19
+
20
+ Please join me in thanking Ernie for bringing Ransack to life, I personally think it is one of the most amazing Rails libraries out there.
@@ -0,0 +1,4 @@
1
+ {
2
+ "label": "Getting started",
3
+ "position": 2
4
+ }
@@ -0,0 +1,46 @@
1
+ ---
2
+ sidebar_position: 2
3
+ title: Advanced Mode
4
+ ---
5
+
6
+
7
+ "Advanced" searches Rails's nested attributes functionality in order to
8
+ generate complex queries with nested AND/OR groupings, etc. This takes a bit
9
+ more work but can generate some pretty cool search interfaces that put a lot of
10
+ power in the hands of your users.
11
+
12
+ A notable drawback with these searches is
13
+ that the increased size of the parameter string will typically force you to use
14
+ the HTTP POST method instead of GET.
15
+
16
+
17
+ ## Tweak your routes
18
+
19
+ ```ruby
20
+ resources :people do
21
+ collection do
22
+ match 'search' => 'people#search', via: [:get, :post], as: :search
23
+ end
24
+ end
25
+ ```
26
+
27
+ ## Add a controller action
28
+
29
+ ```ruby
30
+ def search
31
+ index
32
+ render :index
33
+ end
34
+ ```
35
+
36
+ ## Update your form
37
+
38
+ ```erb
39
+ <%= search_form_for @q, url: search_people_path,
40
+ html: { method: :post } do |f| %>
41
+ ```
42
+
43
+ Once you've done so, you can make use of the helpers in [Ransack::Helpers::FormBuilder](https://github.com/activerecord-hackery/ransack/lib/ransack/helpers/form_builder.rb) to
44
+ construct much more complex search forms, such as the one on the
45
+ [demo app](http://ransack-demo.herokuapp.com/users/advanced_search)
46
+ (source code [here](https://github.com/activerecord-hackery/ransack_demo)).
@@ -0,0 +1,47 @@
1
+ ---
2
+ sidebar_position: 3
3
+ title: Configuration
4
+ ---
5
+
6
+
7
+
8
+ Ransack may be easily configured. The best place to put configuration is in an initializer file at `config/initializers/ransack.rb`, containing code such as:
9
+
10
+ ```ruby
11
+ Ransack.configure do |config|
12
+
13
+ # Change default search parameter key name.
14
+ # Default key name is :q
15
+ config.search_key = :query
16
+
17
+ # Raise errors if a query contains an unknown predicate or attribute.
18
+ # Default is true (do not raise error on unknown conditions).
19
+ config.ignore_unknown_conditions = false
20
+
21
+ # Globally display sort links without the order indicator arrow.
22
+ # Default is false (sort order indicators are displayed).
23
+ # This can also be configured individually in each sort link (see the README).
24
+ config.hide_sort_order_indicators = true
25
+
26
+ end
27
+ ```
28
+
29
+ ## Custom search parameter key name
30
+
31
+ Sometimes there are situations when the default search parameter name cannot be used, for instance,
32
+ if there are two searches on one page. Another name may be set using the `search_key` option in the `ransack` or `search` methods in the controller, and in the `@search_form_for` method in the view.
33
+
34
+ ### In the controller
35
+
36
+ ```ruby
37
+ @search = Log.ransack(params[:log_search], search_key: :log_search)
38
+ # or
39
+ @search = Log.search(params[:log_search], search_key: :log_search)
40
+ ```
41
+
42
+ ### In the view
43
+
44
+ ```erb
45
+ <%= f.search_form_for @search, as: :log_search %>
46
+ <%= sort_link(@search) %>
47
+ ```
@@ -0,0 +1,67 @@
1
+ ---
2
+ title: Search Matchers
3
+ ---
4
+
5
+ ### Search Matchers
6
+
7
+ List of all possible predicates
8
+
9
+
10
+ | Predicate | Description | Notes |
11
+ | ------------- | ------------- |-------- |
12
+ | `*_eq` | equal | |
13
+ | `*_not_eq` | not equal | |
14
+ | `*_matches` | matches with `LIKE` | e.g. `q[email_matches]=%@gmail.com`|
15
+ | `*_does_not_match` | does not match with `LIKE` | |
16
+ | `*_matches_any` | Matches any | |
17
+ | `*_matches_all` | Matches all | |
18
+ | `*_does_not_match_any` | Does not match any | |
19
+ | `*_does_not_match_all` | Does not match all | |
20
+ | `*_lt` | less than | |
21
+ | `*_lteq` | less than or equal | |
22
+ | `*_gt` | greater than | |
23
+ | `*_gteq` | greater than or equal | |
24
+ | `*_present` | not null and not empty | Only compatible with string columns. Example: `q[name_present]=1` (SQL: `col is not null AND col != ''`) |
25
+ | `*_blank` | is null or empty. | (SQL: `col is null OR col = ''`) |
26
+ | `*_null` | is null | |
27
+ | `*_not_null` | is not null | |
28
+ | `*_in` | match any values in array | e.g. `q[name_in][]=Alice&q[name_in][]=Bob` |
29
+ | `*_not_in` | match none of values in array | |
30
+ | `*_lt_any` | Less than any | SQL: `col < value1 OR col < value2` |
31
+ | `*_lteq_any` | Less than or equal to any | |
32
+ | `*_gt_any` | Greater than any | |
33
+ | `*_gteq_any` | Greater than or equal to any | |
34
+ | `*_lt_all` | Less than all | SQL: `col < value1 AND col < value2` |
35
+ | `*_lteq_all` | Less than or equal to all | |
36
+ | `*_gt_all` | Greater than all | |
37
+ | `*_gteq_all` | Greater than or equal to all | |
38
+ | `*_not_eq_all` | none of values in a set | |
39
+ | `*_start` | Starts with | SQL: `col LIKE 'value%'` |
40
+ | `*_not_start` | Does not start with | |
41
+ | `*_start_any` | Starts with any of | |
42
+ | `*_start_all` | Starts with all of | |
43
+ | `*_not_start_any` | Does not start with any of | |
44
+ | `*_not_start_all` | Does not start with all of | |
45
+ | `*_end` | Ends with | SQL: `col LIKE '%value'` |
46
+ | `*_not_end` | Does not end with | |
47
+ | `*_end_any` | Ends with any of | |
48
+ | `*_end_all` | Ends with all of | |
49
+ | `*_not_end_any` | | |
50
+ | `*_not_end_all` | | |
51
+ | `*_cont` | Contains value | uses `LIKE` |
52
+ | `*_cont_any` | Contains any of | |
53
+ | `*_cont_all` | Contains all of | |
54
+ | `*_not_cont` | Does not contain |
55
+ | `*_not_cont_any` | Does not contain any of | |
56
+ | `*_not_cont_all` | Does not contain all of | |
57
+ | `*_i_cont` | Contains value with case insensitive | uses `ILIKE` |
58
+ | `*_i_cont_any` | Contains any of values with case insensitive | |
59
+ | `*_i_cont_all` | Contains all of values with case insensitive | |
60
+ | `*_not_i_cont` | Does not contain with case insensitive |
61
+ | `*_not_i_cont_any` | Does not contain any of values with case insensitive | |
62
+ | `*_not_i_cont_all` | Does not contain all of values with case insensitive | |
63
+ | `*_true` | is true | |
64
+ | `*_false` | is false | |
65
+
66
+
67
+ See full list: https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/locale/en.yml#L15
@@ -0,0 +1,284 @@
1
+ ---
2
+ sidebar_position: 1
3
+ title: Simple mode
4
+ ---
5
+
6
+ # Simple Mode
7
+
8
+ Ransack can be used in one of two modes, simple or advanced. For
9
+ searching/filtering not requiring complex boolean logic, Ransack's simple
10
+ mode should meet your needs.
11
+
12
+ ## In your controller
13
+
14
+ ```jsx
15
+ def index
16
+ @q = Person.ransack(params[:q])
17
+ @people = @q.result(distinct: true)
18
+ end
19
+ ```
20
+ or without `distinct: true`, for sorting on an associated table's columns (in
21
+ this example, with preloading each Person's Articles and pagination):
22
+
23
+ ```jsx
24
+ def index
25
+ @q = Person.ransack(params[:q])
26
+ @people = @q.result.includes(:articles).page(params[:page])
27
+ end
28
+ ```
29
+
30
+ ### Default search options
31
+
32
+ #### Search parameter
33
+
34
+ Ransack uses a default `:q` param key for search params. This may be changed by
35
+ setting the `search_key` option in a Ransack initializer file (typically
36
+ `config/initializers/ransack.rb`):
37
+
38
+ ```jsx
39
+ Ransack.configure do |c|
40
+ # Change default search parameter key name.
41
+ # Default key name is :q
42
+ c.search_key = :query
43
+ end
44
+ ```
45
+
46
+ #### String search
47
+
48
+ After version 2.4.0 when searching a string query Ransack by default strips all whitespace around the query string.
49
+ This may be disabled by setting the `strip_whitespace` option in a Ransack initializer file:
50
+
51
+ ```jsx
52
+ Ransack.configure do |c|
53
+ # Change whitespace stripping behaviour.
54
+ # Default is true
55
+ c.strip_whitespace = false
56
+ end
57
+ ```
58
+
59
+ ## In your view
60
+
61
+ The two primary Ransack view helpers are `search_form_for` and `sort_link`,
62
+ which are defined in
63
+ [Ransack::Helpers::FormHelper](https://github.com/activerecord-hackery/ransack/lib/ransack/helpers/form_helper.rb).
64
+
65
+ ### Form helper
66
+
67
+ Ransack's `search_form_for` helper replaces `form_for` for creating the view search form
68
+
69
+ ```jsx
70
+ <%= search_form_for @q do |f| %>
71
+
72
+ # Search if the name field contains...
73
+ <%= f.label :name_cont %>
74
+ <%= f.search_field :name_cont %>
75
+
76
+ # Search if an associated articles.title starts with...
77
+ <%= f.label :articles_title_start %>
78
+ <%= f.search_field :articles_title_start %>
79
+
80
+ # Attributes may be chained. Search multiple attributes for one value...
81
+ <%= f.label :name_or_description_or_email_or_articles_title_cont %>
82
+ <%= f.search_field :name_or_description_or_email_or_articles_title_cont %>
83
+
84
+ <%= f.submit %>
85
+ <% end %>
86
+ ```
87
+
88
+ The argument of `f.search_field` has to be in this form:
89
+ `attribute_name[_or_attribute_name]..._predicate`
90
+
91
+ where `[_or_another_attribute_name]...` means any repetition of `_or_` plus the name of the attribute.
92
+
93
+ `cont` (contains) and `start` (starts with) are just two of the available
94
+ search predicates.
95
+
96
+ The `search_form_for` answer format can be set like this:
97
+
98
+ ```jsx
99
+ <%= search_form_for(@q, format: :pdf) do |f| %>
100
+
101
+ <%= search_form_for(@q, format: :json) do |f| %>
102
+ ```
103
+
104
+ ### Search link helper
105
+
106
+ Ransack's `sort_link` helper creates table headers that are sortable links
107
+
108
+ ```jsx
109
+ <%= sort_link(@q, :name) %>
110
+ ```
111
+ Additional options can be passed after the column parameter, like a different
112
+ column title or a default sort order.
113
+
114
+ If the first option after the column parameter is a String, it's considered a
115
+ custom label for the link:
116
+
117
+ ```jsx
118
+ <%= sort_link(@q, :name, 'Last Name', default_order: :desc) %>
119
+ ```
120
+
121
+ You can use a block if the link markup is hard to fit into the label parameter:
122
+
123
+ ```jsx
124
+ <%= sort_link(@q, :name) do %>
125
+ <strong>Player Name</strong>
126
+ <% end %>
127
+ ```
128
+
129
+ With a polymorphic association, you may need to specify the name of the link
130
+ explicitly to avoid an `uninitialized constant Model::Xxxable` error (see issue
131
+ [#421](https://github.com/activerecord-hackery/ransack/issues/421)):
132
+
133
+ ```jsx
134
+ <%= sort_link(@q, :xxxable_of_Ymodel_type_some_attribute, 'Attribute Name') %>
135
+ ```
136
+
137
+ If the first option after the column parameter and/or the label parameter is an
138
+ Array, it will be used for sorting on multiple fields:
139
+
140
+ ```jsx
141
+ <%= sort_link(@q, :last_name, [:last_name, 'first_name asc'], 'Last Name') %>
142
+ ```
143
+
144
+ In the example above, clicking the link will sort by `last_name` and then
145
+ `first_name`. Specifying the sort direction on a field in the array tells
146
+ Ransack to _always_ sort that particular field in the specified direction.
147
+
148
+ Multiple `default_order` fields may also be specified with a trailing options
149
+ Hash:
150
+
151
+ ```jsx
152
+ <%= sort_link(@q, :last_name, %i(last_name first_name),
153
+ default_order: { last_name: 'asc', first_name: 'desc' }) %>
154
+ ```
155
+
156
+ This example toggles the sort directions of both fields, by default
157
+ initially sorting the `last_name` field by ascending order, and the
158
+ `first_name` field by descending order.
159
+
160
+ In the case that you wish to sort by some complex value, such as the result
161
+ of a SQL function, you may do so using scopes. In your model, define scopes
162
+ whose names line up with the name of the virtual field you wish to sort by,
163
+ as so:
164
+
165
+ ```jsx
166
+ class Person < ActiveRecord::Base
167
+ scope :sort_by_reverse_name_asc, lambda { order("REVERSE(name) ASC") }
168
+ scope :sort_by_reverse_name_desc, lambda { order("REVERSE(name) DESC") }
169
+ ...
170
+ ```
171
+
172
+ and you can then sort by this virtual field:
173
+
174
+ ```jsx
175
+ <%= sort_link(@q, :reverse_name) %>
176
+ ```
177
+
178
+ The trailing options Hash can also be used for passing additional options to the
179
+ generated link, like `class:`.
180
+
181
+ The sort link order indicator arrows may be globally customized by setting a
182
+ `custom_arrows` option in an initializer file like
183
+ `config/initializers/ransack.rb`.
184
+
185
+ You can also enable a `default_arrow` which is displayed on all sortable fields
186
+ which are not currently used in the sorting. This is disabled by default so
187
+ nothing will be displayed:
188
+
189
+ ```jsx
190
+ Ransack.configure do |c|
191
+ c.custom_arrows = {
192
+ up_arrow: '<i class="custom-up-arrow-icon"></i>',
193
+ down_arrow: 'U+02193',
194
+ default_arrow: '<i class="default-arrow-icon"></i>'
195
+ }
196
+ end
197
+ ```
198
+
199
+ All sort links may be displayed without the order indicator
200
+ arrows by setting `hide_sort_order_indicators` to true in the initializer file.
201
+ Note that this hides the arrows even if they were customized:
202
+
203
+ ```jsx
204
+ Ransack.configure do |c|
205
+ c.hide_sort_order_indicators = true
206
+ end
207
+ ```
208
+
209
+ Without setting it globally, individual sort links may be displayed without
210
+ the order indicator arrow by passing `hide_indicator: true` in the sort link:
211
+
212
+ ```jsx
213
+ <%= sort_link(@q, :name, hide_indicator: true) %>
214
+ ```
215
+
216
+ ### sort_url
217
+
218
+ Ransack's `sort_url` helper is like a `sort_link` but returns only the url
219
+
220
+ `sort_url` has the same API as `sort_link`:
221
+
222
+ ```jsx
223
+ <%= sort_url(@q, :name, default_order: :desc) %>
224
+ ```
225
+
226
+ ```jsx
227
+ <%= sort_url(@q, :last_name, [:last_name, 'first_name asc']) %>
228
+ ```
229
+
230
+ ```jsx
231
+ <%= sort_url(@q, :last_name, %i(last_name first_name),
232
+ default_order: { last_name: 'asc', first_name: 'desc' }) %>
233
+ ```
234
+
235
+ ### PostgreSQL's sort option
236
+
237
+ The `NULLS FIRST` and `NULLS LAST` options can be used to determine whether nulls appear before or after non-null values in the sort ordering.
238
+
239
+ You may want to configure it like this:
240
+
241
+ ```jsx
242
+ Ransack.configure do |c|
243
+ c.postgres_fields_sort_option = :nulls_first # or :nulls_last
244
+ end
245
+ ```
246
+
247
+ To treat nulls as having the lowest or highest value respectively. To force nulls to always be first or last, use
248
+
249
+ ```jsx
250
+ Ransack.configure do |c|
251
+ c.postgres_fields_sort_option = :nulls_always_first # or :nulls_always_last
252
+ end
253
+ ```
254
+
255
+ See this feature: https://www.postgresql.org/docs/13/queries-order.html
256
+
257
+ #### Case Insensitive Sorting in PostgreSQL
258
+
259
+ In order to request PostgreSQL to do a case insensitive sort for all string columns of a model at once, Ransack can be extended by using this approach:
260
+
261
+ ```jsx
262
+ module RansackObject
263
+
264
+ def self.included(base)
265
+ base.columns.each do |column|
266
+ if column.type == :string
267
+ base.ransacker column.name.to_sym, type: :string do
268
+ Arel.sql("lower(#{base.table_name}.#{column.name})")
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
274
+ ```
275
+
276
+ ```jsx
277
+ class UserWithManyAttributes < ActiveRecord::Base
278
+ include RansackObject
279
+ end
280
+ ```
281
+
282
+ If this approach is taken, it is advisable to [add a functional index](https://www.postgresql.org/docs/13/citext.html).
283
+
284
+ This was originally asked in [a Ransack issue](https://github.com/activerecord-hackery/ransack/issues/1201) and a solution was found on [Stack Overflow](https://stackoverflow.com/a/34677378).
@@ -0,0 +1,79 @@
1
+ ---
2
+ title: Sorting
3
+ ---
4
+
5
+
6
+ # Sorting
7
+
8
+ ## Sorting in the View
9
+
10
+ You can add a form to capture sorting and filtering options together.
11
+
12
+ ```jsx
13
+ <div class="filters" id="filtersSidebar">
14
+ <header class="filters-header">
15
+ <div class="filters-header-content">
16
+ <h3>Filters</h3>
17
+ </div>
18
+ </header>
19
+
20
+ <div class="filters-content">
21
+ <%= search_form_for @q,
22
+ class: 'form',
23
+ url: articles_path,
24
+ html: { autocomplete: 'off', autocapitalize: 'none' } do |f| %>
25
+
26
+ <div class="form-group">
27
+ <%= f.label :title_cont, t('Filter_by_keyword') %>
28
+ <%= f.search_field :title_cont %>
29
+ </div>
30
+
31
+ <%= render partial: 'filters/date_title_sort', locals: { f: f } %>
32
+
33
+ <div class="form-group">
34
+ <%= f.label :grade_level_gteq, t('Grade_level') %> >=
35
+ <%= f.search_field :grade_level_gteq %>
36
+ </div>
37
+
38
+ <div class="form-group">
39
+ <%= f.label :readability_gteq, t('Readability') %> >=
40
+ <%= f.search_field :readability_gteq %>
41
+ </div>
42
+
43
+ <div class="form-group">
44
+ <i><%= @articles.total_count %> articles</i>
45
+ </div>
46
+
47
+ <div class="form-group">
48
+ <hr/>
49
+ <div class="filters-header-content">
50
+ <%= link_to request.path, class: 'form-link' do %>
51
+ <i class="far fa-undo icon-l"></i><%= t('Clear_all') %>
52
+ <% end %>
53
+
54
+ <%= f.submit t('Filter'), class: 'btn btn-primary' %>
55
+ </div>
56
+ </div>
57
+ <% end %>
58
+ </div>
59
+ </div>
60
+ ```
61
+
62
+
63
+ ## Sorting in the Controller
64
+
65
+ To specify a default search sort field + order in the controller `index`:
66
+
67
+ ```ruby
68
+ @search = Post.ransack(params[:q])
69
+ @search.sorts = 'name asc' if @search.sorts.empty?
70
+ @posts = @search.result.paginate(page: params[:page], per_page: 20)
71
+ ```
72
+
73
+ Multiple sorts can be set by:
74
+
75
+ ```ruby
76
+ @search = Post.ransack(params[:q])
77
+ @search.sorts = ['name asc', 'created_at desc'] if @search.sorts.empty?
78
+ @posts = @search.result.paginate(page: params[:page], per_page: 20)
79
+ ```