Merge branch 'master' into allow-scopes-with-string-joins
This commit is contained in:
commit
1c69b9ddbf
|
@ -11,8 +11,8 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby:
|
ruby:
|
||||||
- 3.0.0
|
- 3.0.2
|
||||||
- 2.7.2
|
- 2.7.4
|
||||||
env:
|
env:
|
||||||
DB: sqlite3
|
DB: sqlite3
|
||||||
RAILS: main
|
RAILS: main
|
||||||
|
@ -33,8 +33,8 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby:
|
ruby:
|
||||||
- 3.0.0
|
- 3.0.2
|
||||||
- 2.7.2
|
- 2.7.4
|
||||||
env:
|
env:
|
||||||
DB: mysql
|
DB: mysql
|
||||||
RAILS: main
|
RAILS: main
|
||||||
|
@ -64,8 +64,8 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby:
|
ruby:
|
||||||
- 3.0.0
|
- 3.0.2
|
||||||
- 2.7.2
|
- 2.7.4
|
||||||
env:
|
env:
|
||||||
DB: postgres
|
DB: postgres
|
||||||
RAILS: main
|
RAILS: main
|
||||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
- name: Set up Ruby
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 3.0.0
|
ruby-version: 3.0.1
|
||||||
- name: Install gems
|
- name: Install gems
|
||||||
run: bundle install --jobs 4 --retry 3
|
run: bundle install --jobs 4 --retry 3
|
||||||
- name: Run RuboCop
|
- name: Run RuboCop
|
||||||
|
|
|
@ -11,20 +11,17 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
rails:
|
rails:
|
||||||
- v6.1.3
|
- v7.0.0
|
||||||
- v6.0.3
|
- v6.1.4
|
||||||
|
- v6.0.4
|
||||||
- 6-0-stable
|
- 6-0-stable
|
||||||
- 5-2-stable
|
|
||||||
- v5.2.4
|
|
||||||
ruby:
|
ruby:
|
||||||
- 3.0.0
|
- 3.0.2
|
||||||
- 2.7.2
|
- 2.7.4
|
||||||
- 2.6.6
|
- 2.6.7
|
||||||
exclude:
|
exclude:
|
||||||
- rails: v5.2.4
|
- rails: v7.0.0
|
||||||
ruby: 3.0.0
|
ruby: 2.6.7
|
||||||
- rails: 5-2-stable
|
|
||||||
ruby: 3.0.0
|
|
||||||
env:
|
env:
|
||||||
DB: sqlite3
|
DB: sqlite3
|
||||||
RAILS: ${{ matrix.rails }}
|
RAILS: ${{ matrix.rails }}
|
||||||
|
@ -45,20 +42,17 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
rails:
|
rails:
|
||||||
- v6.1.3
|
- v7.0.0
|
||||||
- v6.0.3
|
- v6.1.4
|
||||||
|
- v6.0.4
|
||||||
- 6-0-stable
|
- 6-0-stable
|
||||||
- 5-2-stable
|
|
||||||
- v5.2.4
|
|
||||||
ruby:
|
ruby:
|
||||||
- 3.0.0
|
- 3.0.2
|
||||||
- 2.7.2
|
- 2.7.4
|
||||||
- 2.6.6
|
- 2.6.7
|
||||||
exclude:
|
exclude:
|
||||||
- rails: v5.2.4
|
- rails: v7.0.0
|
||||||
ruby: 3.0.0
|
ruby: 2.6.7
|
||||||
- rails: 5-2-stable
|
|
||||||
ruby: 3.0.0
|
|
||||||
env:
|
env:
|
||||||
DB: mysql
|
DB: mysql
|
||||||
RAILS: ${{ matrix.rails }}
|
RAILS: ${{ matrix.rails }}
|
||||||
|
@ -88,20 +82,17 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
rails:
|
rails:
|
||||||
- v6.1.3
|
- v7.0.0
|
||||||
- v6.0.3
|
- v6.1.4
|
||||||
|
- v6.0.4
|
||||||
- 6-0-stable
|
- 6-0-stable
|
||||||
- 5-2-stable
|
|
||||||
- v5.2.4
|
|
||||||
ruby:
|
ruby:
|
||||||
- 3.0.0
|
- 3.0.2
|
||||||
- 2.7.2
|
- 2.7.4
|
||||||
- 2.6.6
|
- 2.6.7
|
||||||
exclude:
|
exclude:
|
||||||
- rails: v5.2.4
|
- rails: v7.0.0
|
||||||
ruby: 3.0.0
|
ruby: 2.6.7
|
||||||
- rails: 5-2-stable
|
|
||||||
ruby: 3.0.0
|
|
||||||
env:
|
env:
|
||||||
DB: postgres
|
DB: postgres
|
||||||
RAILS: ${{ matrix.rails }}
|
RAILS: ${{ matrix.rails }}
|
||||||
|
@ -144,7 +135,7 @@ jobs:
|
||||||
- name: Set up Ruby
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 3.0.0
|
ruby-version: 3.0.2
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bundle install
|
run: bundle install
|
||||||
- name: Run bug report templates
|
- name: Run bug report templates
|
||||||
|
|
85
CHANGELOG.md
85
CHANGELOG.md
|
@ -1,22 +1,83 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
* Drop support for rubies under 2.5. PR #1189
|
## Unreleased
|
||||||
|
|
||||||
|
* Improve `sort_link` documentation.
|
||||||
|
|
||||||
|
* Deprecate passing two trailing hashes to `sort_link`, for example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
sort_link(@q, :bussiness_name, "bussines_name", {}, class: "foo")
|
||||||
|
```
|
||||||
|
|
||||||
|
Pass a single hash with all options instead.
|
||||||
|
|
||||||
|
* Fix `:class` option to `sort_link` not being passed to the generated link
|
||||||
|
correctly when no additional options are passed. For example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
sort_link(@q, :bussiness_name, class: "foo")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.6.0 - 2022-03-08
|
||||||
|
|
||||||
|
* Fix regression when joining a table with itself.
|
||||||
|
PR [1275](https://github.com/activerecord-hackery/ransack/pull/1276)
|
||||||
|
|
||||||
|
* Drop support for ActiveRecord older than 6.0.4.
|
||||||
|
PR [1276](https://github.com/activerecord-hackery/ransack/pull/1276)
|
||||||
|
|
||||||
|
## 2.5.0 - 2021-12-26
|
||||||
|
|
||||||
|
* Document release process by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1199, https://github.com/activerecord-hackery/ransack/pull/1200.
|
||||||
|
* Support Rails 7 by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1205, https://github.com/activerecord-hackery/ransack/pull/1209, https://github.com/activerecord-hackery/ransack/pull/1234, https://github.com/activerecord-hackery/ransack/pull/1230, https://github.com/activerecord-hackery/ransack/pull/1266
|
||||||
|
* Fix for `ActiveRecord::UnknownAttributeReference` in ransack by @TechnologyHypofriend in https://github.com/activerecord-hackery/ransack/pull/1207
|
||||||
|
* Make gem compatible with old polyamorous require by @rtweeks in https://github.com/activerecord-hackery/ransack/pull/1145
|
||||||
|
* Adding swedish translations by @johanandre in https://github.com/activerecord-hackery/ransack/pull/1208
|
||||||
|
* Document how to do case insensitive searches by @scarroll32 in https://github.com/activerecord-hackery/ransack/pull/1213
|
||||||
|
* Add the ability to disable whitespace stripping for string searches by @DCrow in https://github.com/activerecord-hackery/ransack/pull/1214
|
||||||
|
* Fix `:default` option in `Translate.attribute` method by @coreyaus in https://github.com/activerecord-hackery/ransack/pull/1218
|
||||||
|
* Fix typo in README.md by @d-m-u in https://github.com/activerecord-hackery/ransack/pull/1220
|
||||||
|
* Fix another typo in README.md by @plan-do-break-fix in https://github.com/activerecord-hackery/ransack/pull/1221
|
||||||
|
* Fix several documentation typos @wonda-tea-coffee in https://github.com/activerecord-hackery/ransack/pull/1233
|
||||||
|
* Allow ransack to treat nulls as always first or last by @mollerhoj in https://github.com/activerecord-hackery/ransack/pull/1226
|
||||||
|
* Consider ransack aliases when sorting by @faragorn and @waldyr in https://github.com/activerecord-hackery/ransack/pull/1223
|
||||||
|
* Fix non-casted array predicates by @danielpclark in https://github.com/activerecord-hackery/ransack/pull/1246
|
||||||
|
* Remove Squeel references from README by @Schwad in https://github.com/activerecord-hackery/ransack/pull/1249
|
||||||
|
* Remove part of the README that might lead to incorrect results by @RadekMolenda in https://github.com/activerecord-hackery/ransack/pull/1258
|
||||||
|
* ActiveRecord 7.0 support
|
||||||
|
|
||||||
|
## 2.4.2 - 2021-01-23
|
||||||
|
|
||||||
|
* Enable RuboCop and configure GitHub Actions to run RuboCop by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1185
|
||||||
|
* Add Ruby 3.0.0 support by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1190
|
||||||
|
* Drop Ruby 2.5 or older versions of Ruby by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1189
|
||||||
|
* Move bug report templates into ransack repository and run templates at CI by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1191
|
||||||
|
* Allow Ransack to be tested with Rails main branch by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1192
|
||||||
|
|
||||||
## 2.4.1 - 2020-12-21
|
## 2.4.1 - 2020-12-21
|
||||||
|
|
||||||
* Add `ActiveRecord::Base.ransack!` which raises error if passed unknown condition
|
* Links to Tidelift subscription by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1178
|
||||||
|
* Enable GitHub Actions by @yahonda in https://github.com/activerecord-hackery/ransack/pull/1180
|
||||||
*Aaron Lipman*
|
* Move security contact information to SECURITY.md by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1179
|
||||||
|
* Add `ActiveRecord::Base.ransack!` which raises error if passed unknown condition by @alipman88 in https://github.com/activerecord-hackery/ransack/pull/1132
|
||||||
|
* Add ability to config PostgreSQL ORDER BY ... NULLS FIRST or NULLS LAST by @itsalongstory in https://github.com/activerecord-hackery/ransack/pull/1184
|
||||||
|
|
||||||
## 2.4.0 - 2020-11-27
|
## 2.4.0 - 2020-11-27
|
||||||
|
|
||||||
* Support ActiveRecord 6.1.0.rc1.
|
* Specify actual version of polyamorous, so we can release that separately by @gregmolnar in https://github.com/activerecord-hackery/ransack/pull/1101
|
||||||
PR [1172](https://github.com/activerecord-hackery/ransack/pull/1172)
|
* Only include necessary files in gem package by @tvdeyen in https://github.com/activerecord-hackery/ransack/pull/1104
|
||||||
|
* Test/Fix for subquery in Rails 5.2.4 by @stevenjonescgm in https://github.com/activerecord-hackery/ransack/pull/1112
|
||||||
* Fix for ActiveRecord 5.2.4 (note security fix in 5.2.4.2 for ActiveView's escape_javascript CVE-2020-5267 for all earlier versions)
|
* Polyamorous module by @varyonic in https://github.com/activerecord-hackery/ransack/pull/1113
|
||||||
|
* Remove duplicated rows by @sasharevzin in https://github.com/activerecord-hackery/ransack/pull/1116
|
||||||
* Drop support for ActiveRecord older than 5.2.4.
|
* Fix Ruby 2.7 deprecation warnings by @terracatta in https://github.com/activerecord-hackery/ransack/pull/1121
|
||||||
PR [1166](https://github.com/activerecord-hackery/ransack/pull/1166)
|
* Fixes polymorphic joins. by @PhilCoggins in https://github.com/activerecord-hackery/ransack/pull/1122
|
||||||
|
* Drop support for activerecord older than 5.2.4 by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1166
|
||||||
|
* Adapt to quoting change in Rails by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1165
|
||||||
|
* Typo in docs by @brett-anderson in https://github.com/activerecord-hackery/ransack/pull/1155
|
||||||
|
* Add Rails 6.1 support by @deivid-rodriguez in https://github.com/activerecord-hackery/ransack/pull/1172
|
||||||
|
* Strip Leading & Trailing Whitespace Before Searching by @itsalongstory in https://github.com/activerecord-hackery/ransack/pull/1126
|
||||||
|
* Use unfrozen version of symbol to string by @fauno in https://github.com/activerecord-hackery/ransack/pull/1149
|
||||||
|
|
||||||
## 2.3.2 - 2020-01-11
|
## 2.3.2 - 2020-01-11
|
||||||
|
|
||||||
|
@ -200,7 +261,7 @@
|
||||||
ignored when block parameter is specified.
|
ignored when block parameter is specified.
|
||||||
PR [#818](https://github.com/activerecord-hackery/ransack/pull/818).
|
PR [#818](https://github.com/activerecord-hackery/ransack/pull/818).
|
||||||
|
|
||||||
* No need pass some arugments to JoinAssociation#join_constraints in Rails 5.1.
|
* No need pass some arguments to JoinAssociation#join_constraints in Rails 5.1.
|
||||||
PR [#814](https://github.com/activerecord-hackery/ransack/pull/814).
|
PR [#814](https://github.com/activerecord-hackery/ransack/pull/814).
|
||||||
Fixes [#807](https://github.com/activerecord-hackery/ransack/issues/807).
|
Fixes [#807](https://github.com/activerecord-hackery/ransack/issues/807).
|
||||||
Reference [rails/rails#28267](https://github.com/rails/rails/pull/28267)
|
Reference [rails/rails#28267](https://github.com/rails/rails/pull/28267)
|
||||||
|
|
|
@ -64,9 +64,7 @@ Here's a quick guide:
|
||||||
2. Create a thoughtfully-named branch for your changes (`git checkout -b my-new-feature`).
|
2. Create a thoughtfully-named branch for your changes (`git checkout -b my-new-feature`).
|
||||||
|
|
||||||
3. Install the development dependencies by running `bundle install`.
|
3. Install the development dependencies by running `bundle install`.
|
||||||
To install rails other than latest (set in Gemfile): `RAILS='5-2-stable' bundle install`
|
To install rails other than latest (set in Gemfile): `RAILS='6-0-stable' bundle install`
|
||||||
|
|
||||||
$ RAILS='5-2-stable' bundle install
|
|
||||||
|
|
||||||
4. Begin by running the tests. We only take pull requests with passing tests,
|
4. Begin by running the tests. We only take pull requests with passing tests,
|
||||||
and it's great to know that you have a clean slate:
|
and it's great to know that you have a clean slate:
|
||||||
|
|
89
README.md
89
README.md
|
@ -11,25 +11,22 @@ Ransack enables the creation of both
|
||||||
for your Ruby on Rails application
|
for your Ruby on Rails application
|
||||||
([demo source code here](https://github.com/activerecord-hackery/ransack_demo)).
|
([demo source code here](https://github.com/activerecord-hackery/ransack_demo)).
|
||||||
If you're looking for something that simplifies query generation at the model
|
If you're looking for something that simplifies query generation at the model
|
||||||
or controller layer, you're probably not looking for Ransack (or MetaSearch,
|
or controller layer, you're probably not looking for Ransack.
|
||||||
for that matter). Try [Squeel](https://github.com/activerecord-hackery/squeel)
|
|
||||||
instead.
|
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
Ransack is supported for Rails 6.1, 6.0, 5.2 on Ruby 2.6.6 and later.
|
Ransack is supported for Rails 7.0, 6.x on Ruby 2.6.6 and later.
|
||||||
|
|
||||||
In your Gemfile, for the last officially released gem:
|
To install `ransack` and add it to your Gemfile, run
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
gem 'ransack'
|
bundle add ransack
|
||||||
```
|
```
|
||||||
|
|
||||||
If you would like to use the latest updates (recommended), use the `master`
|
If you would like to use the latest updates, use the `master` branch:
|
||||||
branch:
|
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
gem 'ransack', github: 'activerecord-hackery/ransack'
|
bundle add ransack --github "activerecord-hackery/ransack"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Issues tracker
|
## Issues tracker
|
||||||
|
@ -65,9 +62,6 @@ this example, with preloading each Person's Articles and pagination):
|
||||||
def index
|
def index
|
||||||
@q = Person.ransack(params[:q])
|
@q = Person.ransack(params[:q])
|
||||||
@people = @q.result.includes(:articles).page(params[:page])
|
@people = @q.result.includes(:articles).page(params[:page])
|
||||||
|
|
||||||
# or use `to_a.uniq` to remove duplicates (can also be done in the view):
|
|
||||||
@people = @q.result.includes(:articles).page(params[:page]).to_a.uniq
|
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -152,8 +146,11 @@ The `search_form_for` answer format can be set like this:
|
||||||
```erb
|
```erb
|
||||||
<%= sort_link(@q, :name) %>
|
<%= sort_link(@q, :name) %>
|
||||||
```
|
```
|
||||||
Additional options can be passed after the column attribute, like a different
|
Additional options can be passed after the column parameter, like a different
|
||||||
column title or a default sort order:
|
column title or a default sort order.
|
||||||
|
|
||||||
|
If the first option after the column parameter is a String, it's considered a
|
||||||
|
custom label for the link:
|
||||||
|
|
||||||
```erb
|
```erb
|
||||||
<%= sort_link(@q, :name, 'Last Name', default_order: :desc) %>
|
<%= sort_link(@q, :name, 'Last Name', default_order: :desc) %>
|
||||||
|
@ -175,7 +172,8 @@ explicitly to avoid an `uninitialized constant Model::Xxxable` error (see issue
|
||||||
<%= sort_link(@q, :xxxable_of_Ymodel_type_some_attribute, 'Attribute Name') %>
|
<%= sort_link(@q, :xxxable_of_Ymodel_type_some_attribute, 'Attribute Name') %>
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also sort on multiple fields by specifying an ordered array:
|
If the first option after the column parameter and/or the label parameter is an
|
||||||
|
Array, it will be used for sorting on multiple fields:
|
||||||
|
|
||||||
```erb
|
```erb
|
||||||
<%= sort_link(@q, :last_name, [:last_name, 'first_name asc'], 'Last Name') %>
|
<%= sort_link(@q, :last_name, [:last_name, 'first_name asc'], 'Last Name') %>
|
||||||
|
@ -185,7 +183,8 @@ In the example above, clicking the link will sort by `last_name` and then
|
||||||
`first_name`. Specifying the sort direction on a field in the array tells
|
`first_name`. Specifying the sort direction on a field in the array tells
|
||||||
Ransack to _always_ sort that particular field in the specified direction.
|
Ransack to _always_ sort that particular field in the specified direction.
|
||||||
|
|
||||||
Multiple `default_order` fields may also be specified with a hash:
|
Multiple `default_order` fields may also be specified with a trailing options
|
||||||
|
Hash:
|
||||||
|
|
||||||
```erb
|
```erb
|
||||||
<%= sort_link(@q, :last_name, %i(last_name first_name),
|
<%= sort_link(@q, :last_name, %i(last_name first_name),
|
||||||
|
@ -214,6 +213,9 @@ and you can then sort by this virtual field:
|
||||||
<%= sort_link(@q, :reverse_name) %>
|
<%= sort_link(@q, :reverse_name) %>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The trailing options Hash can also be used for passing additional options to the
|
||||||
|
generated link, like `class:`.
|
||||||
|
|
||||||
The sort link order indicator arrows may be globally customized by setting a
|
The sort link order indicator arrows may be globally customized by setting a
|
||||||
`custom_arrows` option in an initializer file like
|
`custom_arrows` option in an initializer file like
|
||||||
`config/initializers/ransack.rb`.
|
`config/initializers/ransack.rb`.
|
||||||
|
@ -278,11 +280,19 @@ Ransack.configure do |c|
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To treat nulls as having the lowest or highest value respectively. To force nulls to always be first or last, use
|
||||||
|
|
||||||
|
```rb
|
||||||
|
Ransack.configure do |c|
|
||||||
|
c.postgres_fields_sort_option = :nulls_always_first # or :nulls_always_last
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
See this feature: https://www.postgresql.org/docs/13/queries-order.html
|
See this feature: https://www.postgresql.org/docs/13/queries-order.html
|
||||||
|
|
||||||
#### Case Insensitive Sorting in PostgreSQL
|
#### Case Insensitive Sorting in PostgreSQL
|
||||||
|
|
||||||
In order to request PostgresSQL to do a case insensitive sort for all string columns of a model at once, Ransack can be extended by using this approach:
|
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:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
module RansackObject
|
module RansackObject
|
||||||
|
@ -349,32 +359,6 @@ construct much more complex search forms, such as the one on the
|
||||||
[demo app](http://ransack-demo.herokuapp.com/users/advanced_search)
|
[demo app](http://ransack-demo.herokuapp.com/users/advanced_search)
|
||||||
(source code [here](https://github.com/activerecord-hackery/ransack_demo)).
|
(source code [here](https://github.com/activerecord-hackery/ransack_demo)).
|
||||||
|
|
||||||
### Ransack #search method
|
|
||||||
|
|
||||||
Ransack will try to make the class method `#search` available in your
|
|
||||||
models, but if `#search` has already been defined elsewhere, you can always use
|
|
||||||
the default `#ransack` class method. So the following are equivalent:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Article.ransack(params[:q])
|
|
||||||
Article.search(params[:q])
|
|
||||||
```
|
|
||||||
|
|
||||||
Users have reported issues of `#search` name conflicts with other gems, so
|
|
||||||
the `#search` method alias will be deprecated in the next major version of
|
|
||||||
Ransack (2.0). It's advisable to use the default `#ransack` instead.
|
|
||||||
|
|
||||||
For now, if Ransack's `#search` method conflicts with the name of another
|
|
||||||
method named `search` in your code or another gem, you may resolve it either by
|
|
||||||
patching the `extended` class_method in `Ransack::Adapters::ActiveRecord::Base`
|
|
||||||
to remove the line `alias :search :ransack unless base.respond_to? :search`, or
|
|
||||||
by placing the following line in your Ransack initializer file at
|
|
||||||
`config/initializers/ransack.rb`:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Ransack::Adapters::ActiveRecord::Base.class_eval('remove_method :search')
|
|
||||||
```
|
|
||||||
|
|
||||||
### Associations
|
### Associations
|
||||||
|
|
||||||
You can easily use Ransack to search for objects in `has_many` and `belongs_to`
|
You can easily use Ransack to search for objects in `has_many` and `belongs_to`
|
||||||
|
@ -467,6 +451,25 @@ query parameters in your URLs.
|
||||||
<% end %>
|
<% end %>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also use `ransack_alias` for sorting.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class Post < ActiveRecord::Base
|
||||||
|
belongs_to :author
|
||||||
|
|
||||||
|
# Abbreviate :author_first_name to :author
|
||||||
|
ransack_alias :author, :author_first_name
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, you can use `:author` instead of `:author_first_name` in a `sort_link`.
|
||||||
|
|
||||||
|
```erb
|
||||||
|
<%= sort_link(@q, :author) %>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that using `:author_first_name_or_author_last_name_cont` would produce an invalid sql query. In those cases, Ransack ignores the sorting clause.
|
||||||
|
|
||||||
### Search Matchers
|
### Search Matchers
|
||||||
|
|
||||||
List of all possible predicates
|
List of all possible predicates
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
module Polyamorous
|
|
||||||
module JoinAssociationExtensions
|
|
||||||
include SwappingReflectionClass
|
|
||||||
def self.prepended(base)
|
|
||||||
base.class_eval { attr_reader :join_type }
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(reflection, children, polymorphic_class = nil, join_type = Arel::Nodes::InnerJoin)
|
|
||||||
@join_type = join_type
|
|
||||||
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
|
|
||||||
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
|
|
||||||
super(reflection, children)
|
|
||||||
self.reflection.options[:polymorphic] = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
super(reflection, children)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(other)
|
|
||||||
base_klass == other.base_klass
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,79 +0,0 @@
|
||||||
module Polyamorous
|
|
||||||
module JoinDependencyExtensions
|
|
||||||
# Replaces ActiveRecord::Associations::JoinDependency#build
|
|
||||||
def build(associations, base_klass)
|
|
||||||
associations.map do |name, right|
|
|
||||||
if name.is_a? Join
|
|
||||||
reflection = find_reflection base_klass, name.name
|
|
||||||
reflection.check_validity!
|
|
||||||
reflection.check_eager_loadable!
|
|
||||||
|
|
||||||
klass = if reflection.polymorphic?
|
|
||||||
name.klass || base_klass
|
|
||||||
else
|
|
||||||
reflection.klass
|
|
||||||
end
|
|
||||||
JoinAssociation.new(reflection, build(right, klass), name.klass, name.type)
|
|
||||||
else
|
|
||||||
reflection = find_reflection base_klass, name
|
|
||||||
reflection.check_validity!
|
|
||||||
reflection.check_eager_loadable!
|
|
||||||
|
|
||||||
if reflection.polymorphic?
|
|
||||||
raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
|
|
||||||
end
|
|
||||||
JoinAssociation.new(reflection, build(right, reflection.klass))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def join_constraints(joins_to_add, join_type, alias_tracker)
|
|
||||||
@alias_tracker = alias_tracker
|
|
||||||
|
|
||||||
construct_tables!(join_root)
|
|
||||||
joins = make_join_constraints(join_root, join_type)
|
|
||||||
|
|
||||||
joins.concat joins_to_add.flat_map { |oj|
|
|
||||||
construct_tables!(oj.join_root)
|
|
||||||
if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
|
|
||||||
walk join_root, oj.join_root
|
|
||||||
else
|
|
||||||
make_join_constraints(oj.join_root, join_type)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
|
|
||||||
foreign_table = parent.table
|
|
||||||
foreign_klass = parent.base_klass
|
|
||||||
join_type = child.join_type || join_type if join_type == Arel::Nodes::InnerJoin
|
|
||||||
joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
|
|
||||||
joins.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
|
|
||||||
end
|
|
||||||
|
|
||||||
module ClassMethods
|
|
||||||
# Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
|
|
||||||
#
|
|
||||||
def walk_tree(associations, hash)
|
|
||||||
case associations
|
|
||||||
when TreeNode
|
|
||||||
associations.add_to_tree(hash)
|
|
||||||
when Hash
|
|
||||||
associations.each do |k, v|
|
|
||||||
cache =
|
|
||||||
if TreeNode === k
|
|
||||||
k.add_to_tree(hash)
|
|
||||||
else
|
|
||||||
hash[k] ||= {}
|
|
||||||
end
|
|
||||||
walk_tree(v, cache)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
super(associations, hash)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
module Polyamorous
|
|
||||||
module ReflectionExtensions
|
|
||||||
def join_scope(table, foreign_table, foreign_klass)
|
|
||||||
if respond_to?(:polymorphic?) && polymorphic?
|
|
||||||
super.where!(foreign_table[foreign_type].eq(klass.name))
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1 +1,20 @@
|
||||||
require 'polyamorous/activerecord_5.2_ruby_2/join_association'
|
module Polyamorous
|
||||||
|
module JoinAssociationExtensions
|
||||||
|
include SwappingReflectionClass
|
||||||
|
def self.prepended(base)
|
||||||
|
base.class_eval { attr_reader :join_type }
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(reflection, children, polymorphic_class = nil, join_type = Arel::Nodes::InnerJoin)
|
||||||
|
@join_type = join_type
|
||||||
|
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
|
||||||
|
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
|
||||||
|
super(reflection, children)
|
||||||
|
self.reflection.options[:polymorphic] = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
super(reflection, children)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# active_record_6.0_ruby_2/join_dependency.rb
|
|
||||||
module Polyamorous
|
module Polyamorous
|
||||||
module JoinDependencyExtensions
|
module JoinDependencyExtensions
|
||||||
# Replaces ActiveRecord::Associations::JoinDependency#build
|
# Replaces ActiveRecord::Associations::JoinDependency#build
|
||||||
|
|
|
@ -1 +1,11 @@
|
||||||
require 'polyamorous/activerecord_5.2_ruby_2/reflection'
|
module Polyamorous
|
||||||
|
module ReflectionExtensions
|
||||||
|
def join_scope(table, foreign_table, foreign_klass)
|
||||||
|
if respond_to?(:polymorphic?) && polymorphic?
|
||||||
|
super.where!(foreign_table[foreign_type].eq(klass.name))
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -66,9 +66,5 @@ module Polyamorous
|
||||||
|
|
||||||
joins
|
joins
|
||||||
end
|
end
|
||||||
|
|
||||||
def ==(other)
|
|
||||||
base_klass == other.base_klass
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# active_record_6.1_ruby_2/join_dependency.rb
|
|
||||||
module Polyamorous
|
module Polyamorous
|
||||||
module JoinDependencyExtensions
|
module JoinDependencyExtensions
|
||||||
# Replaces ActiveRecord::Associations::JoinDependency#build
|
# Replaces ActiveRecord::Associations::JoinDependency#build
|
||||||
|
|
|
@ -4,7 +4,6 @@ module Ransack
|
||||||
module Base
|
module Base
|
||||||
|
|
||||||
def self.extended(base)
|
def self.extended(base)
|
||||||
alias :search :ransack unless base.respond_to? :search
|
|
||||||
base.class_eval do
|
base.class_eval do
|
||||||
class_attribute :_ransackers
|
class_attribute :_ransackers
|
||||||
class_attribute :_ransack_aliases
|
class_attribute :_ransack_aliases
|
||||||
|
@ -14,7 +13,6 @@ module Ransack
|
||||||
end
|
end
|
||||||
|
|
||||||
def ransack(params = {}, options = {})
|
def ransack(params = {}, options = {})
|
||||||
ActiveSupport::Deprecation.warn("#search is deprecated and will be removed in 2.3, please use #ransack instead") if __callee__ == :search
|
|
||||||
Search.new(self, params, options)
|
Search.new(self, params, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,7 +68,7 @@ module Ransack
|
||||||
end
|
end
|
||||||
|
|
||||||
# ransack_scope_skip_sanitize_args, by default, returns an empty array.
|
# ransack_scope_skip_sanitize_args, by default, returns an empty array.
|
||||||
# i.e. use the sanitize_scope_args setting to determin if args should be converted.
|
# i.e. use the sanitize_scope_args setting to determine if args should be converted.
|
||||||
# For overriding with a list of scopes which should be passed the args as-is.
|
# For overriding with a list of scopes which should be passed the args as-is.
|
||||||
#
|
#
|
||||||
def ransackable_scopes_skip_sanitize_args
|
def ransackable_scopes_skip_sanitize_args
|
||||||
|
|
|
@ -47,6 +47,10 @@ module Ransack
|
||||||
scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") : Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
|
scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") : Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
|
||||||
when :nulls_last
|
when :nulls_last
|
||||||
scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") : Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
|
scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") : Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
|
||||||
|
when :nulls_always_first
|
||||||
|
scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
|
||||||
|
when :nulls_always_last
|
||||||
|
scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
|
||||||
end
|
end
|
||||||
|
|
||||||
relation = relation.order(scope_or_sort)
|
relation = relation.order(scope_or_sort)
|
||||||
|
|
|
@ -47,18 +47,19 @@ module Ransack
|
||||||
end
|
end
|
||||||
|
|
||||||
def casted_array?(predicate)
|
def casted_array?(predicate)
|
||||||
(predicate.respond_to?(:value) && predicate.value.is_a?(Array)) || # Rails 6.1
|
value_from(predicate).is_a?(Array) && predicate.is_a?(Arel::Nodes::Casted)
|
||||||
(predicate.respond_to?(:val) && predicate.val.is_a?(Array)) # Rails 5.2, 6.0
|
end
|
||||||
|
|
||||||
|
def value_from(predicate)
|
||||||
|
if predicate.respond_to?(:value)
|
||||||
|
predicate.value # Rails 6.1
|
||||||
|
elsif predicate.respond_to?(:val)
|
||||||
|
predicate.val # Rails 6.0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_values_for(predicate)
|
def format_values_for(predicate)
|
||||||
value = if predicate.respond_to?(:value)
|
value_from(predicate).map do |val|
|
||||||
predicate.value # Rails 6.1
|
|
||||||
else
|
|
||||||
predicate.val # Rails 5.2, 6.0
|
|
||||||
end
|
|
||||||
|
|
||||||
value.map do |val|
|
|
||||||
val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
|
val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -149,7 +149,7 @@ module Ransack
|
||||||
# User may want to configure it like this:
|
# User may want to configure it like this:
|
||||||
#
|
#
|
||||||
# Ransack.configure do |c|
|
# Ransack.configure do |c|
|
||||||
# c.postgres_fields_sort_option = :nulls_first # or :nulls_last
|
# c.postgres_fields_sort_option = :nulls_first # or e.g. :nulls_always_last
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# See this feature: https://www.postgresql.org/docs/13/queries-order.html
|
# See this feature: https://www.postgresql.org/docs/13/queries-order.html
|
||||||
|
|
|
@ -130,12 +130,20 @@ module Ransack
|
||||||
|
|
||||||
def url_options
|
def url_options
|
||||||
@params.merge(
|
@params.merge(
|
||||||
@options.merge(
|
@options.except(:class).merge(
|
||||||
@search.context.search_key => search_and_sort_params))
|
@search.context.search_key => search_and_sort_params))
|
||||||
end
|
end
|
||||||
|
|
||||||
def html_options(args)
|
def html_options(args)
|
||||||
|
if args.empty?
|
||||||
|
html_options = @options
|
||||||
|
else
|
||||||
|
deprecation_message = "Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one."
|
||||||
|
caller_location = caller_locations(2, 2).first
|
||||||
|
warn "#{deprecation_message} (called at #{caller_location.path}:#{caller_location.lineno})"
|
||||||
html_options = extract_options_and_mutate_args!(args)
|
html_options = extract_options_and_mutate_args!(args)
|
||||||
|
end
|
||||||
|
|
||||||
html_options.merge(
|
html_options.merge(
|
||||||
class: [['sort_link'.freeze, @current_dir], html_options[:class]]
|
class: [['sort_link'.freeze, @current_dir], html_options[:class]]
|
||||||
.compact.join(' '.freeze)
|
.compact.join(' '.freeze)
|
||||||
|
@ -145,7 +153,7 @@ module Ransack
|
||||||
private
|
private
|
||||||
|
|
||||||
def parameters_hash(params)
|
def parameters_hash(params)
|
||||||
if ::ActiveRecord::VERSION::MAJOR >= 5 && params.respond_to?(:to_unsafe_h)
|
if params.respond_to?(:to_unsafe_h)
|
||||||
params.to_unsafe_h
|
params.to_unsafe_h
|
||||||
else
|
else
|
||||||
params
|
params
|
||||||
|
|
|
@ -31,8 +31,8 @@ module Ransack
|
||||||
end
|
end
|
||||||
|
|
||||||
def name=(name)
|
def name=(name)
|
||||||
@name = name
|
@name = context.ransackable_alias(name) || name
|
||||||
context.bind(self, name)
|
context.bind(self, @name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def dir=(dir)
|
def dir=(dir)
|
||||||
|
|
|
@ -43,10 +43,10 @@ module Ransack
|
||||||
collapse_multiparameter_attributes!(params).each do |key, value|
|
collapse_multiparameter_attributes!(params).each do |key, value|
|
||||||
if ['s'.freeze, 'sorts'.freeze].freeze.include?(key)
|
if ['s'.freeze, 'sorts'.freeze].freeze.include?(key)
|
||||||
send("#{key}=", value)
|
send("#{key}=", value)
|
||||||
elsif base.attribute_method?(key)
|
|
||||||
base.send("#{key}=", value)
|
|
||||||
elsif @context.ransackable_scope?(key, @context.object)
|
elsif @context.ransackable_scope?(key, @context.object)
|
||||||
add_scope(key, value)
|
add_scope(key, value)
|
||||||
|
elsif base.attribute_method?(key)
|
||||||
|
base.send("#{key}=", value)
|
||||||
elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions
|
elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions
|
||||||
raise ArgumentError, "Invalid search term #{key}"
|
raise ArgumentError, "Invalid search term #{key}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
module Ransack
|
module Ransack
|
||||||
VERSION = '2.4.2'
|
VERSION = '2.6.0'
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,8 +15,8 @@ Gem::Specification.new do |s|
|
||||||
s.required_ruby_version = '>= 2.6'
|
s.required_ruby_version = '>= 2.6'
|
||||||
s.license = 'MIT'
|
s.license = 'MIT'
|
||||||
|
|
||||||
s.add_dependency 'activerecord', '>= 5.2.4'
|
s.add_dependency 'activerecord', '>= 6.0.4'
|
||||||
s.add_dependency 'activesupport', '>= 5.2.4'
|
s.add_dependency 'activesupport', '>= 6.0.4'
|
||||||
s.add_dependency 'i18n'
|
s.add_dependency 'i18n'
|
||||||
|
|
||||||
s.files = `git ls-files`.split("\n")
|
s.files = `git ls-files`.split("\n")
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
module Polyamorous
|
||||||
|
describe "ActiveRecord Compatibility" do
|
||||||
|
it 'works with self joins and includes' do
|
||||||
|
trade_account = Account.create!
|
||||||
|
Account.create!(trade_account: trade_account)
|
||||||
|
|
||||||
|
accounts = Account.joins(:trade_account).includes(:trade_account, :agent_account)
|
||||||
|
account = accounts.first
|
||||||
|
|
||||||
|
expect(account.agent_account).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,12 +12,7 @@ module Polyamorous
|
||||||
|
|
||||||
subject { new_join_association(reflection, parent.children, Person) }
|
subject { new_join_association(reflection, parent.children, Person) }
|
||||||
|
|
||||||
it 'respects polymorphism on equality test' do
|
it 'leaves the original reflection intact for thread safety' do
|
||||||
expect(subject).to eq new_join_association(reflection, parent.children, Person)
|
|
||||||
expect(subject).not_to eq new_join_association(reflection, parent.children, Article)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'leaves the orginal reflection intact for thread safety' do
|
|
||||||
reflection.instance_variable_set(:@klass, Article)
|
reflection.instance_variable_set(:@klass, Article)
|
||||||
join_association
|
join_association
|
||||||
.swapping_reflection_klass(reflection, Person) do |new_reflection|
|
.swapping_reflection_klass(reflection, Person) do |new_reflection|
|
||||||
|
|
|
@ -77,21 +77,5 @@ module Polyamorous
|
||||||
specify { expect(subject.send(:join_root).drop(1)[1].table_name)
|
specify { expect(subject.send(:join_root).drop(1)[1].table_name)
|
||||||
.to eq 'comments' }
|
.to eq 'comments' }
|
||||||
end
|
end
|
||||||
|
|
||||||
context '#left_outer_join in Rails 5 overrides join type specified',
|
|
||||||
if: ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MAJOR < 6 && ActiveRecord::VERSION::MINOR < 2 do
|
|
||||||
|
|
||||||
let(:join_type_class) do
|
|
||||||
new_join_dependency(
|
|
||||||
Person,
|
|
||||||
new_join(:articles)
|
|
||||||
).join_constraints(
|
|
||||||
[],
|
|
||||||
Arel::Nodes::OuterJoin
|
|
||||||
).first.joins.map(&:class)
|
|
||||||
end
|
|
||||||
|
|
||||||
specify { expect(join_type_class).to eq [Arel::Nodes::OuterJoin] }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,6 @@ module Ransack
|
||||||
subject { ::ActiveRecord::Base }
|
subject { ::ActiveRecord::Base }
|
||||||
|
|
||||||
it { should respond_to :ransack }
|
it { should respond_to :ransack }
|
||||||
it { should respond_to :search }
|
|
||||||
|
|
||||||
describe '#search' do
|
describe '#search' do
|
||||||
subject { Person.ransack }
|
subject { Person.ransack }
|
||||||
|
@ -44,12 +43,12 @@ module Ransack
|
||||||
|
|
||||||
it 'applies stringy boolean scopes with true value in an array' do
|
it 'applies stringy boolean scopes with true value in an array' do
|
||||||
s = Person.ransack('of_age' => ['true'])
|
s = Person.ransack('of_age' => ['true'])
|
||||||
expect(s.result.to_sql).to (include 'age >= 18')
|
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{(age >= '18')} : 'age >= 18')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'applies stringy boolean scopes with false value in an array' do
|
it 'applies stringy boolean scopes with false value in an array' do
|
||||||
s = Person.ransack('of_age' => ['false'])
|
s = Person.ransack('of_age' => ['false'])
|
||||||
expect(s.result.to_sql).to (include 'age < 18')
|
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age < '18'} : 'age < 18')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ignores unlisted scopes' do
|
it 'ignores unlisted scopes' do
|
||||||
|
@ -69,12 +68,12 @@ module Ransack
|
||||||
|
|
||||||
it 'passes values to scopes' do
|
it 'passes values to scopes' do
|
||||||
s = Person.ransack('over_age' => 18)
|
s = Person.ransack('over_age' => 18)
|
||||||
expect(s.result.to_sql).to (include 'age > 18')
|
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'chains scopes' do
|
it 'chains scopes' do
|
||||||
s = Person.ransack('over_age' => 18, 'active' => true)
|
s = Person.ransack('over_age' => 18, 'active' => true)
|
||||||
expect(s.result.to_sql).to (include 'age > 18')
|
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
|
||||||
expect(s.result.to_sql).to (include 'active = 1')
|
expect(s.result.to_sql).to (include 'active = 1')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -99,12 +98,12 @@ module Ransack
|
||||||
|
|
||||||
it 'passes true values to scopes' do
|
it 'passes true values to scopes' do
|
||||||
s = Person.ransack('over_age' => 1)
|
s = Person.ransack('over_age' => 1)
|
||||||
expect(s.result.to_sql).to (include 'age > 1')
|
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'passes false values to scopes' do
|
it 'passes false values to scopes' do
|
||||||
s = Person.ransack('over_age' => 0)
|
s = Person.ransack('over_age' => 0)
|
||||||
expect(s.result.to_sql).to (include 'age > 0')
|
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -117,12 +116,12 @@ module Ransack
|
||||||
|
|
||||||
it 'passes true values to scopes' do
|
it 'passes true values to scopes' do
|
||||||
s = Person.ransack('over_age' => 1)
|
s = Person.ransack('over_age' => 1)
|
||||||
expect(s.result.to_sql).to (include 'age > 1')
|
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'passes false values to scopes' do
|
it 'passes false values to scopes' do
|
||||||
s = Person.ransack('over_age' => 0)
|
s = Person.ransack('over_age' => 0)
|
||||||
expect(s.result.to_sql).to (include 'age > 0')
|
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -324,7 +323,11 @@ module Ransack
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should function correctly with a multi-parameter attribute' do
|
it 'should function correctly with a multi-parameter attribute' do
|
||||||
|
if ::ActiveRecord::VERSION::MAJOR >= 7
|
||||||
|
::ActiveRecord.default_timezone = :utc
|
||||||
|
else
|
||||||
::ActiveRecord::Base.default_timezone = :utc
|
::ActiveRecord::Base.default_timezone = :utc
|
||||||
|
end
|
||||||
Time.zone = 'UTC'
|
Time.zone = 'UTC'
|
||||||
|
|
||||||
date = Date.current
|
date = Date.current
|
||||||
|
@ -700,6 +703,10 @@ module Ransack
|
||||||
it { should eq [] }
|
it { should eq [] }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def rails7_and_mysql
|
||||||
|
::ActiveRecord::VERSION::MAJOR >= 7 && ENV['DB'] == 'mysql'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -469,8 +469,7 @@ module Ransack
|
||||||
it { should match /exist\=existing/ }
|
it { should match /exist\=existing/ }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'using a real ActionController::Parameter object',
|
context 'using a real ActionController::Parameter object' do
|
||||||
if: ::ActiveRecord::VERSION::MAJOR > 3 do
|
|
||||||
|
|
||||||
describe 'with symbol q:, #sort_link should include search params' do
|
describe 'with symbol q:, #sort_link should include search params' do
|
||||||
subject { @controller.view_context.sort_link(Person.ransack, :name) }
|
subject { @controller.view_context.sort_link(Person.ransack, :name) }
|
||||||
|
@ -727,6 +726,38 @@ module Ransack
|
||||||
it { should match /Block label ▼/ }
|
it { should match /Block label ▼/ }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#sort_link with class option' do
|
||||||
|
subject { @controller.view_context
|
||||||
|
.sort_link(
|
||||||
|
[:main_app, Person.ransack(sorts: ['name desc'])],
|
||||||
|
:name,
|
||||||
|
class: 'people', controller: 'people'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
it { should match /class="sort_link desc people"/ }
|
||||||
|
it { should_not match /people\?class=people/ }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#sort_link with class option workaround' do
|
||||||
|
it "generates a correct link and prints a deprecation" do
|
||||||
|
expect do
|
||||||
|
link = @controller.view_context
|
||||||
|
.sort_link(
|
||||||
|
[:main_app, Person.ransack(sorts: ['name desc'])],
|
||||||
|
:name,
|
||||||
|
'name',
|
||||||
|
{ controller: 'people' },
|
||||||
|
class: 'people'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(link).to match(/class="sort_link desc people"/)
|
||||||
|
expect(link).not_to match(/people\?class=people/)
|
||||||
|
end.to output(
|
||||||
|
/Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one\. \(called at #{Regexp.escape(__FILE__)}:/
|
||||||
|
).to_stderr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#search_form_for with default format' do
|
describe '#search_form_for with default format' do
|
||||||
subject { @controller.view_context
|
subject { @controller.view_context
|
||||||
.search_form_for(Person.ransack) {} }
|
.search_form_for(Person.ransack) {} }
|
||||||
|
|
|
@ -3,6 +3,19 @@ require 'spec_helper'
|
||||||
module Ransack
|
module Ransack
|
||||||
module Nodes
|
module Nodes
|
||||||
describe Condition do
|
describe Condition do
|
||||||
|
context 'bug report #1245' do
|
||||||
|
it 'preserves tuple behavior' do
|
||||||
|
ransack_hash = {
|
||||||
|
m: 'and',
|
||||||
|
g: [
|
||||||
|
{ title_type_in: ['["title 1", ""]'] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = Article.ransack(ransack_hash).result.to_sql
|
||||||
|
expect(sql).to include("IN (('title 1', ''))")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with an alias' do
|
context 'with an alias' do
|
||||||
subject {
|
subject {
|
||||||
|
|
|
@ -312,6 +312,29 @@ module Ransack
|
||||||
expect { Search.new(Person, params) }.not_to change { params }
|
expect { Search.new(Person, params) }.not_to change { params }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "ransackable_scope" do
|
||||||
|
around(:each) do |example|
|
||||||
|
Person.define_singleton_method(:name_eq) do |name|
|
||||||
|
self.where(name: name)
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
example.run
|
||||||
|
ensure
|
||||||
|
Person.singleton_class.undef_method :name_eq
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is prioritized over base predicates" do
|
||||||
|
allow(Person).to receive(:ransackable_scopes)
|
||||||
|
.and_return(Person.ransackable_scopes + [:name_eq])
|
||||||
|
|
||||||
|
s = Search.new(Person, name_eq: "Johny")
|
||||||
|
expect(s.instance_variable_get(:@scope_args)["name_eq"]).to eq("Johny")
|
||||||
|
expect(s.base[:name_eq]).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#result' do
|
describe '#result' do
|
||||||
|
@ -332,8 +355,6 @@ module Ransack
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'use appropriate table alias' do
|
it 'use appropriate table alias' do
|
||||||
skip "Rails 6 regressed here, but it's fixed in 6-0-stable since https://github.com/rails/rails/commit/f9ba52477ca288e7effa5f6794ae3df3f4e982bc" if ENV["RAILS"] == "v6.0.3"
|
|
||||||
|
|
||||||
s = Search.new(Person, {
|
s = Search.new(Person, {
|
||||||
name_eq: "person_name_query",
|
name_eq: "person_name_query",
|
||||||
articles_title_eq: "person_article_title_query",
|
articles_title_eq: "person_article_title_query",
|
||||||
|
@ -483,82 +504,109 @@ module Ransack
|
||||||
expect(sort.dir).to eq 'asc'
|
expect(sort.dir).to eq 'asc'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates sorts based on multiple attributes/directions in array format' do
|
it 'creates sorts based on a single alias/direction' do
|
||||||
@s.sorts = ['id desc', { name: 'name', dir: 'asc' }]
|
@s.sorts = 'daddy desc'
|
||||||
|
expect(@s.sorts.size).to eq(1)
|
||||||
|
sort = @s.sorts.first
|
||||||
|
expect(sort).to be_a Nodes::Sort
|
||||||
|
expect(sort.name).to eq 'parent_name'
|
||||||
|
expect(sort.dir).to eq 'desc'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates sorts based on a single alias and uppercase direction' do
|
||||||
|
@s.sorts = 'daddy DESC'
|
||||||
|
expect(@s.sorts.size).to eq(1)
|
||||||
|
sort = @s.sorts.first
|
||||||
|
expect(sort).to be_a Nodes::Sort
|
||||||
|
expect(sort.name).to eq 'parent_name'
|
||||||
|
expect(sort.dir).to eq 'desc'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates sorts based on a single alias and without direction' do
|
||||||
|
@s.sorts = 'daddy'
|
||||||
|
expect(@s.sorts.size).to eq(1)
|
||||||
|
sort = @s.sorts.first
|
||||||
|
expect(sort).to be_a Nodes::Sort
|
||||||
|
expect(sort.name).to eq 'parent_name'
|
||||||
|
expect(sort.dir).to eq 'asc'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates sorts based on attributes, alias and directions in array format' do
|
||||||
|
@s.sorts = ['id desc', { name: 'daddy', dir: 'asc' }]
|
||||||
expect(@s.sorts.size).to eq(2)
|
expect(@s.sorts.size).to eq(2)
|
||||||
sort1, sort2 = @s.sorts
|
sort1, sort2 = @s.sorts
|
||||||
expect(sort1).to be_a Nodes::Sort
|
expect(sort1).to be_a Nodes::Sort
|
||||||
expect(sort1.name).to eq 'id'
|
expect(sort1.name).to eq 'id'
|
||||||
expect(sort1.dir).to eq 'desc'
|
expect(sort1.dir).to eq 'desc'
|
||||||
expect(sort2).to be_a Nodes::Sort
|
expect(sort2).to be_a Nodes::Sort
|
||||||
expect(sort2.name).to eq 'name'
|
expect(sort2.name).to eq 'parent_name'
|
||||||
expect(sort2.dir).to eq 'asc'
|
expect(sort2.dir).to eq 'asc'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates sorts based on multiple attributes and uppercase directions in array format' do
|
it 'creates sorts based on attributes, alias and uppercase directions in array format' do
|
||||||
@s.sorts = ['id DESC', { name: 'name', dir: 'ASC' }]
|
@s.sorts = ['id DESC', { name: 'daddy', dir: 'ASC' }]
|
||||||
expect(@s.sorts.size).to eq(2)
|
expect(@s.sorts.size).to eq(2)
|
||||||
sort1, sort2 = @s.sorts
|
sort1, sort2 = @s.sorts
|
||||||
expect(sort1).to be_a Nodes::Sort
|
expect(sort1).to be_a Nodes::Sort
|
||||||
expect(sort1.name).to eq 'id'
|
expect(sort1.name).to eq 'id'
|
||||||
expect(sort1.dir).to eq 'desc'
|
expect(sort1.dir).to eq 'desc'
|
||||||
expect(sort2).to be_a Nodes::Sort
|
expect(sort2).to be_a Nodes::Sort
|
||||||
expect(sort2.name).to eq 'name'
|
expect(sort2.name).to eq 'parent_name'
|
||||||
expect(sort2.dir).to eq 'asc'
|
expect(sort2.dir).to eq 'asc'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates sorts based on multiple attributes and different directions
|
it 'creates sorts based on attributes, alias and different directions
|
||||||
in array format' do
|
in array format' do
|
||||||
@s.sorts = ['id DESC', { name: 'name', dir: nil }]
|
@s.sorts = ['id DESC', { name: 'daddy', dir: nil }]
|
||||||
expect(@s.sorts.size).to eq(2)
|
expect(@s.sorts.size).to eq(2)
|
||||||
sort1, sort2 = @s.sorts
|
sort1, sort2 = @s.sorts
|
||||||
expect(sort1).to be_a Nodes::Sort
|
expect(sort1).to be_a Nodes::Sort
|
||||||
expect(sort1.name).to eq 'id'
|
expect(sort1.name).to eq 'id'
|
||||||
expect(sort1.dir).to eq 'desc'
|
expect(sort1.dir).to eq 'desc'
|
||||||
expect(sort2).to be_a Nodes::Sort
|
expect(sort2).to be_a Nodes::Sort
|
||||||
expect(sort2.name).to eq 'name'
|
expect(sort2.name).to eq 'parent_name'
|
||||||
expect(sort2.dir).to eq 'asc'
|
expect(sort2.dir).to eq 'asc'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates sorts based on multiple attributes/directions in hash format' do
|
it 'creates sorts based on attributes, alias and directions in hash format' do
|
||||||
@s.sorts = {
|
@s.sorts = {
|
||||||
'0' => { name: 'id', dir: 'desc' },
|
'0' => { name: 'id', dir: 'desc' },
|
||||||
'1' => { name: 'name', dir: 'asc' }
|
'1' => { name: 'daddy', dir: 'asc' }
|
||||||
}
|
}
|
||||||
expect(@s.sorts.size).to eq(2)
|
expect(@s.sorts.size).to eq(2)
|
||||||
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
||||||
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
||||||
name_sort = @s.sorts.detect { |s| s.name == 'name' }
|
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
||||||
expect(id_sort.dir).to eq 'desc'
|
expect(id_sort.dir).to eq 'desc'
|
||||||
expect(name_sort.dir).to eq 'asc'
|
expect(daddy_sort.dir).to eq 'asc'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates sorts based on multiple attributes and uppercase directions
|
it 'creates sorts based on attributes, alias and uppercase directions
|
||||||
in hash format' do
|
in hash format' do
|
||||||
@s.sorts = {
|
@s.sorts = {
|
||||||
'0' => { name: 'id', dir: 'DESC' },
|
'0' => { name: 'id', dir: 'DESC' },
|
||||||
'1' => { name: 'name', dir: 'ASC' }
|
'1' => { name: 'daddy', dir: 'ASC' }
|
||||||
}
|
}
|
||||||
expect(@s.sorts.size).to eq(2)
|
expect(@s.sorts.size).to eq(2)
|
||||||
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
||||||
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
||||||
name_sort = @s.sorts.detect { |s| s.name == 'name' }
|
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
||||||
expect(id_sort.dir).to eq 'desc'
|
expect(id_sort.dir).to eq 'desc'
|
||||||
expect(name_sort.dir).to eq 'asc'
|
expect(daddy_sort.dir).to eq 'asc'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates sorts based on multiple attributes and different directions
|
it 'creates sorts based on attributes, alias and different directions
|
||||||
in hash format' do
|
in hash format' do
|
||||||
@s.sorts = {
|
@s.sorts = {
|
||||||
'0' => { name: 'id', dir: 'DESC' },
|
'0' => { name: 'id', dir: 'DESC' },
|
||||||
'1' => { name: 'name', dir: nil }
|
'1' => { name: 'daddy', dir: nil }
|
||||||
}
|
}
|
||||||
expect(@s.sorts.size).to eq(2)
|
expect(@s.sorts.size).to eq(2)
|
||||||
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
||||||
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
||||||
name_sort = @s.sorts.detect { |s| s.name == 'name' }
|
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
||||||
expect(id_sort.dir).to eq 'desc'
|
expect(id_sort.dir).to eq 'desc'
|
||||||
expect(name_sort.dir).to eq 'asc'
|
expect(daddy_sort.dir).to eq 'asc'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'overrides existing sort' do
|
it 'overrides existing sort' do
|
||||||
|
@ -605,6 +653,18 @@ module Ransack
|
||||||
s = Search.new(Person, s: 'doubled_name desc')
|
s = Search.new(Person, s: 'doubled_name desc')
|
||||||
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
|
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
|
||||||
|
|
||||||
|
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_first }
|
||||||
|
s = Search.new(Person, s: 'doubled_name asc')
|
||||||
|
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
|
||||||
|
s = Search.new(Person, s: 'doubled_name desc')
|
||||||
|
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
|
||||||
|
|
||||||
|
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_last }
|
||||||
|
s = Search.new(Person, s: 'doubled_name asc')
|
||||||
|
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
|
||||||
|
s = Search.new(Person, s: 'doubled_name desc')
|
||||||
|
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
|
||||||
|
|
||||||
Ransack.options = default
|
Ransack.options = default
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -154,6 +154,29 @@ class Article < ActiveRecord::Base
|
||||||
|
|
||||||
joins(join).where("latest_comment.body ILIKE ?", "%#{msg}%")
|
joins(join).where("latest_comment.body ILIKE ?", "%#{msg}%")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ransacker :title_type, formatter: lambda { |tuples|
|
||||||
|
title, type = JSON.parse(tuples)
|
||||||
|
Arel::Nodes::Grouping.new(
|
||||||
|
[
|
||||||
|
Arel::Nodes.build_quoted(title),
|
||||||
|
Arel::Nodes.build_quoted(type)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
} do |_parent|
|
||||||
|
articles = Article.arel_table
|
||||||
|
Arel::Nodes::Grouping.new(
|
||||||
|
%i[title type].map do |field|
|
||||||
|
Arel::Nodes::NamedFunction.new(
|
||||||
|
'COALESCE',
|
||||||
|
[
|
||||||
|
Arel::Nodes::NamedFunction.new('TRIM', [articles[field]]),
|
||||||
|
Arel::Nodes.build_quoted('')
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class StoryArticle < Article
|
class StoryArticle < Article
|
||||||
|
@ -192,6 +215,11 @@ class Note < ActiveRecord::Base
|
||||||
belongs_to :notable, polymorphic: true
|
belongs_to :notable, polymorphic: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Account < ActiveRecord::Base
|
||||||
|
belongs_to :agent_account, class_name: "Account"
|
||||||
|
belongs_to :trade_account, class_name: "Account"
|
||||||
|
end
|
||||||
|
|
||||||
module Schema
|
module Schema
|
||||||
def self.create
|
def self.create
|
||||||
ActiveRecord::Migration.verbose = false
|
ActiveRecord::Migration.verbose = false
|
||||||
|
@ -250,6 +278,11 @@ module Schema
|
||||||
t.integer :target_person_id
|
t.integer :target_person_id
|
||||||
t.integer :article_id
|
t.integer :article_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table :accounts, force: true do |t|
|
||||||
|
t.belongs_to :agent_account
|
||||||
|
t.belongs_to :trade_account
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
10.times do
|
10.times do
|
||||||
|
|
Loading…
Reference in New Issue