ransack 2.3.2 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/SECURITY.md +12 -0
- data/.github/workflows/codeql.yml +72 -0
- data/.github/workflows/cronjob.yml +99 -0
- data/.github/workflows/deploy.yml +35 -0
- data/.github/workflows/rubocop.yml +20 -0
- data/.github/workflows/test-deploy.yml +29 -0
- data/.github/workflows/test.yml +131 -0
- data/.nojekyll +0 -0
- data/.rubocop.yml +50 -0
- data/CHANGELOG.md +251 -1
- data/CONTRIBUTING.md +51 -29
- data/Gemfile +12 -10
- data/README.md +45 -907
- data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
- data/bug_report_templates/test-ransacker-arel-present-predicate.rb +75 -0
- data/docs/.gitignore +19 -0
- data/docs/.nojekyll +0 -0
- data/docs/babel.config.js +3 -0
- data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
- data/docs/docs/getting-started/_category_.json +4 -0
- data/docs/docs/getting-started/advanced-mode.md +46 -0
- data/docs/docs/getting-started/configuration.md +47 -0
- data/docs/docs/getting-started/search-matches.md +67 -0
- data/docs/docs/getting-started/simple-mode.md +288 -0
- data/docs/docs/getting-started/sorting.md +71 -0
- data/docs/docs/getting-started/using-predicates.md +282 -0
- data/docs/docs/going-further/_category_.json +4 -0
- data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
- data/docs/docs/going-further/associations.md +70 -0
- data/docs/docs/going-further/custom-predicates.md +52 -0
- data/docs/docs/going-further/documentation.md +43 -0
- data/docs/docs/going-further/exporting-to-csv.md +49 -0
- data/docs/docs/going-further/external-guides.md +57 -0
- data/docs/docs/going-further/form-customisation.md +63 -0
- data/docs/docs/going-further/i18n.md +53 -0
- data/docs/docs/going-further/img/create_release.png +0 -0
- data/docs/docs/going-further/merging-searches.md +41 -0
- data/docs/docs/going-further/other-notes.md +428 -0
- data/docs/docs/going-further/polymorphic-search.md +46 -0
- data/docs/docs/going-further/ransackers.md +331 -0
- data/docs/docs/going-further/release_process.md +36 -0
- data/docs/docs/going-further/saving-queries.md +82 -0
- data/docs/docs/going-further/searching-postgres.md +57 -0
- data/docs/docs/going-further/wiki-contributors.md +82 -0
- data/docs/docs/intro.md +99 -0
- data/docs/docusaurus.config.js +120 -0
- data/docs/package.json +42 -0
- data/docs/sidebars.js +31 -0
- data/docs/src/components/HomepageFeatures/index.js +64 -0
- data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- data/docs/src/css/custom.css +39 -0
- data/docs/src/pages/index.module.css +23 -0
- data/docs/src/pages/markdown-page.md +7 -0
- data/docs/static/.nojekyll +0 -0
- data/docs/static/img/docusaurus.png +0 -0
- data/docs/static/img/favicon.ico +0 -0
- data/docs/static/img/logo.svg +1 -0
- data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
- data/docs/static/img/tutorial/localeDropdown.png +0 -0
- data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
- data/docs/static/img/undraw_docusaurus_react.svg +170 -0
- data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- data/docs/yarn.lock +8879 -0
- data/lib/polyamorous/activerecord/join_association.rb +70 -0
- data/{polyamorous/lib/polyamorous/activerecord_6.0_ruby_2 → lib/polyamorous/activerecord}/join_dependency.rb +33 -12
- data/lib/polyamorous/activerecord/reflection.rb +11 -0
- data/{polyamorous/lib → lib/polyamorous}/polyamorous.rb +3 -4
- data/lib/ransack/adapters/active_record/base.rb +83 -10
- data/lib/ransack/adapters/active_record/context.rb +56 -44
- data/lib/ransack/configuration.rb +53 -10
- data/lib/ransack/constants.rb +126 -4
- data/lib/ransack/context.rb +34 -5
- data/lib/ransack/helpers/form_builder.rb +6 -6
- data/lib/ransack/helpers/form_helper.rb +14 -5
- data/lib/ransack/helpers.rb +1 -1
- data/lib/ransack/locale/sv.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +3 -3
- data/lib/ransack/nodes/condition.rb +80 -9
- data/lib/ransack/nodes/grouping.rb +4 -4
- data/lib/ransack/nodes/node.rb +1 -1
- data/lib/ransack/nodes/sort.rb +3 -3
- data/lib/ransack/nodes/value.rb +3 -3
- data/lib/ransack/predicate.rb +1 -1
- data/lib/ransack/ransacker.rb +1 -1
- data/lib/ransack/search.rb +15 -7
- data/lib/ransack/translate.rb +6 -6
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +38 -2
- data/lib/ransack.rb +5 -8
- data/ransack.gemspec +9 -15
- data/spec/blueprints/articles.rb +1 -1
- data/spec/blueprints/comments.rb +1 -1
- data/spec/blueprints/notes.rb +1 -1
- data/spec/blueprints/tags.rb +1 -1
- data/spec/console.rb +5 -5
- data/spec/helpers/polyamorous_helper.rb +2 -8
- data/spec/helpers/ransack_helper.rb +1 -1
- data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
- data/spec/{ransack → polyamorous}/join_association_spec.rb +3 -1
- data/spec/{ransack → polyamorous}/join_dependency_spec.rb +0 -16
- data/spec/ransack/adapters/active_record/base_spec.rb +125 -16
- data/spec/ransack/adapters/active_record/context_spec.rb +19 -18
- data/spec/ransack/configuration_spec.rb +33 -9
- data/spec/ransack/helpers/form_builder_spec.rb +8 -8
- data/spec/ransack/helpers/form_helper_spec.rb +109 -20
- data/spec/ransack/nodes/condition_spec.rb +37 -0
- data/spec/ransack/nodes/grouping_spec.rb +2 -2
- data/spec/ransack/nodes/value_spec.rb +115 -0
- data/spec/ransack/predicate_spec.rb +37 -2
- data/spec/ransack/search_spec.rb +238 -30
- data/spec/ransack/translate_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -5
- data/spec/support/schema.rb +108 -11
- metadata +98 -62
- data/.travis.yml +0 -47
- data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -128
- data/lib/ransack/adapters/active_record/ransack/context.rb +0 -55
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +0 -61
- data/lib/ransack/adapters/active_record/ransack/translate.rb +0 -8
- data/lib/ransack/adapters/active_record/ransack/visitor.rb +0 -47
- data/lib/ransack/adapters.rb +0 -64
- data/lib/ransack/nodes.rb +0 -8
- data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +0 -20
- data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -79
- data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -12
- data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +0 -2
- data/polyamorous/lib/polyamorous/version.rb +0 -3
- data/polyamorous/polyamorous.gemspec +0 -27
- /data/{logo → docs/static/logo}/ransack-h.png +0 -0
- /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
- /data/{logo → docs/static/logo}/ransack-v.png +0 -0
- /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
- /data/{logo → docs/static/logo}/ransack.png +0 -0
- /data/{logo → docs/static/logo}/ransack.svg +0 -0
- /data/{polyamorous/lib → lib}/polyamorous/join.rb +0 -0
- /data/{polyamorous/lib → lib}/polyamorous/swapping_reflection_class.rb +0 -0
- /data/{polyamorous/lib → lib}/polyamorous/tree_node.rb +0 -0
- /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
- /data/spec/{ransack → polyamorous}/join_spec.rb +0 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
# test-ransack-scope-and-column-same-name.rb
|
2
|
+
|
3
|
+
# This is a stand-alone test case.
|
4
|
+
|
5
|
+
# Run it in your console with: `ruby test-ransack-scope-and-column-same-name.rb`
|
6
|
+
|
7
|
+
# If you change the gem dependencies, run it with:
|
8
|
+
# `rm gemfile* && ruby test-ransack-scope-and-column-same-name.rb`
|
9
|
+
|
10
|
+
unless File.exist?('Gemfile')
|
11
|
+
File.write('Gemfile', <<-GEMFILE)
|
12
|
+
source 'https://rubygems.org'
|
13
|
+
|
14
|
+
# Rails master
|
15
|
+
gem 'rails', github: 'rails/rails', branch: '7-1-stable'
|
16
|
+
|
17
|
+
# Rails last release
|
18
|
+
# gem 'rails'
|
19
|
+
|
20
|
+
gem 'sqlite3'
|
21
|
+
gem 'ransack', github: 'activerecord-hackery/ransack'
|
22
|
+
GEMFILE
|
23
|
+
|
24
|
+
system 'bundle install'
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'bundler'
|
28
|
+
Bundler.setup(:default)
|
29
|
+
|
30
|
+
require 'active_record'
|
31
|
+
require 'minitest/autorun'
|
32
|
+
require 'logger'
|
33
|
+
require 'ransack'
|
34
|
+
|
35
|
+
# This connection will do for database-independent bug reports.
|
36
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
37
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
38
|
+
|
39
|
+
# Display versions.
|
40
|
+
message = "Running test case with Ruby #{RUBY_VERSION}, Active Record #{
|
41
|
+
::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION} and #{
|
42
|
+
::ActiveRecord::Base.connection.adapter_name}"
|
43
|
+
line = '=' * message.length
|
44
|
+
puts line, message, line
|
45
|
+
|
46
|
+
ActiveRecord::Schema.define do
|
47
|
+
create_table :users, force: true do |t|
|
48
|
+
t.boolean :active, null: false, default: true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class User < ActiveRecord::Base
|
53
|
+
scope :activated, -> (boolean = true) { where(active: boolean) }
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def self.ransackable_scopes(auth_object = nil)
|
58
|
+
%i(activated)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class BugTest < Minitest::Test
|
63
|
+
def test_activated_scope_equals_true
|
64
|
+
sql = User.ransack({ activated: true }).result.to_sql
|
65
|
+
puts sql
|
66
|
+
assert_equal(
|
67
|
+
"SELECT \"users\".* FROM \"users\" WHERE \"users\".\"active\" = 1", sql
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_activated_scope_equals_false
|
72
|
+
sql = User.ransack({ activated: false }).result.to_sql
|
73
|
+
puts sql
|
74
|
+
assert_equal(
|
75
|
+
"SELECT \"users\".* FROM \"users\"", sql
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# test-ransacker-arel-present-predicate.rb
|
2
|
+
|
3
|
+
# Run it in your console with: `ruby test-ransacker-arel-present-predicate.rb`
|
4
|
+
|
5
|
+
# If you change the gem dependencies, run it with:
|
6
|
+
# `rm gemfile* && ruby test-ransacker-arel-present-predicate.rb`
|
7
|
+
|
8
|
+
unless File.exist?('Gemfile')
|
9
|
+
File.write('Gemfile', <<-GEMFILE)
|
10
|
+
source 'https://rubygems.org'
|
11
|
+
|
12
|
+
# Rails master
|
13
|
+
gem 'rails', github: 'rails/rails', branch: '7-1-stable'
|
14
|
+
|
15
|
+
# Rails last release
|
16
|
+
# gem 'rails'
|
17
|
+
|
18
|
+
gem 'sqlite3'
|
19
|
+
gem 'ransack', github: 'activerecord-hackery/ransack'
|
20
|
+
GEMFILE
|
21
|
+
|
22
|
+
system 'bundle install'
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'bundler'
|
26
|
+
Bundler.setup(:default)
|
27
|
+
|
28
|
+
require 'active_record'
|
29
|
+
require 'minitest/autorun'
|
30
|
+
require 'logger'
|
31
|
+
require 'ransack'
|
32
|
+
|
33
|
+
# This connection will do for database-independent bug reports.
|
34
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
35
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
36
|
+
|
37
|
+
# Display versions.
|
38
|
+
message = "Running test case with Ruby #{RUBY_VERSION}, Active Record #{
|
39
|
+
::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION} and #{
|
40
|
+
::ActiveRecord::Base.connection.adapter_name}"
|
41
|
+
line = '=' * message.length
|
42
|
+
puts line, message, line
|
43
|
+
|
44
|
+
ActiveRecord::Schema.define do
|
45
|
+
create_table :projects, force: true do |t|
|
46
|
+
t.string :name
|
47
|
+
t.string :number
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Project < ActiveRecord::Base
|
52
|
+
ransacker :name do
|
53
|
+
Arel.sql('projects.name')
|
54
|
+
end
|
55
|
+
|
56
|
+
ransacker :number do |parent|
|
57
|
+
parent.table[:number]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.ransackable_attributes(_auth_object = nil)
|
61
|
+
["name", "number"]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class BugTest < Minitest::Test
|
66
|
+
def test_ransackers
|
67
|
+
sql = Project.ransack({ number_present: 1 }).result.to_sql
|
68
|
+
puts sql
|
69
|
+
assert_equal "SELECT \"projects\".* FROM \"projects\" WHERE (\"projects\".\"number\" IS NOT NULL AND \"projects\".\"number\" != '')", sql
|
70
|
+
|
71
|
+
sql = Project.ransack({ name_present: 1 }).result.to_sql
|
72
|
+
puts sql
|
73
|
+
assert_equal "SELECT \"projects\".* FROM \"projects\" WHERE (projects.name IS NOT NULL AND projects.name != '')", sql
|
74
|
+
end
|
75
|
+
end
|
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,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,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/blob/main/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/main/lib/ransack/locale/en.yml#L16
|
@@ -0,0 +1,288 @@
|
|
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
|
+
```ruby
|
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
|
+
```ruby
|
24
|
+
def index
|
25
|
+
@q = Person.ransack(params[:q])
|
26
|
+
@people = @q.result.includes(:articles).page(params[:page])
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
:::caution
|
31
|
+
By default, searching and sorting are authorized on any column of your model. See [Authorization (allowlisting/denylisting)](/going-further/other-notes.md#authorization-allowlistingdenylisting) on how to prevent this.
|
32
|
+
:::
|
33
|
+
|
34
|
+
### Default search options
|
35
|
+
|
36
|
+
#### Search parameter
|
37
|
+
|
38
|
+
Ransack uses a default `:q` param key for search params. This may be changed by
|
39
|
+
setting the `search_key` option in a Ransack initializer file (typically
|
40
|
+
`config/initializers/ransack.rb`):
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
Ransack.configure do |c|
|
44
|
+
# Change default search parameter key name.
|
45
|
+
# Default key name is :q
|
46
|
+
c.search_key = :query
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
#### String search
|
51
|
+
|
52
|
+
After version 2.4.0 when searching a string query Ransack by default strips all whitespace around the query string.
|
53
|
+
This may be disabled by setting the `strip_whitespace` option in a Ransack initializer file:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
Ransack.configure do |c|
|
57
|
+
# Change whitespace stripping behavior.
|
58
|
+
# Default is true
|
59
|
+
c.strip_whitespace = false
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
## In your view
|
64
|
+
|
65
|
+
The two primary Ransack view helpers are `search_form_for` and `sort_link`,
|
66
|
+
which are defined in
|
67
|
+
[Ransack::Helpers::FormHelper](https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/helpers/form_helper.rb).
|
68
|
+
|
69
|
+
### Form helper
|
70
|
+
|
71
|
+
Ransack's `search_form_for` helper replaces `form_for` for creating the view search form
|
72
|
+
|
73
|
+
```erb
|
74
|
+
<%= search_form_for @q do |f| %>
|
75
|
+
|
76
|
+
# Search if the name field contains...
|
77
|
+
<%= f.label :name_cont %>
|
78
|
+
<%= f.search_field :name_cont %>
|
79
|
+
|
80
|
+
# Search if an associated articles.title starts with...
|
81
|
+
<%= f.label :articles_title_start %>
|
82
|
+
<%= f.search_field :articles_title_start %>
|
83
|
+
|
84
|
+
# Attributes may be chained. Search multiple attributes for one value...
|
85
|
+
<%= f.label :name_or_description_or_email_or_articles_title_cont %>
|
86
|
+
<%= f.search_field :name_or_description_or_email_or_articles_title_cont %>
|
87
|
+
|
88
|
+
<%= f.submit %>
|
89
|
+
<% end %>
|
90
|
+
```
|
91
|
+
|
92
|
+
The argument of `f.search_field` has to be in this form:
|
93
|
+
`attribute_name[_or_attribute_name]..._predicate`
|
94
|
+
|
95
|
+
where `[_or_another_attribute_name]...` means any repetition of `_or_` plus the name of the attribute.
|
96
|
+
|
97
|
+
`cont` (contains) and `start` (starts with) are just two of the available
|
98
|
+
search predicates.
|
99
|
+
|
100
|
+
The `search_form_for` answer format can be set like this:
|
101
|
+
|
102
|
+
```erb
|
103
|
+
<%= search_form_for(@q, format: :pdf) do |f| %>
|
104
|
+
|
105
|
+
<%= search_form_for(@q, format: :json) do |f| %>
|
106
|
+
```
|
107
|
+
|
108
|
+
### Search link helper
|
109
|
+
|
110
|
+
Ransack's `sort_link` helper creates table headers that are sortable links
|
111
|
+
|
112
|
+
```erb
|
113
|
+
<%= sort_link(@q, :name) %>
|
114
|
+
```
|
115
|
+
Additional options can be passed after the column parameter, like a different
|
116
|
+
column title or a default sort order.
|
117
|
+
|
118
|
+
If the first option after the column parameter is a String, it's considered a
|
119
|
+
custom label for the link:
|
120
|
+
|
121
|
+
```erb
|
122
|
+
<%= sort_link(@q, :name, 'Last Name', default_order: :desc) %>
|
123
|
+
```
|
124
|
+
|
125
|
+
You can use a block if the link markup is hard to fit into the label parameter:
|
126
|
+
|
127
|
+
```erb
|
128
|
+
<%= sort_link(@q, :name) do %>
|
129
|
+
<strong>Player Name</strong>
|
130
|
+
<% end %>
|
131
|
+
```
|
132
|
+
|
133
|
+
With a polymorphic association, you may need to specify the name of the link
|
134
|
+
explicitly to avoid an `uninitialized constant Model::Xxxable` error (see issue
|
135
|
+
[#421](https://github.com/activerecord-hackery/ransack/issues/421)):
|
136
|
+
|
137
|
+
```erb
|
138
|
+
<%= sort_link(@q, :xxxable_of_Ymodel_type_some_attribute, 'Attribute Name') %>
|
139
|
+
```
|
140
|
+
|
141
|
+
If the first option after the column parameter and/or the label parameter is an
|
142
|
+
Array, it will be used for sorting on multiple fields:
|
143
|
+
|
144
|
+
```erb
|
145
|
+
<%= sort_link(@q, :last_name, [:last_name, 'first_name asc'], 'Last Name') %>
|
146
|
+
```
|
147
|
+
|
148
|
+
In the example above, clicking the link will sort by `last_name` and then
|
149
|
+
`first_name`. Specifying the sort direction on a field in the array tells
|
150
|
+
Ransack to _always_ sort that particular field in the specified direction.
|
151
|
+
|
152
|
+
Multiple `default_order` fields may also be specified with a trailing options
|
153
|
+
Hash:
|
154
|
+
|
155
|
+
```erb
|
156
|
+
<%= sort_link(@q, :last_name, %i(last_name first_name),
|
157
|
+
default_order: { last_name: 'asc', first_name: 'desc' }) %>
|
158
|
+
```
|
159
|
+
|
160
|
+
This example toggles the sort directions of both fields, by default
|
161
|
+
initially sorting the `last_name` field by ascending order, and the
|
162
|
+
`first_name` field by descending order.
|
163
|
+
|
164
|
+
In the case that you wish to sort by some complex value, such as the result
|
165
|
+
of a SQL function, you may do so using scopes. In your model, define scopes
|
166
|
+
whose names line up with the name of the virtual field you wish to sort by,
|
167
|
+
as so:
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
class Person < ActiveRecord::Base
|
171
|
+
scope :sort_by_reverse_name_asc, lambda { order("REVERSE(name) ASC") }
|
172
|
+
scope :sort_by_reverse_name_desc, lambda { order("REVERSE(name) DESC") }
|
173
|
+
...
|
174
|
+
```
|
175
|
+
|
176
|
+
and you can then sort by this virtual field:
|
177
|
+
|
178
|
+
```erb
|
179
|
+
<%= sort_link(@q, :reverse_name) %>
|
180
|
+
```
|
181
|
+
|
182
|
+
The trailing options Hash can also be used for passing additional options to the
|
183
|
+
generated link, like `class:`.
|
184
|
+
|
185
|
+
The sort link order indicator arrows may be globally customized by setting a
|
186
|
+
`custom_arrows` option in an initializer file like
|
187
|
+
`config/initializers/ransack.rb`.
|
188
|
+
|
189
|
+
You can also enable a `default_arrow` which is displayed on all sortable fields
|
190
|
+
which are not currently used in the sorting. This is disabled by default so
|
191
|
+
nothing will be displayed:
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
Ransack.configure do |c|
|
195
|
+
c.custom_arrows = {
|
196
|
+
up_arrow: '<i class="custom-up-arrow-icon"></i>',
|
197
|
+
down_arrow: 'U+02193',
|
198
|
+
default_arrow: '<i class="default-arrow-icon"></i>'
|
199
|
+
}
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
All sort links may be displayed without the order indicator
|
204
|
+
arrows by setting `hide_sort_order_indicators` to true in the initializer file.
|
205
|
+
Note that this hides the arrows even if they were customized:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
Ransack.configure do |c|
|
209
|
+
c.hide_sort_order_indicators = true
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
Without setting it globally, individual sort links may be displayed without
|
214
|
+
the order indicator arrow by passing `hide_indicator: true` in the sort link:
|
215
|
+
|
216
|
+
```erb
|
217
|
+
<%= sort_link(@q, :name, hide_indicator: true) %>
|
218
|
+
```
|
219
|
+
|
220
|
+
### sort_url
|
221
|
+
|
222
|
+
Ransack's `sort_url` helper is like a `sort_link` but returns only the url
|
223
|
+
|
224
|
+
`sort_url` has the same API as `sort_link`:
|
225
|
+
|
226
|
+
```erb
|
227
|
+
<%= sort_url(@q, :name, default_order: :desc) %>
|
228
|
+
```
|
229
|
+
|
230
|
+
```erb
|
231
|
+
<%= sort_url(@q, :last_name, [:last_name, 'first_name asc']) %>
|
232
|
+
```
|
233
|
+
|
234
|
+
```erb
|
235
|
+
<%= sort_url(@q, :last_name, %i(last_name first_name),
|
236
|
+
default_order: { last_name: 'asc', first_name: 'desc' }) %>
|
237
|
+
```
|
238
|
+
|
239
|
+
### PostgreSQL's sort option
|
240
|
+
|
241
|
+
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.
|
242
|
+
|
243
|
+
You may want to configure it like this:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
Ransack.configure do |c|
|
247
|
+
c.postgres_fields_sort_option = :nulls_first # or :nulls_last
|
248
|
+
end
|
249
|
+
```
|
250
|
+
|
251
|
+
To treat nulls as having the lowest or highest value respectively. To force nulls to always be first or last, use
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
Ransack.configure do |c|
|
255
|
+
c.postgres_fields_sort_option = :nulls_always_first # or :nulls_always_last
|
256
|
+
end
|
257
|
+
```
|
258
|
+
|
259
|
+
See this feature: https://www.postgresql.org/docs/13/queries-order.html
|
260
|
+
|
261
|
+
#### Case Insensitive Sorting in PostgreSQL
|
262
|
+
|
263
|
+
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:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
module RansackObject
|
267
|
+
|
268
|
+
def self.included(base)
|
269
|
+
base.columns.each do |column|
|
270
|
+
if column.type == :string
|
271
|
+
base.ransacker column.name.to_sym, type: :string do
|
272
|
+
Arel.sql("lower(#{base.table_name}.#{column.name})")
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
```
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
class UserWithManyAttributes < ActiveRecord::Base
|
282
|
+
include RansackObject
|
283
|
+
end
|
284
|
+
```
|
285
|
+
|
286
|
+
If this approach is taken, it is advisable to [add a functional index](https://www.postgresql.org/docs/13/citext.html).
|
287
|
+
|
288
|
+
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).
|