ransack 4.3.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/lib/polyamorous/polyamorous.rb +1 -1
- data/lib/ransack/adapters/active_record/context.rb +30 -3
- data/lib/ransack/context.rb +3 -0
- data/lib/ransack/helpers/form_builder.rb +6 -7
- data/lib/ransack/helpers/form_helper.rb +86 -20
- data/lib/ransack/locale/ja.yml +51 -51
- data/lib/ransack/locale/ko.yml +6 -6
- data/lib/ransack/locale/uk.yml +72 -0
- data/lib/ransack/nodes/condition.rb +36 -6
- data/lib/ransack/nodes/grouping.rb +1 -1
- data/lib/ransack/nodes/sort.rb +1 -1
- data/lib/ransack/nodes/value.rb +9 -1
- data/lib/ransack/search.rb +1 -1
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack.rb +8 -0
- data/spec/polyamorous/join_association_spec.rb +0 -1
- data/spec/polyamorous/join_dependency_spec.rb +0 -1
- data/spec/ransack/adapters/active_record/base_spec.rb +101 -2
- data/spec/ransack/adapters/active_record/context_spec.rb +72 -0
- data/spec/ransack/helpers/form_builder_spec.rb +0 -2
- data/spec/ransack/helpers/form_helper_spec.rb +219 -5
- data/spec/ransack/nodes/condition_spec.rb +229 -0
- data/spec/ransack/nodes/grouping_spec.rb +2 -2
- data/spec/ransack/nodes/value_spec.rb +12 -1
- data/spec/ransack/predicate_spec.rb +0 -1
- data/spec/ransack/search_spec.rb +107 -1
- data/spec/ransack/translate_spec.rb +0 -1
- data/spec/spec_helper.rb +2 -3
- data/spec/support/schema.rb +30 -0
- metadata +41 -87
- data/.github/FUNDING.yml +0 -3
- data/.github/SECURITY.md +0 -12
- data/.github/workflows/codeql.yml +0 -72
- data/.github/workflows/cronjob.yml +0 -141
- data/.github/workflows/deploy.yml +0 -35
- data/.github/workflows/rubocop.yml +0 -20
- data/.github/workflows/test-deploy.yml +0 -29
- data/.github/workflows/test.yml +0 -183
- data/.gitignore +0 -7
- data/.nojekyll +0 -0
- data/.rubocop.yml +0 -50
- data/CHANGELOG.md +0 -1193
- data/CONTRIBUTING.md +0 -171
- data/Gemfile +0 -58
- data/Rakefile +0 -24
- data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +0 -78
- data/bug_report_templates/test-ransacker-arel-present-predicate.rb +0 -75
- data/docs/.gitignore +0 -19
- data/docs/.nojekyll +0 -0
- data/docs/babel.config.js +0 -3
- data/docs/blog/2022-03-27-ransack-3.0.0.md +0 -20
- data/docs/docs/getting-started/_category_.json +0 -4
- data/docs/docs/getting-started/advanced-mode.md +0 -46
- data/docs/docs/getting-started/configuration.md +0 -47
- data/docs/docs/getting-started/search-matches.md +0 -67
- data/docs/docs/getting-started/simple-mode.md +0 -289
- data/docs/docs/getting-started/sorting.md +0 -71
- data/docs/docs/getting-started/using-predicates.md +0 -282
- data/docs/docs/going-further/_category_.json +0 -4
- data/docs/docs/going-further/acts-as-taggable-on.md +0 -114
- data/docs/docs/going-further/associations.md +0 -70
- data/docs/docs/going-further/custom-predicates.md +0 -52
- data/docs/docs/going-further/documentation.md +0 -43
- data/docs/docs/going-further/exporting-to-csv.md +0 -49
- data/docs/docs/going-further/external-guides.md +0 -57
- data/docs/docs/going-further/form-customisation.md +0 -63
- data/docs/docs/going-further/i18n.md +0 -53
- data/docs/docs/going-further/img/create_release.png +0 -0
- data/docs/docs/going-further/merging-searches.md +0 -41
- data/docs/docs/going-further/other-notes.md +0 -425
- data/docs/docs/going-further/polymorphic-search.md +0 -46
- data/docs/docs/going-further/ransackers.md +0 -331
- data/docs/docs/going-further/release_process.md +0 -36
- data/docs/docs/going-further/saving-queries.md +0 -82
- data/docs/docs/going-further/searching-postgres.md +0 -57
- data/docs/docs/going-further/wiki-contributors.md +0 -82
- data/docs/docs/intro.md +0 -99
- data/docs/docusaurus.config.js +0 -120
- data/docs/package.json +0 -42
- data/docs/sidebars.js +0 -31
- data/docs/src/components/HomepageFeatures/index.js +0 -64
- data/docs/src/components/HomepageFeatures/styles.module.css +0 -11
- data/docs/src/css/custom.css +0 -39
- data/docs/src/pages/index.module.css +0 -23
- data/docs/src/pages/markdown-page.md +0 -7
- 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 +0 -1
- 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 +0 -171
- data/docs/static/img/undraw_docusaurus_react.svg +0 -170
- data/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- data/docs/static/logo/ransack-h.png +0 -0
- data/docs/static/logo/ransack-h.svg +0 -34
- data/docs/static/logo/ransack-v.png +0 -0
- data/docs/static/logo/ransack-v.svg +0 -34
- data/docs/static/logo/ransack.png +0 -0
- data/docs/static/logo/ransack.svg +0 -21
- data/docs/yarn.lock +0 -8884
- data/ransack.gemspec +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4fe4a128cacdb920f2efe5fc5063cd21b8fa1a4a527fa07e84123f2ead7a6694
|
4
|
+
data.tar.gz: 82f66ffeaa4d8bed52614374fe71617bb1c2d85ac1745dfca7e4520e07782545
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a80ce4b0da981c6e3b2d18de8c742d3598a2a7479b44d04adaa1ce3c60a81fa4377f706c78048c1d72f719d488afd379c303b55811053d8613eb82cf0f0dfcad
|
7
|
+
data.tar.gz: cd3f836f63aa19596a3510961bbe64a1f9abcc858473bf9516623e5fe76908bcbb346d36cf1cd7cb83094f0a3c0f4bcdb942b0e137a9020cd8772e28d01d9f51
|
data/README.md
CHANGED
@@ -33,9 +33,11 @@ gem 'ransack', :github => 'activerecord-hackery/ransack', :branch => 'main'
|
|
33
33
|
|
34
34
|
### Documentation
|
35
35
|
|
36
|
-
There is [extensive documentation on Ransack](https://activerecord-hackery.github.io/ransack/), which is a [Docusaurus](https://docusaurus.io/) project and run as a GitHub Pages site.
|
36
|
+
There is [extensive documentation on Ransack](https://activerecord-hackery.github.io/ransack/), which is a [Docusaurus](https://docusaurus.io/) project and run as a GitHub Pages site. Alternatively there is [AI Generated documentation](https://deepwiki.com/activerecord-hackery/ransack/1-overview) produced by [devin.ai](https://devin.ai/).
|
37
37
|
|
38
|
-
|
38
|
+
This [gist](https://gist.github.com/raghubetina/d5fc3df67ddbadcac271) has a quick-start cheatsheet, created by [@raghubetina](https://gist.github.com/raghubetina)
|
39
|
+
|
40
|
+
## Issue tracker
|
39
41
|
|
40
42
|
* Before filing an issue, please read the [Contributing Guide](CONTRIBUTING.md).
|
41
43
|
* File an issue if a bug is caused by Ransack, is new (has not already been reported), and _can be reproduced from the information you provide_.
|
@@ -19,7 +19,8 @@ module Ransack
|
|
19
19
|
unless schema_cache.send(:data_source_exists?, table)
|
20
20
|
raise "No table named #{table} exists."
|
21
21
|
end
|
22
|
-
attr.klass.columns.find { |column| column.name == name }
|
22
|
+
column = attr.klass.columns.find { |column| column.name == name }
|
23
|
+
column&.type
|
23
24
|
end
|
24
25
|
|
25
26
|
def evaluate(search, opts = {})
|
@@ -171,7 +172,25 @@ module Ransack
|
|
171
172
|
join_constraints.each do |j|
|
172
173
|
subquery.join_sources << Arel::Nodes::InnerJoin.new(j.left, j.right)
|
173
174
|
end
|
174
|
-
|
175
|
+
|
176
|
+
# Handle polymorphic associations where correlated_key is an array
|
177
|
+
if correlated_key.is_a?(Array)
|
178
|
+
# For polymorphic associations, we need to add conditions for both the foreign key and type
|
179
|
+
correlated_key.each_with_index do |key, index|
|
180
|
+
if index == 0
|
181
|
+
# This is the foreign key
|
182
|
+
subquery = subquery.where(key.eq(primary_key))
|
183
|
+
else
|
184
|
+
# This is the type key, which should be equal to the model name
|
185
|
+
subquery = subquery.where(key.eq(@klass.name))
|
186
|
+
end
|
187
|
+
end
|
188
|
+
else
|
189
|
+
# Original behavior for non-polymorphic associations
|
190
|
+
subquery = subquery.where(correlated_key.eq(primary_key))
|
191
|
+
end
|
192
|
+
|
193
|
+
subquery
|
175
194
|
end
|
176
195
|
|
177
196
|
def primary_key
|
@@ -201,7 +220,15 @@ module Ransack
|
|
201
220
|
nil
|
202
221
|
end
|
203
222
|
when Arel::Nodes::And
|
204
|
-
|
223
|
+
# And may have multiple children, so we need to check all, not via left/right
|
224
|
+
if join_root.children.any?
|
225
|
+
join_root.children.each do |child|
|
226
|
+
key = extract_correlated_key(child)
|
227
|
+
return key if key
|
228
|
+
end
|
229
|
+
else
|
230
|
+
extract_correlated_key(join_root.left) || extract_correlated_key(join_root.right)
|
231
|
+
end
|
205
232
|
else
|
206
233
|
# eg parent was Arel::Nodes::And and the evaluated side was one of
|
207
234
|
# Arel::Nodes::Grouping or MultiTenant::TenantEnforcementClause
|
data/lib/ransack/context.rb
CHANGED
@@ -77,6 +77,9 @@ module Ransack
|
|
77
77
|
return unless @klass.method(scope) && args != false
|
78
78
|
@object = if scope_arity(scope) < 1 && args == true
|
79
79
|
@object.public_send(scope)
|
80
|
+
elsif scope_arity(scope) == 1 && args.is_a?(Array)
|
81
|
+
# For scopes with arity 1, pass the array as a single argument instead of splatting
|
82
|
+
@object.public_send(scope, args)
|
80
83
|
else
|
81
84
|
@object.public_send(scope, *args)
|
82
85
|
end
|
@@ -6,13 +6,12 @@ module ActionView::Helpers::Tags
|
|
6
6
|
# https://github.com/rails/rails/commit/c1a118a
|
7
7
|
class Base
|
8
8
|
private
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
9
|
+
|
10
|
+
def value
|
11
|
+
if @allow_method_names_outside_object
|
12
|
+
object.send @method_name if object && object.respond_to?(@method_name, true)
|
13
|
+
else
|
14
|
+
object.send @method_name if object
|
16
15
|
end
|
17
16
|
end
|
18
17
|
end
|
@@ -7,30 +7,48 @@ module Ransack
|
|
7
7
|
# <%= search_form_for(@q) do |f| %>
|
8
8
|
#
|
9
9
|
def search_form_for(record, options = {}, &proc)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
search = extract_search_and_set_url(record, options, 'search_form_for')
|
11
|
+
options[:html] ||= {}
|
12
|
+
html_options = build_html_options(search, options, :get)
|
13
|
+
finalize_form_options(options, html_options)
|
14
|
+
form_for(record, options, &proc)
|
15
|
+
end
|
16
|
+
|
17
|
+
# +search_form_with+
|
18
|
+
#
|
19
|
+
# <%= search_form_with(model: @q) do |f| %>
|
20
|
+
#
|
21
|
+
def search_form_with(record_or_options = {}, options = {}, &proc)
|
22
|
+
if record_or_options.is_a?(Hash) && record_or_options.key?(:model)
|
23
|
+
# Called with keyword arguments: search_form_with(model: @q)
|
24
|
+
options = record_or_options
|
25
|
+
record = options.delete(:model)
|
20
26
|
else
|
21
|
-
|
22
|
-
|
27
|
+
# Called with positional arguments: search_form_with(@q)
|
28
|
+
record = record_or_options
|
23
29
|
end
|
30
|
+
search = extract_search_and_set_url(record, options, 'search_form_with')
|
24
31
|
options[:html] ||= {}
|
25
|
-
html_options =
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
}
|
30
|
-
options[:as] ||= Ransack.options[:search_key]
|
31
|
-
options[:html].reverse_merge!(html_options)
|
32
|
-
options[:builder] ||= FormBuilder
|
32
|
+
html_options = build_html_options(search, options, :get)
|
33
|
+
finalize_form_with_options(options, html_options)
|
34
|
+
form_with(model: search, **options, &proc)
|
35
|
+
end
|
33
36
|
|
37
|
+
# +turbo_search_form_for+
|
38
|
+
#
|
39
|
+
# <%= turbo_search_form_for(@q) do |f| %>
|
40
|
+
#
|
41
|
+
# This is a turbo-enabled version of search_form_for that submits via turbo streams
|
42
|
+
# instead of traditional HTML GET requests. Useful for seamless integration with
|
43
|
+
# paginated results and other turbo-enabled components.
|
44
|
+
#
|
45
|
+
def turbo_search_form_for(record, options = {}, &proc)
|
46
|
+
search = extract_search_and_set_url(record, options, 'turbo_search_form_for')
|
47
|
+
options[:html] ||= {}
|
48
|
+
turbo_options = build_turbo_options(options)
|
49
|
+
method = options.delete(:method) || :post
|
50
|
+
html_options = build_html_options(search, options, method).merge(turbo_options)
|
51
|
+
finalize_form_options(options, html_options)
|
34
52
|
form_for(record, options, &proc)
|
35
53
|
end
|
36
54
|
|
@@ -68,6 +86,54 @@ module Ransack
|
|
68
86
|
|
69
87
|
private
|
70
88
|
|
89
|
+
def extract_search_and_set_url(record, options, method_name)
|
90
|
+
if record.is_a? Ransack::Search
|
91
|
+
search = record
|
92
|
+
options[:url] ||= polymorphic_path(
|
93
|
+
search.klass, format: options.delete(:format)
|
94
|
+
)
|
95
|
+
search
|
96
|
+
elsif record.is_a?(Array) &&
|
97
|
+
(search = record.detect { |o| o.is_a?(Ransack::Search) })
|
98
|
+
options[:url] ||= polymorphic_path(
|
99
|
+
options_for(record), format: options.delete(:format)
|
100
|
+
)
|
101
|
+
search
|
102
|
+
else
|
103
|
+
raise ArgumentError,
|
104
|
+
"No Ransack::Search object was provided to #{method_name}!"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def build_turbo_options(options)
|
109
|
+
data_options = {}
|
110
|
+
if options[:turbo_frame]
|
111
|
+
data_options[:turbo_frame] = options.delete(:turbo_frame)
|
112
|
+
end
|
113
|
+
data_options[:turbo_action] = options.delete(:turbo_action) || 'advance'
|
114
|
+
{ data: data_options }
|
115
|
+
end
|
116
|
+
|
117
|
+
def build_html_options(search, options, method)
|
118
|
+
{
|
119
|
+
class: html_option_for(options[:class], search),
|
120
|
+
id: html_option_for(options[:id], search),
|
121
|
+
method: method
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
def finalize_form_options(options, html_options)
|
126
|
+
options[:as] ||= Ransack.options[:search_key]
|
127
|
+
options[:html].reverse_merge!(html_options)
|
128
|
+
options[:builder] ||= FormBuilder
|
129
|
+
end
|
130
|
+
|
131
|
+
def finalize_form_with_options(options, html_options)
|
132
|
+
options[:scope] ||= Ransack.options[:search_key]
|
133
|
+
options[:html].reverse_merge!(html_options)
|
134
|
+
options[:builder] ||= FormBuilder
|
135
|
+
end
|
136
|
+
|
71
137
|
def options_for(record)
|
72
138
|
record.map { |r| parse_record(r) }
|
73
139
|
end
|
data/lib/ransack/locale/ja.yml
CHANGED
@@ -14,57 +14,57 @@ ja:
|
|
14
14
|
asc: "昇順"
|
15
15
|
desc: "降順"
|
16
16
|
predicates:
|
17
|
-
eq: "
|
18
|
-
eq_any: "
|
19
|
-
eq_all: "
|
20
|
-
not_eq: "
|
21
|
-
not_eq_any: "
|
22
|
-
not_eq_all: "
|
23
|
-
matches: "
|
24
|
-
matches_any: "
|
25
|
-
matches_all: "
|
26
|
-
does_not_match: "
|
27
|
-
does_not_match_any: "
|
28
|
-
does_not_match_all: "
|
29
|
-
lt: "
|
30
|
-
lt_any: "
|
31
|
-
lt_all: "
|
32
|
-
lteq: "
|
33
|
-
lteq_any: "
|
34
|
-
lteq_all: "
|
35
|
-
gt: "
|
36
|
-
gt_any: "
|
37
|
-
gt_all: "
|
38
|
-
gteq: "
|
39
|
-
gteq_any: "
|
40
|
-
gteq_all: "
|
41
|
-
in: "
|
42
|
-
in_any: "
|
43
|
-
in_all: "
|
44
|
-
not_in: "
|
45
|
-
not_in_any: "
|
46
|
-
not_in_all: "
|
47
|
-
cont: "
|
48
|
-
cont_any: "
|
49
|
-
cont_all: "
|
50
|
-
not_cont: "
|
51
|
-
not_cont_any: "
|
52
|
-
not_cont_all: "
|
53
|
-
start: "
|
54
|
-
start_any: "
|
55
|
-
start_all: "
|
56
|
-
not_start: "
|
57
|
-
not_start_any: "
|
58
|
-
not_start_all: "
|
59
|
-
end: "
|
60
|
-
end_any: "
|
61
|
-
end_all: "
|
62
|
-
not_end: "
|
63
|
-
not_end_any: "
|
64
|
-
not_end_all: "
|
17
|
+
eq: "等しい"
|
18
|
+
eq_any: "いずれかに等しい"
|
19
|
+
eq_all: "全てに等しい"
|
20
|
+
not_eq: "等しくない"
|
21
|
+
not_eq_any: "いずれかに等しくない"
|
22
|
+
not_eq_all: "全てと等しくない"
|
23
|
+
matches: "合致している"
|
24
|
+
matches_any: "いずれかと合致している"
|
25
|
+
matches_all: "全てと合致している"
|
26
|
+
does_not_match: "合致していない"
|
27
|
+
does_not_match_any: "いずれかに合致していない"
|
28
|
+
does_not_match_all: "全てに合致していない"
|
29
|
+
lt: "小さい"
|
30
|
+
lt_any: "いずれかより小さい"
|
31
|
+
lt_all: "全てよりも小さい"
|
32
|
+
lteq: "小さいか等しい"
|
33
|
+
lteq_any: "いずれかより小さいか等しい"
|
34
|
+
lteq_all: "全てより小さいか等しい"
|
35
|
+
gt: "大きい"
|
36
|
+
gt_any: "いずれかより大きい"
|
37
|
+
gt_all: "全てより大きい"
|
38
|
+
gteq: "大きいか等しい"
|
39
|
+
gteq_any: "いずれかより大きいか等しい"
|
40
|
+
gteq_all: "全てより大きいか等しい"
|
41
|
+
in: "範囲内である"
|
42
|
+
in_any: "いずれかの範囲内である"
|
43
|
+
in_all: "全ての範囲内である"
|
44
|
+
not_in: "範囲内でない"
|
45
|
+
not_in_any: "いずれかの範囲内でない"
|
46
|
+
not_in_all: "全ての範囲内"
|
47
|
+
cont: "含む"
|
48
|
+
cont_any: "いずれかを含む"
|
49
|
+
cont_all: "全てを含む"
|
50
|
+
not_cont: "含まない"
|
51
|
+
not_cont_any: "いずれかを含まない"
|
52
|
+
not_cont_all: "全てを含まない"
|
53
|
+
start: "始まる"
|
54
|
+
start_any: "どれかで始まる"
|
55
|
+
start_all: "全てで始まる"
|
56
|
+
not_start: "始まらない"
|
57
|
+
not_start_any: "いずれかで始まらない"
|
58
|
+
not_start_all: "全てで始まらない"
|
59
|
+
end: "終わる"
|
60
|
+
end_any: "いずれかで終わる"
|
61
|
+
end_all: "全てで終わる"
|
62
|
+
not_end: "どれでも終わらない"
|
63
|
+
not_end_any: "いずれかで終わらない"
|
64
|
+
not_end_all: "全てで終わらない"
|
65
65
|
'true': "真"
|
66
66
|
'false': "偽"
|
67
|
-
present: "
|
68
|
-
blank: "
|
67
|
+
present: "存在する"
|
68
|
+
blank: "空である"
|
69
69
|
'null': "無効"
|
70
|
-
not_null: "
|
70
|
+
not_null: "無効ではない"
|
data/lib/ransack/locale/ko.yml
CHANGED
@@ -62,9 +62,9 @@ ko:
|
|
62
62
|
not_end: "끝나지 않음"
|
63
63
|
not_end_any: "어떤 것이든 끝나지 않음"
|
64
64
|
not_end_all: "모두 끝나지 않음"
|
65
|
-
'true': "
|
66
|
-
'false': "
|
67
|
-
present: "
|
68
|
-
blank: "
|
69
|
-
'null': "
|
70
|
-
not_null: "
|
65
|
+
'true': "참"
|
66
|
+
'false': "거짓"
|
67
|
+
present: "존재함"
|
68
|
+
blank: "비어있음"
|
69
|
+
'null': "널"
|
70
|
+
not_null: "널이 아님"
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Інші переклади на https://github.com/activerecord-hackery/ransack/blob/main/lib/ransack/locale
|
2
|
+
#
|
3
|
+
uk:
|
4
|
+
ransack:
|
5
|
+
search: пошук
|
6
|
+
predicate: предикат
|
7
|
+
and: і
|
8
|
+
or: або
|
9
|
+
any: будь-який
|
10
|
+
all: усі
|
11
|
+
combinator: комбінатор
|
12
|
+
attribute: атрибут
|
13
|
+
value: значення
|
14
|
+
condition: умова
|
15
|
+
sort: сортування
|
16
|
+
asc: за зростанням
|
17
|
+
desc: за спаданням
|
18
|
+
predicates:
|
19
|
+
eq: рівний
|
20
|
+
eq_any: рівний будь-якому
|
21
|
+
eq_all: рівний усім
|
22
|
+
not_eq: не рівний
|
23
|
+
not_eq_any: не рівний будь-якому
|
24
|
+
not_eq_all: не рівний усім
|
25
|
+
matches: збігається
|
26
|
+
matches_any: збігається з будь-яким
|
27
|
+
matches_all: збігається з усіма
|
28
|
+
does_not_match: не збігається
|
29
|
+
does_not_match_any: не збігається з будь-яким
|
30
|
+
does_not_match_all: не збігається з усіма
|
31
|
+
lt: менше ніж
|
32
|
+
lt_any: менше за будь-який
|
33
|
+
lt_all: менше за всі
|
34
|
+
lteq: менше або рівне
|
35
|
+
lteq_any: менше або рівне будь-якому
|
36
|
+
lteq_all: менше або рівне всім
|
37
|
+
gt: більше ніж
|
38
|
+
gt_any: більше ніж будь-який
|
39
|
+
gt_all: більше ніж усі
|
40
|
+
gteq: більше або рівне
|
41
|
+
gteq_any: більше або рівне будь-якому
|
42
|
+
gteq_all: більше або рівне всім
|
43
|
+
in: міститься у
|
44
|
+
in_any: міститься в будь-якому
|
45
|
+
in_all: міститься в усіх
|
46
|
+
not_in: не міститься у
|
47
|
+
not_in_any: не міститься в будь-якому
|
48
|
+
not_in_all: не міститься в усіх
|
49
|
+
cont: містить
|
50
|
+
cont_any: містить будь-який
|
51
|
+
cont_all: містить усі
|
52
|
+
not_cont: не містить
|
53
|
+
not_cont_any: не містить жодного
|
54
|
+
not_cont_all: не містить усіх
|
55
|
+
start: починається з
|
56
|
+
start_any: починається з будь-якого
|
57
|
+
start_all: починається з усіх
|
58
|
+
not_start: не починається з
|
59
|
+
not_start_any: не починається з будь-якого
|
60
|
+
not_start_all: не починається з усіх
|
61
|
+
end: закінчується на
|
62
|
+
end_any: закінчується на будь-який
|
63
|
+
end_all: закінчується на всі
|
64
|
+
not_end: не закінчується на
|
65
|
+
not_end_any: не закінчується на будь-який
|
66
|
+
not_end_all: не закінчується на всі
|
67
|
+
'true': так
|
68
|
+
'false': ні
|
69
|
+
present: присутній
|
70
|
+
blank: порожній
|
71
|
+
'null': нульовий
|
72
|
+
not_null: не нульовий
|
@@ -226,7 +226,7 @@ module Ransack
|
|
226
226
|
end
|
227
227
|
|
228
228
|
def casted_values_for_attribute(attr)
|
229
|
-
validated_values.map
|
229
|
+
validated_values.map(&:cast_array)
|
230
230
|
end
|
231
231
|
|
232
232
|
def formatted_values_for_attribute(attr)
|
@@ -235,6 +235,9 @@ module Ransack
|
|
235
235
|
val = attr.ransacker.formatter.call(val)
|
236
236
|
end
|
237
237
|
val = predicate.format(val)
|
238
|
+
if val.is_a?(String) && val.include?('%')
|
239
|
+
val = Arel::Nodes::Quoted.new(val)
|
240
|
+
end
|
238
241
|
val
|
239
242
|
end
|
240
243
|
if predicate.wants_array
|
@@ -288,12 +291,24 @@ module Ransack
|
|
288
291
|
def arel_predicate
|
289
292
|
predicate = attributes.map { |attribute|
|
290
293
|
association = attribute.parent
|
291
|
-
|
294
|
+
parent_table = association.table
|
295
|
+
|
296
|
+
if negative? && attribute.associated_collection? && not_nested_condition(attribute, parent_table)
|
292
297
|
query = context.build_correlated_subquery(association)
|
293
298
|
context.remove_association(association)
|
294
|
-
|
295
|
-
|
296
|
-
|
299
|
+
|
300
|
+
case self.predicate_name
|
301
|
+
when 'not_null'
|
302
|
+
if self.value
|
303
|
+
query.where(format_predicate(attribute))
|
304
|
+
Arel::Nodes::In.new(context.primary_key, Arel.sql(query.to_sql))
|
305
|
+
else
|
306
|
+
query.where(format_predicate(attribute).not)
|
307
|
+
Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
|
308
|
+
end
|
309
|
+
when 'not_cont'
|
310
|
+
query.where(attribute.attr.matches(formatted_values_for_attribute(attribute)))
|
311
|
+
Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
|
297
312
|
else
|
298
313
|
query.where(format_predicate(attribute).not)
|
299
314
|
Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
|
@@ -315,6 +330,10 @@ module Ransack
|
|
315
330
|
predicate
|
316
331
|
end
|
317
332
|
|
333
|
+
def not_nested_condition(attribute, parent_table)
|
334
|
+
parent_table.class != Arel::Nodes::TableAlias && attribute.name.starts_with?(parent_table.name)
|
335
|
+
end
|
336
|
+
|
318
337
|
private
|
319
338
|
|
320
339
|
def combinator_method
|
@@ -324,6 +343,13 @@ module Ransack
|
|
324
343
|
def format_predicate(attribute)
|
325
344
|
arel_pred = arel_predicate_for_attribute(attribute)
|
326
345
|
arel_values = formatted_values_for_attribute(attribute)
|
346
|
+
|
347
|
+
# For LIKE predicates, wrap the value in Arel::Nodes.build_quoted to prevent
|
348
|
+
# ActiveRecord normalization from affecting wildcard patterns
|
349
|
+
if like_predicate?(arel_pred)
|
350
|
+
arel_values = Arel::Nodes.build_quoted(arel_values)
|
351
|
+
end
|
352
|
+
|
327
353
|
predicate = attr_value_for_attribute(attribute).public_send(arel_pred, arel_values)
|
328
354
|
|
329
355
|
if in_predicate?(predicate)
|
@@ -340,8 +366,12 @@ module Ransack
|
|
340
366
|
predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
|
341
367
|
end
|
342
368
|
|
369
|
+
def like_predicate?(arel_predicate)
|
370
|
+
arel_predicate == 'matches' || arel_predicate == 'does_not_match'
|
371
|
+
end
|
372
|
+
|
343
373
|
def casted_array?(predicate)
|
344
|
-
predicate.
|
374
|
+
predicate.is_a?(Arel::Nodes::Casted) && predicate.value.is_a?(Array)
|
345
375
|
end
|
346
376
|
|
347
377
|
def format_values_for(predicate)
|
data/lib/ransack/nodes/sort.rb
CHANGED
data/lib/ransack/nodes/value.rb
CHANGED
@@ -43,6 +43,14 @@ module Ransack
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
def cast_array
|
47
|
+
if value.is_a?(Array)
|
48
|
+
cast_to_date(value)
|
49
|
+
else
|
50
|
+
value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
46
54
|
def cast_to_date(val)
|
47
55
|
if val.respond_to?(:to_date)
|
48
56
|
val.to_date rescue nil
|
@@ -80,7 +88,7 @@ module Ransack
|
|
80
88
|
end
|
81
89
|
|
82
90
|
def cast_to_integer(val)
|
83
|
-
val.blank? ?
|
91
|
+
val.respond_to?(:to_i) && !val.blank? ? val.to_i : nil
|
84
92
|
end
|
85
93
|
|
86
94
|
def cast_to_float(val)
|
data/lib/ransack/search.rb
CHANGED
data/lib/ransack/version.rb
CHANGED
data/lib/ransack.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
require 'active_support/dependencies/autoload'
|
2
|
+
require 'active_support/deprecation'
|
3
|
+
require 'active_support/version'
|
4
|
+
|
5
|
+
if ::ActiveSupport.version >= ::Gem::Version.new("7.1")
|
6
|
+
require 'active_support/deprecator'
|
7
|
+
end
|
8
|
+
|
1
9
|
require 'active_support/core_ext'
|
2
10
|
require 'ransack/configuration'
|
3
11
|
require 'polyamorous/polyamorous'
|