ransack 4.1.1 → 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 +5 -3
- data/lib/polyamorous/activerecord/join_association_7_2.rb +55 -0
- data/lib/polyamorous/polyamorous.rb +5 -1
- data/lib/ransack/adapters/active_record/context.rb +32 -5
- data/lib/ransack/constants.rb +1 -1
- data/lib/ransack/context.rb +7 -4
- data/lib/ransack/helpers/form_builder.rb +6 -7
- data/lib/ransack/helpers/form_helper.rb +86 -20
- data/lib/ransack/invalid_search_error.rb +3 -0
- data/lib/ransack/locale/ja.yml +51 -51
- data/lib/ransack/locale/ko.yml +70 -0
- data/lib/ransack/locale/uk.yml +72 -0
- data/lib/ransack/nodes/condition.rb +39 -7
- 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 +4 -3
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack.rb +9 -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 +106 -3
- 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 +230 -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 +16 -9
- data/spec/ransack/search_spec.rb +121 -1
- data/spec/ransack/translate_spec.rb +0 -1
- data/spec/spec_helper.rb +2 -3
- data/spec/support/schema.rb +42 -0
- metadata +17 -86
- 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 -99
- 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 -131
- data/.gitignore +0 -7
- data/.nojekyll +0 -0
- data/.rubocop.yml +0 -50
- data/CHANGELOG.md +0 -1176
- data/CONTRIBUTING.md +0 -171
- data/Gemfile +0 -53
- 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 -288
- 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 -428
- 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 -8879
- data/ransack.gemspec +0 -26
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ransack/invalid_search_error'
|
2
|
+
|
1
3
|
module Ransack
|
2
4
|
module Nodes
|
3
5
|
class Condition < Node
|
@@ -38,7 +40,7 @@ module Ransack
|
|
38
40
|
predicate = Predicate.named(name)
|
39
41
|
|
40
42
|
unless predicate || Ransack.options[:ignore_unknown_conditions]
|
41
|
-
raise
|
43
|
+
raise InvalidSearchError, "No valid predicate for #{key}"
|
42
44
|
end
|
43
45
|
|
44
46
|
if context.present?
|
@@ -224,7 +226,7 @@ module Ransack
|
|
224
226
|
end
|
225
227
|
|
226
228
|
def casted_values_for_attribute(attr)
|
227
|
-
validated_values.map
|
229
|
+
validated_values.map(&:cast_array)
|
228
230
|
end
|
229
231
|
|
230
232
|
def formatted_values_for_attribute(attr)
|
@@ -233,6 +235,9 @@ module Ransack
|
|
233
235
|
val = attr.ransacker.formatter.call(val)
|
234
236
|
end
|
235
237
|
val = predicate.format(val)
|
238
|
+
if val.is_a?(String) && val.include?('%')
|
239
|
+
val = Arel::Nodes::Quoted.new(val)
|
240
|
+
end
|
236
241
|
val
|
237
242
|
end
|
238
243
|
if predicate.wants_array
|
@@ -286,12 +291,24 @@ module Ransack
|
|
286
291
|
def arel_predicate
|
287
292
|
predicate = attributes.map { |attribute|
|
288
293
|
association = attribute.parent
|
289
|
-
|
294
|
+
parent_table = association.table
|
295
|
+
|
296
|
+
if negative? && attribute.associated_collection? && not_nested_condition(attribute, parent_table)
|
290
297
|
query = context.build_correlated_subquery(association)
|
291
298
|
context.remove_association(association)
|
292
|
-
|
293
|
-
|
294
|
-
|
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))
|
295
312
|
else
|
296
313
|
query.where(format_predicate(attribute).not)
|
297
314
|
Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
|
@@ -313,6 +330,10 @@ module Ransack
|
|
313
330
|
predicate
|
314
331
|
end
|
315
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
|
+
|
316
337
|
private
|
317
338
|
|
318
339
|
def combinator_method
|
@@ -322,6 +343,13 @@ module Ransack
|
|
322
343
|
def format_predicate(attribute)
|
323
344
|
arel_pred = arel_predicate_for_attribute(attribute)
|
324
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
|
+
|
325
353
|
predicate = attr_value_for_attribute(attribute).public_send(arel_pred, arel_values)
|
326
354
|
|
327
355
|
if in_predicate?(predicate)
|
@@ -338,8 +366,12 @@ module Ransack
|
|
338
366
|
predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
|
339
367
|
end
|
340
368
|
|
369
|
+
def like_predicate?(arel_predicate)
|
370
|
+
arel_predicate == 'matches' || arel_predicate == 'does_not_match'
|
371
|
+
end
|
372
|
+
|
341
373
|
def casted_array?(predicate)
|
342
|
-
predicate.
|
374
|
+
predicate.is_a?(Arel::Nodes::Casted) && predicate.value.is_a?(Array)
|
343
375
|
end
|
344
376
|
|
345
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
@@ -7,6 +7,7 @@ require 'ransack/nodes/sort'
|
|
7
7
|
require 'ransack/nodes/grouping'
|
8
8
|
require 'ransack/context'
|
9
9
|
require 'ransack/naming'
|
10
|
+
require 'ransack/invalid_search_error'
|
10
11
|
|
11
12
|
module Ransack
|
12
13
|
class Search
|
@@ -53,7 +54,7 @@ module Ransack
|
|
53
54
|
elsif base.attribute_method?(key)
|
54
55
|
base.send("#{key}=", value)
|
55
56
|
elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions
|
56
|
-
raise
|
57
|
+
raise InvalidSearchError, "Invalid search term #{key}"
|
57
58
|
end
|
58
59
|
end
|
59
60
|
self
|
@@ -68,7 +69,7 @@ module Ransack
|
|
68
69
|
else
|
69
70
|
sort = Nodes::Sort.extract(@context, sort)
|
70
71
|
end
|
71
|
-
self.sorts << sort
|
72
|
+
self.sorts << sort if sort
|
72
73
|
end
|
73
74
|
when Hash
|
74
75
|
args.each do |index, attrs|
|
@@ -78,7 +79,7 @@ module Ransack
|
|
78
79
|
when String
|
79
80
|
self.sorts = [args]
|
80
81
|
else
|
81
|
-
raise
|
82
|
+
raise InvalidSearchError,
|
82
83
|
"Invalid argument (#{args.class}) supplied to sorts="
|
83
84
|
end
|
84
85
|
end
|
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'
|
@@ -21,6 +29,7 @@ require 'ransack/ransacker'
|
|
21
29
|
require 'ransack/translate'
|
22
30
|
require 'ransack/active_record'
|
23
31
|
require 'ransack/context'
|
32
|
+
require 'ransack/version'
|
24
33
|
|
25
34
|
ActiveSupport.on_load(:action_controller) do
|
26
35
|
require 'ransack/helpers'
|
@@ -4,7 +4,6 @@ module Ransack
|
|
4
4
|
module Adapters
|
5
5
|
module ActiveRecord
|
6
6
|
describe Base do
|
7
|
-
|
8
7
|
subject { ::ActiveRecord::Base }
|
9
8
|
|
10
9
|
it { should respond_to :ransack }
|
@@ -124,17 +123,20 @@ module Ransack
|
|
124
123
|
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
|
125
124
|
end
|
126
125
|
end
|
127
|
-
|
128
126
|
end
|
129
127
|
|
130
128
|
it 'does not raise exception for string :params argument' do
|
131
129
|
expect { Person.ransack('') }.to_not raise_error
|
132
130
|
end
|
133
131
|
|
134
|
-
it 'raises exception if ransack! called with unknown condition' do
|
132
|
+
it 'raises ArgumentError exception if ransack! called with unknown condition' do
|
135
133
|
expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error(ArgumentError)
|
136
134
|
end
|
137
135
|
|
136
|
+
it 'raises InvalidSearchError exception if ransack! called with unknown condition' do
|
137
|
+
expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error(InvalidSearchError)
|
138
|
+
end
|
139
|
+
|
138
140
|
it 'does not modify the parameters' do
|
139
141
|
params = { name_eq: '' }
|
140
142
|
expect { Person.ransack(params) }.not_to change { params }
|
@@ -191,6 +193,41 @@ module Ransack
|
|
191
193
|
end
|
192
194
|
end
|
193
195
|
|
196
|
+
context 'negative conditions on related object with HABTM associations' do
|
197
|
+
let(:medieval) { Tag.create!(name: 'Medieval') }
|
198
|
+
let(:fantasy) { Tag.create!(name: 'Fantasy') }
|
199
|
+
let(:arthur) { Article.create!(title: 'King Arthur') }
|
200
|
+
let(:marco) { Article.create!(title: 'Marco Polo') }
|
201
|
+
let(:comment_arthur) { marco.comments.create!(body: 'King Arthur comment') }
|
202
|
+
let(:comment_marco) { arthur.comments.create!(body: 'Marco Polo comment') }
|
203
|
+
|
204
|
+
before do
|
205
|
+
comment_arthur.tags << medieval
|
206
|
+
comment_marco.tags << fantasy
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'removes redundant joins from top query' do
|
210
|
+
s = Article.ransack(comments_tags_name_not_eq: "Fantasy")
|
211
|
+
sql = s.result.to_sql
|
212
|
+
expect(sql).to include('LEFT OUTER JOIN')
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'handles != for single values' do
|
216
|
+
s = Article.ransack(comments_tags_name_not_eq: "Fantasy")
|
217
|
+
articles = s.result.to_a
|
218
|
+
expect(articles).to include marco
|
219
|
+
expect(articles).to_not include arthur
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'handles NOT IN for multiple attributes' do
|
223
|
+
s = Article.ransack(comments_tags_name_not_in: ["Fantasy", "Scifi"])
|
224
|
+
articles = s.result.to_a
|
225
|
+
|
226
|
+
expect(articles).to include marco
|
227
|
+
expect(articles).to_not include arthur
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
194
231
|
context 'negative conditions on self-referenced associations' do
|
195
232
|
let(:pop) { Person.create!(name: 'Grandpa') }
|
196
233
|
let(:dad) { Person.create!(name: 'Father') }
|
@@ -374,6 +411,63 @@ module Ransack
|
|
374
411
|
expect(s.result.to_a).to eq [p]
|
375
412
|
end
|
376
413
|
|
414
|
+
if ::ActiveRecord::VERSION::MAJOR >= 7 && ActiveRecord::Base.respond_to?(:normalizes)
|
415
|
+
context 'with ActiveRecord::normalizes' do
|
416
|
+
around(:each) do |example|
|
417
|
+
# Create a temporary model class with normalization for testing
|
418
|
+
test_class = Class.new(ActiveRecord::Base) do
|
419
|
+
self.table_name = 'people'
|
420
|
+
normalizes :name, with: ->(name) { name.gsub(/[^a-z0-9]/, '_') }
|
421
|
+
|
422
|
+
def self.ransackable_attributes(auth_object = nil)
|
423
|
+
Person.ransackable_attributes(auth_object)
|
424
|
+
end
|
425
|
+
|
426
|
+
def self.name
|
427
|
+
'TestPersonWithNormalization'
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
stub_const('TestPersonWithNormalization', test_class)
|
432
|
+
example.run
|
433
|
+
end
|
434
|
+
|
435
|
+
it 'should not apply normalization to LIKE wildcards for cont predicate' do
|
436
|
+
# Create a person with characters that would be normalized
|
437
|
+
p = TestPersonWithNormalization.create!(name: 'foo%bar')
|
438
|
+
expect(p.reload.name).to eq('foo_bar') # Verify normalization happened on storage
|
439
|
+
|
440
|
+
# Search should find the person using the original search term
|
441
|
+
s = TestPersonWithNormalization.ransack(name_cont: 'foo')
|
442
|
+
expect(s.result.to_a).to eq [p]
|
443
|
+
|
444
|
+
# Verify the SQL contains proper LIKE wildcards, not normalized ones
|
445
|
+
sql = s.result.to_sql
|
446
|
+
expect(sql).to include("LIKE '%foo%'")
|
447
|
+
expect(sql).not_to include("LIKE '_foo_'")
|
448
|
+
end
|
449
|
+
|
450
|
+
it 'should not apply normalization to LIKE wildcards for other LIKE predicates' do
|
451
|
+
p = TestPersonWithNormalization.create!(name: 'foo%bar')
|
452
|
+
|
453
|
+
# Test start predicate
|
454
|
+
s = TestPersonWithNormalization.ransack(name_start: 'foo')
|
455
|
+
expect(s.result.to_a).to eq [p]
|
456
|
+
expect(s.result.to_sql).to include("LIKE 'foo%'")
|
457
|
+
|
458
|
+
# Test end predicate
|
459
|
+
s = TestPersonWithNormalization.ransack(name_end: 'bar')
|
460
|
+
expect(s.result.to_a).to eq [p]
|
461
|
+
expect(s.result.to_sql).to include("LIKE '%bar'")
|
462
|
+
|
463
|
+
# Test i_cont predicate
|
464
|
+
s = TestPersonWithNormalization.ransack(name_i_cont: 'FOO')
|
465
|
+
expect(s.result.to_a).to eq [p]
|
466
|
+
expect(s.result.to_sql).to include("LIKE '%foo%'")
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
377
471
|
context 'searching by underscores' do
|
378
472
|
# when escaping is supported right in LIKE expression without adding extra expressions
|
379
473
|
def self.simple_escaping?
|
@@ -408,6 +502,15 @@ module Ransack
|
|
408
502
|
expect(s.result.map(&:id)).to eq [3, 2, 1]
|
409
503
|
end
|
410
504
|
|
505
|
+
it 'should function correctly with HABTM associations' do
|
506
|
+
article = Article.first
|
507
|
+
tag = article.tags.first
|
508
|
+
s = Person.ransack(article_tags_in: [tag.id])
|
509
|
+
|
510
|
+
expect(s.result.count).to be 1
|
511
|
+
expect(s.result.map(&:id)).to eq [article.person.id]
|
512
|
+
end
|
513
|
+
|
411
514
|
it 'should function correctly when passing an array of strings' do
|
412
515
|
a, b = Person.select(:id).order(:id).limit(2).map { |a| a.id.to_s }
|
413
516
|
|
@@ -97,6 +97,62 @@ module Ransack
|
|
97
97
|
|
98
98
|
expect(search.result.to_sql).to match /.comments.\..person_id. = .people.\..id./
|
99
99
|
end
|
100
|
+
|
101
|
+
it 'handles Arel::Nodes::And with children' do
|
102
|
+
# Create a mock Arel::Nodes::And with children for testing
|
103
|
+
search = Search.new(Person, { articles_title_not_eq: 'some_title', articles_body_not_eq: 'some_body' }, context: subject)
|
104
|
+
attribute = search.conditions.first.attributes.first
|
105
|
+
constraints = subject.build_correlated_subquery(attribute.parent).constraints
|
106
|
+
constraint = constraints.first
|
107
|
+
|
108
|
+
expect(constraints.length).to eql 1
|
109
|
+
expect(constraint.left.name).to eql 'person_id'
|
110
|
+
expect(constraint.left.relation.name).to eql 'articles'
|
111
|
+
expect(constraint.right.name).to eql 'id'
|
112
|
+
expect(constraint.right.relation.name).to eql 'people'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'correctly extracts correlated key from complex AND conditions' do
|
116
|
+
# Test with multiple nested conditions to ensure the children traversal works
|
117
|
+
search = Search.new(
|
118
|
+
Person,
|
119
|
+
{
|
120
|
+
articles_title_not_eq: 'title',
|
121
|
+
articles_body_not_eq: 'body',
|
122
|
+
articles_published_eq: true
|
123
|
+
},
|
124
|
+
context: subject
|
125
|
+
)
|
126
|
+
|
127
|
+
attribute = search.conditions.first.attributes.first
|
128
|
+
constraints = subject.build_correlated_subquery(attribute.parent).constraints
|
129
|
+
constraint = constraints.first
|
130
|
+
|
131
|
+
expect(constraints.length).to eql 1
|
132
|
+
expect(constraint.left.relation.name).to eql 'articles'
|
133
|
+
expect(constraint.left.name).to eql 'person_id'
|
134
|
+
expect(constraint.right.relation.name).to eql 'people'
|
135
|
+
expect(constraint.right.name).to eql 'id'
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'build correlated subquery for polymorphic & default_scope when predicate is not_cont_all' do
|
139
|
+
search = Search.new(Article,
|
140
|
+
g: [
|
141
|
+
{
|
142
|
+
m: "and",
|
143
|
+
c: [
|
144
|
+
{
|
145
|
+
a: ["recent_notes_note"],
|
146
|
+
p: "not_eq",
|
147
|
+
v: ["some_note"],
|
148
|
+
}
|
149
|
+
]
|
150
|
+
}
|
151
|
+
],
|
152
|
+
)
|
153
|
+
|
154
|
+
expect(search.result.to_sql).to match /(.notes.\..note. != \'some_note\')/
|
155
|
+
end
|
100
156
|
end
|
101
157
|
|
102
158
|
describe 'sharing context across searches' do
|
@@ -141,6 +197,22 @@ module Ransack
|
|
141
197
|
expect(attribute.relation.table_alias).to be_nil
|
142
198
|
end
|
143
199
|
|
200
|
+
describe '#type_for' do
|
201
|
+
it 'returns nil when column does not exist instead of raising NoMethodError' do
|
202
|
+
# Create a mock attribute that references a non-existent column
|
203
|
+
mock_attr = double('attribute')
|
204
|
+
allow(mock_attr).to receive(:valid?).and_return(true)
|
205
|
+
|
206
|
+
mock_arel_attr = double('arel_attribute')
|
207
|
+
allow(mock_arel_attr).to receive(:relation).and_return(Person.arel_table)
|
208
|
+
allow(mock_arel_attr).to receive(:name).and_return('nonexistent_column')
|
209
|
+
allow(mock_attr).to receive(:arel_attribute).and_return(mock_arel_attr)
|
210
|
+
allow(mock_attr).to receive(:klass).and_return(Person)
|
211
|
+
|
212
|
+
# This should return nil instead of raising an error
|
213
|
+
expect(subject.type_for(mock_attr)).to be_nil
|
214
|
+
end
|
215
|
+
end
|
144
216
|
end
|
145
217
|
end
|
146
218
|
end
|
@@ -3,7 +3,6 @@ require 'spec_helper'
|
|
3
3
|
module Ransack
|
4
4
|
module Helpers
|
5
5
|
describe FormBuilder do
|
6
|
-
|
7
6
|
router = ActionDispatch::Routing::RouteSet.new
|
8
7
|
router.draw do
|
9
8
|
resources :people, :comments, :notes
|
@@ -165,7 +164,6 @@ module Ransack
|
|
165
164
|
def date_select_html(val)
|
166
165
|
%(<option value="#{val}" selected="selected">#{val}</option>)
|
167
166
|
end
|
168
|
-
|
169
167
|
end
|
170
168
|
end
|
171
169
|
end
|