ransack 2.4.2 → 4.0.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/.github/workflows/codeql.yml +72 -0
- data/.github/workflows/cronjob.yml +6 -9
- data/.github/workflows/deploy.yml +35 -0
- data/.github/workflows/rubocop.yml +1 -1
- data/.github/workflows/test-deploy.yml +29 -0
- data/.github/workflows/test.yml +22 -48
- data/.nojekyll +0 -0
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +208 -11
- data/CONTRIBUTING.md +41 -18
- data/Gemfile +10 -10
- data/README.md +44 -977
- data/bug_report_templates/test-ransacker-arel-present-predicate.rb +4 -0
- data/docs/.gitignore +19 -0
- data/docs/.nojekyll +0 -0
- data/docs/babel.config.js +3 -0
- data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
- data/docs/docs/getting-started/_category_.json +4 -0
- data/docs/docs/getting-started/advanced-mode.md +46 -0
- data/docs/docs/getting-started/configuration.md +47 -0
- data/docs/docs/getting-started/search-matches.md +67 -0
- data/docs/docs/getting-started/simple-mode.md +288 -0
- data/docs/docs/getting-started/sorting.md +79 -0
- data/docs/docs/getting-started/using-predicates.md +282 -0
- data/docs/docs/going-further/_category_.json +4 -0
- data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
- data/docs/docs/going-further/associations.md +70 -0
- data/docs/docs/going-further/custom-predicates.md +52 -0
- data/docs/docs/going-further/documentation.md +43 -0
- data/docs/docs/going-further/exporting-to-csv.md +49 -0
- data/docs/docs/going-further/external-guides.md +57 -0
- data/docs/docs/going-further/form-customisation.md +63 -0
- data/docs/docs/going-further/i18n.md +53 -0
- data/docs/docs/going-further/merging-searches.md +41 -0
- data/docs/docs/going-further/other-notes.md +428 -0
- data/docs/docs/going-further/polymorphic-search.md +40 -0
- data/docs/docs/going-further/ransackers.md +331 -0
- data/docs/docs/going-further/release_process.md +36 -0
- data/docs/docs/going-further/saving-queries.md +82 -0
- data/docs/docs/going-further/searching-postgres.md +57 -0
- data/docs/docs/going-further/wiki-contributors.md +82 -0
- data/docs/docs/intro.md +99 -0
- data/docs/docusaurus.config.js +120 -0
- data/docs/package.json +42 -0
- data/docs/sidebars.js +31 -0
- data/docs/src/components/HomepageFeatures/index.js +64 -0
- data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- data/docs/src/css/custom.css +39 -0
- data/docs/src/pages/index.module.css +23 -0
- data/docs/src/pages/markdown-page.md +7 -0
- data/docs/static/.nojekyll +0 -0
- data/docs/static/img/docusaurus.png +0 -0
- data/docs/static/img/favicon.ico +0 -0
- data/docs/static/img/logo.svg +1 -0
- data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
- data/docs/static/img/tutorial/localeDropdown.png +0 -0
- data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
- data/docs/static/img/undraw_docusaurus_react.svg +170 -0
- data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- data/docs/yarn.lock +8790 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -4
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -1
- data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -1
- data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
- data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
- data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
- data/lib/ransack/adapters/active_record/base.rb +79 -10
- data/lib/ransack/adapters/active_record/context.rb +24 -51
- data/lib/ransack/configuration.rb +39 -12
- data/lib/ransack/constants.rb +125 -3
- data/lib/ransack/context.rb +34 -5
- data/lib/ransack/helpers/form_builder.rb +3 -3
- data/lib/ransack/helpers/form_helper.rb +14 -5
- data/lib/ransack/locale/sv.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +2 -2
- data/lib/ransack/nodes/condition.rb +80 -7
- data/lib/ransack/nodes/grouping.rb +3 -3
- data/lib/ransack/nodes/node.rb +1 -1
- data/lib/ransack/nodes/sort.rb +2 -2
- data/lib/ransack/nodes/value.rb +2 -2
- data/lib/ransack/predicate.rb +1 -1
- data/lib/ransack/ransacker.rb +1 -1
- data/lib/ransack/search.rb +13 -7
- data/lib/ransack/translate.rb +3 -3
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +38 -2
- data/lib/ransack.rb +3 -6
- data/ransack.gemspec +5 -5
- data/spec/helpers/polyamorous_helper.rb +2 -8
- data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
- data/spec/polyamorous/join_association_spec.rb +1 -6
- data/spec/polyamorous/join_dependency_spec.rb +0 -16
- data/spec/ransack/adapters/active_record/base_spec.rb +101 -11
- data/spec/ransack/configuration_spec.rb +23 -9
- data/spec/ransack/helpers/form_builder_spec.rb +8 -8
- data/spec/ransack/helpers/form_helper_spec.rb +93 -4
- data/spec/ransack/nodes/condition_spec.rb +37 -0
- data/spec/ransack/nodes/value_spec.rb +115 -0
- data/spec/ransack/predicate_spec.rb +36 -1
- data/spec/ransack/search_spec.rb +140 -27
- data/spec/ransack/translate_spec.rb +1 -1
- data/spec/support/schema.rb +75 -9
- metadata +83 -37
- data/docs/release_process.md +0 -20
- data/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +0 -24
- data/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -79
- data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -11
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -1
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +0 -80
- data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -1
- data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -128
- data/lib/ransack/adapters/active_record/ransack/context.rb +0 -56
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +0 -68
- data/lib/ransack/adapters/active_record/ransack/translate.rb +0 -8
- data/lib/ransack/adapters/active_record/ransack/visitor.rb +0 -47
- data/lib/ransack/adapters.rb +0 -64
- data/lib/ransack/nodes.rb +0 -8
- /data/docs/{img → docs/going-further/img}/create_release.png +0 -0
- /data/{logo → docs/static/logo}/ransack-h.png +0 -0
- /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
- /data/{logo → docs/static/logo}/ransack-v.png +0 -0
- /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
- /data/{logo → docs/static/logo}/ransack.png +0 -0
- /data/{logo → docs/static/logo}/ransack.svg +0 -0
- /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_association.rb +0 -0
- /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_dependency.rb +0 -0
- /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/reflection.rb +0 -0
- /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
|
@@ -35,6 +35,13 @@ module Ransack
|
|
|
35
35
|
@s.awesome_eq = nil
|
|
36
36
|
expect(@s.result.to_sql).not_to match /WHERE/
|
|
37
37
|
end
|
|
38
|
+
|
|
39
|
+
it 'generates a = condition with a huge integer value' do
|
|
40
|
+
val = 123456789012345678901
|
|
41
|
+
@s.salary_eq = val
|
|
42
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
|
43
|
+
expect(@s.result.to_sql).to match /#{field} = #{val}/
|
|
44
|
+
end
|
|
38
45
|
end
|
|
39
46
|
|
|
40
47
|
describe 'lteq' do
|
|
@@ -56,6 +63,13 @@ module Ransack
|
|
|
56
63
|
@s.salary_lteq = nil
|
|
57
64
|
expect(@s.result.to_sql).not_to match /WHERE/
|
|
58
65
|
end
|
|
66
|
+
|
|
67
|
+
it 'generates a <= condition with a huge integer value' do
|
|
68
|
+
val = 123456789012345678901
|
|
69
|
+
@s.salary_lteq = val
|
|
70
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
|
71
|
+
expect(@s.result.to_sql).to match /#{field} <= #{val}/
|
|
72
|
+
end
|
|
59
73
|
end
|
|
60
74
|
|
|
61
75
|
describe 'lt' do
|
|
@@ -77,6 +91,13 @@ module Ransack
|
|
|
77
91
|
@s.salary_lt = nil
|
|
78
92
|
expect(@s.result.to_sql).not_to match /WHERE/
|
|
79
93
|
end
|
|
94
|
+
|
|
95
|
+
it 'generates a = condition with a huge integer value' do
|
|
96
|
+
val = 123456789012345678901
|
|
97
|
+
@s.salary_lt = val
|
|
98
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
|
99
|
+
expect(@s.result.to_sql).to match /#{field} < #{val}/
|
|
100
|
+
end
|
|
80
101
|
end
|
|
81
102
|
|
|
82
103
|
describe 'gteq' do
|
|
@@ -98,6 +119,13 @@ module Ransack
|
|
|
98
119
|
@s.salary_gteq = nil
|
|
99
120
|
expect(@s.result.to_sql).not_to match /WHERE/
|
|
100
121
|
end
|
|
122
|
+
|
|
123
|
+
it 'generates a >= condition with a huge integer value' do
|
|
124
|
+
val = 123456789012345678901
|
|
125
|
+
@s.salary_gteq = val
|
|
126
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
|
127
|
+
expect(@s.result.to_sql).to match /#{field} >= #{val}/
|
|
128
|
+
end
|
|
101
129
|
end
|
|
102
130
|
|
|
103
131
|
describe 'gt' do
|
|
@@ -119,6 +147,13 @@ module Ransack
|
|
|
119
147
|
@s.salary_gt = nil
|
|
120
148
|
expect(@s.result.to_sql).not_to match /WHERE/
|
|
121
149
|
end
|
|
150
|
+
|
|
151
|
+
it 'generates a > condition with a huge integer value' do
|
|
152
|
+
val = 123456789012345678901
|
|
153
|
+
@s.salary_gt = val
|
|
154
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
|
155
|
+
expect(@s.result.to_sql).to match /#{field} > #{val}/
|
|
156
|
+
end
|
|
122
157
|
end
|
|
123
158
|
|
|
124
159
|
describe 'cont' do
|
|
@@ -368,7 +403,7 @@ module Ransack
|
|
|
368
403
|
expect(@s.result.to_sql).to match /#{field} IS NULL/
|
|
369
404
|
end
|
|
370
405
|
|
|
371
|
-
describe 'with association
|
|
406
|
+
describe 'with association query' do
|
|
372
407
|
it 'generates a value IS NOT NULL query' do
|
|
373
408
|
@s.comments_id_not_null = true
|
|
374
409
|
sql = @s.result.to_sql
|
data/spec/ransack/search_spec.rb
CHANGED
|
@@ -20,10 +20,42 @@ module Ransack
|
|
|
20
20
|
Search.new(Person, name_eq: 'foobar')
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
context 'whitespace stripping' do
|
|
24
|
+
context 'when whitespace_strip option is true' do
|
|
25
|
+
before do
|
|
26
|
+
Ransack.configure { |c| c.strip_whitespace = true }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'strips leading & trailing whitespace before building' do
|
|
30
|
+
expect_any_instance_of(Search).to receive(:build)
|
|
31
|
+
.with({ 'name_eq' => 'foobar' })
|
|
32
|
+
Search.new(Person, name_eq: ' foobar ')
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'when whitespace_strip option is false' do
|
|
37
|
+
before do
|
|
38
|
+
Ransack.configure { |c| c.strip_whitespace = false }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'doesn\'t strip leading & trailing whitespace before building' do
|
|
42
|
+
expect_any_instance_of(Search).to receive(:build)
|
|
43
|
+
.with({ 'name_eq' => ' foobar ' })
|
|
44
|
+
Search.new(Person, name_eq: ' foobar ')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'strips leading & trailing whitespace when strip_whitespace search parameter is true' do
|
|
49
|
+
expect_any_instance_of(Search).to receive(:build)
|
|
50
|
+
.with({ 'name_eq' => 'foobar' })
|
|
51
|
+
Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: true })
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'doesn\'t strip leading & trailing whitespace when strip_whitespace search parameter is false' do
|
|
55
|
+
expect_any_instance_of(Search).to receive(:build)
|
|
56
|
+
.with({ 'name_eq' => ' foobar ' })
|
|
57
|
+
Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: false })
|
|
58
|
+
end
|
|
27
59
|
end
|
|
28
60
|
|
|
29
61
|
it 'removes empty suffixed conditions before building' do
|
|
@@ -280,6 +312,29 @@ module Ransack
|
|
|
280
312
|
expect { Search.new(Person, params) }.not_to change { params }
|
|
281
313
|
end
|
|
282
314
|
|
|
315
|
+
context "ransackable_scope" do
|
|
316
|
+
around(:each) do |example|
|
|
317
|
+
Person.define_singleton_method(:name_eq) do |name|
|
|
318
|
+
self.where(name: name)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
begin
|
|
322
|
+
example.run
|
|
323
|
+
ensure
|
|
324
|
+
Person.singleton_class.undef_method :name_eq
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
it "is prioritized over base predicates" do
|
|
329
|
+
allow(Person).to receive(:ransackable_scopes)
|
|
330
|
+
.and_return(Person.ransackable_scopes + [:name_eq])
|
|
331
|
+
|
|
332
|
+
s = Search.new(Person, name_eq: "Johny")
|
|
333
|
+
expect(s.instance_variable_get(:@scope_args)["name_eq"]).to eq("Johny")
|
|
334
|
+
expect(s.base[:name_eq]).to be_nil
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
283
338
|
end
|
|
284
339
|
|
|
285
340
|
describe '#result' do
|
|
@@ -300,8 +355,6 @@ module Ransack
|
|
|
300
355
|
end
|
|
301
356
|
|
|
302
357
|
it 'use appropriate table alias' do
|
|
303
|
-
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"
|
|
304
|
-
|
|
305
358
|
s = Search.new(Person, {
|
|
306
359
|
name_eq: "person_name_query",
|
|
307
360
|
articles_title_eq: "person_article_title_query",
|
|
@@ -451,82 +504,109 @@ module Ransack
|
|
|
451
504
|
expect(sort.dir).to eq 'asc'
|
|
452
505
|
end
|
|
453
506
|
|
|
454
|
-
it 'creates sorts based on
|
|
455
|
-
@s.sorts =
|
|
507
|
+
it 'creates sorts based on a single alias/direction' do
|
|
508
|
+
@s.sorts = 'daddy desc'
|
|
509
|
+
expect(@s.sorts.size).to eq(1)
|
|
510
|
+
sort = @s.sorts.first
|
|
511
|
+
expect(sort).to be_a Nodes::Sort
|
|
512
|
+
expect(sort.name).to eq 'parent_name'
|
|
513
|
+
expect(sort.dir).to eq 'desc'
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
it 'creates sorts based on a single alias and uppercase direction' do
|
|
517
|
+
@s.sorts = 'daddy DESC'
|
|
518
|
+
expect(@s.sorts.size).to eq(1)
|
|
519
|
+
sort = @s.sorts.first
|
|
520
|
+
expect(sort).to be_a Nodes::Sort
|
|
521
|
+
expect(sort.name).to eq 'parent_name'
|
|
522
|
+
expect(sort.dir).to eq 'desc'
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
it 'creates sorts based on a single alias and without direction' do
|
|
526
|
+
@s.sorts = 'daddy'
|
|
527
|
+
expect(@s.sorts.size).to eq(1)
|
|
528
|
+
sort = @s.sorts.first
|
|
529
|
+
expect(sort).to be_a Nodes::Sort
|
|
530
|
+
expect(sort.name).to eq 'parent_name'
|
|
531
|
+
expect(sort.dir).to eq 'asc'
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
it 'creates sorts based on attributes, alias and directions in array format' do
|
|
535
|
+
@s.sorts = ['id desc', { name: 'daddy', dir: 'asc' }]
|
|
456
536
|
expect(@s.sorts.size).to eq(2)
|
|
457
537
|
sort1, sort2 = @s.sorts
|
|
458
538
|
expect(sort1).to be_a Nodes::Sort
|
|
459
539
|
expect(sort1.name).to eq 'id'
|
|
460
540
|
expect(sort1.dir).to eq 'desc'
|
|
461
541
|
expect(sort2).to be_a Nodes::Sort
|
|
462
|
-
expect(sort2.name).to eq '
|
|
542
|
+
expect(sort2.name).to eq 'parent_name'
|
|
463
543
|
expect(sort2.dir).to eq 'asc'
|
|
464
544
|
end
|
|
465
545
|
|
|
466
|
-
it 'creates sorts based on
|
|
467
|
-
@s.sorts = ['id DESC', { name: '
|
|
546
|
+
it 'creates sorts based on attributes, alias and uppercase directions in array format' do
|
|
547
|
+
@s.sorts = ['id DESC', { name: 'daddy', dir: 'ASC' }]
|
|
468
548
|
expect(@s.sorts.size).to eq(2)
|
|
469
549
|
sort1, sort2 = @s.sorts
|
|
470
550
|
expect(sort1).to be_a Nodes::Sort
|
|
471
551
|
expect(sort1.name).to eq 'id'
|
|
472
552
|
expect(sort1.dir).to eq 'desc'
|
|
473
553
|
expect(sort2).to be_a Nodes::Sort
|
|
474
|
-
expect(sort2.name).to eq '
|
|
554
|
+
expect(sort2.name).to eq 'parent_name'
|
|
475
555
|
expect(sort2.dir).to eq 'asc'
|
|
476
556
|
end
|
|
477
557
|
|
|
478
|
-
it 'creates sorts based on
|
|
558
|
+
it 'creates sorts based on attributes, alias and different directions
|
|
479
559
|
in array format' do
|
|
480
|
-
@s.sorts = ['id DESC', { name: '
|
|
560
|
+
@s.sorts = ['id DESC', { name: 'daddy', dir: nil }]
|
|
481
561
|
expect(@s.sorts.size).to eq(2)
|
|
482
562
|
sort1, sort2 = @s.sorts
|
|
483
563
|
expect(sort1).to be_a Nodes::Sort
|
|
484
564
|
expect(sort1.name).to eq 'id'
|
|
485
565
|
expect(sort1.dir).to eq 'desc'
|
|
486
566
|
expect(sort2).to be_a Nodes::Sort
|
|
487
|
-
expect(sort2.name).to eq '
|
|
567
|
+
expect(sort2.name).to eq 'parent_name'
|
|
488
568
|
expect(sort2.dir).to eq 'asc'
|
|
489
569
|
end
|
|
490
570
|
|
|
491
|
-
it 'creates sorts based on
|
|
571
|
+
it 'creates sorts based on attributes, alias and directions in hash format' do
|
|
492
572
|
@s.sorts = {
|
|
493
573
|
'0' => { name: 'id', dir: 'desc' },
|
|
494
|
-
'1' => { name: '
|
|
574
|
+
'1' => { name: 'daddy', dir: 'asc' }
|
|
495
575
|
}
|
|
496
576
|
expect(@s.sorts.size).to eq(2)
|
|
497
577
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
|
498
578
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
|
499
|
-
|
|
579
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
|
500
580
|
expect(id_sort.dir).to eq 'desc'
|
|
501
|
-
expect(
|
|
581
|
+
expect(daddy_sort.dir).to eq 'asc'
|
|
502
582
|
end
|
|
503
583
|
|
|
504
|
-
it 'creates sorts based on
|
|
584
|
+
it 'creates sorts based on attributes, alias and uppercase directions
|
|
505
585
|
in hash format' do
|
|
506
586
|
@s.sorts = {
|
|
507
587
|
'0' => { name: 'id', dir: 'DESC' },
|
|
508
|
-
'1' => { name: '
|
|
588
|
+
'1' => { name: 'daddy', dir: 'ASC' }
|
|
509
589
|
}
|
|
510
590
|
expect(@s.sorts.size).to eq(2)
|
|
511
591
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
|
512
592
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
|
513
|
-
|
|
593
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
|
514
594
|
expect(id_sort.dir).to eq 'desc'
|
|
515
|
-
expect(
|
|
595
|
+
expect(daddy_sort.dir).to eq 'asc'
|
|
516
596
|
end
|
|
517
597
|
|
|
518
|
-
it 'creates sorts based on
|
|
598
|
+
it 'creates sorts based on attributes, alias and different directions
|
|
519
599
|
in hash format' do
|
|
520
600
|
@s.sorts = {
|
|
521
601
|
'0' => { name: 'id', dir: 'DESC' },
|
|
522
|
-
'1' => { name: '
|
|
602
|
+
'1' => { name: 'daddy', dir: nil }
|
|
523
603
|
}
|
|
524
604
|
expect(@s.sorts.size).to eq(2)
|
|
525
605
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
|
526
606
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
|
527
|
-
|
|
607
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
|
528
608
|
expect(id_sort.dir).to eq 'desc'
|
|
529
|
-
expect(
|
|
609
|
+
expect(daddy_sort.dir).to eq 'asc'
|
|
530
610
|
end
|
|
531
611
|
|
|
532
612
|
it 'overrides existing sort' do
|
|
@@ -554,6 +634,39 @@ module Ransack
|
|
|
554
634
|
|
|
555
635
|
Ransack.options = default
|
|
556
636
|
end
|
|
637
|
+
|
|
638
|
+
it "PG's sort option with double name", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
|
|
639
|
+
default = Ransack.options.clone
|
|
640
|
+
|
|
641
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
642
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC"
|
|
643
|
+
|
|
644
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
|
|
645
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
646
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
|
|
647
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
|
648
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
|
|
649
|
+
|
|
650
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
|
|
651
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
652
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
|
|
653
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
|
654
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
|
|
655
|
+
|
|
656
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_first }
|
|
657
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
658
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
|
|
659
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
|
660
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
|
|
661
|
+
|
|
662
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_last }
|
|
663
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
664
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
|
|
665
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
|
666
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
|
|
667
|
+
|
|
668
|
+
Ransack.options = default
|
|
669
|
+
end
|
|
557
670
|
end
|
|
558
671
|
|
|
559
672
|
describe '#method_missing' do
|
|
@@ -8,7 +8,7 @@ module Ransack
|
|
|
8
8
|
ar_translation = ::Namespace::Article.human_attribute_name(:title)
|
|
9
9
|
ransack_translation = Ransack::Translate.attribute(
|
|
10
10
|
:title,
|
|
11
|
-
:
|
|
11
|
+
context: ::Namespace::Article.ransack.context
|
|
12
12
|
)
|
|
13
13
|
expect(ransack_translation).to eq ar_translation
|
|
14
14
|
end
|
data/spec/support/schema.rb
CHANGED
|
@@ -28,7 +28,24 @@ else
|
|
|
28
28
|
)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
# This is just a test app with no sensitive data, so we explicitly allowlist all
|
|
32
|
+
# attributes and associations for search. In general, end users should
|
|
33
|
+
# explicitly authorize each model, but this shows a way to configure the
|
|
34
|
+
# unrestricted default behavior of versions prior to Ransack 4.
|
|
35
|
+
#
|
|
36
|
+
class ApplicationRecord < ActiveRecord::Base
|
|
37
|
+
self.abstract_class = true
|
|
38
|
+
|
|
39
|
+
def self.ransackable_attributes(auth_object = nil)
|
|
40
|
+
authorizable_ransackable_attributes
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.ransackable_associations(auth_object = nil)
|
|
44
|
+
authorizable_ransackable_associations
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class Person < ApplicationRecord
|
|
32
49
|
default_scope { order(id: :desc) }
|
|
33
50
|
belongs_to :parent, class_name: 'Person', foreign_key: :parent_id
|
|
34
51
|
has_many :children, class_name: 'Person', foreign_key: :parent_id
|
|
@@ -111,9 +128,9 @@ class Person < ActiveRecord::Base
|
|
|
111
128
|
|
|
112
129
|
def self.ransackable_attributes(auth_object = nil)
|
|
113
130
|
if auth_object == :admin
|
|
114
|
-
|
|
131
|
+
authorizable_ransackable_attributes - ['only_sort']
|
|
115
132
|
else
|
|
116
|
-
|
|
133
|
+
authorizable_ransackable_attributes - ['only_sort', 'only_admin']
|
|
117
134
|
end
|
|
118
135
|
end
|
|
119
136
|
|
|
@@ -129,7 +146,7 @@ end
|
|
|
129
146
|
class Musician < Person
|
|
130
147
|
end
|
|
131
148
|
|
|
132
|
-
class Article <
|
|
149
|
+
class Article < ApplicationRecord
|
|
133
150
|
belongs_to :person
|
|
134
151
|
has_many :comments
|
|
135
152
|
has_and_belongs_to_many :tags
|
|
@@ -138,12 +155,51 @@ class Article < ActiveRecord::Base
|
|
|
138
155
|
alias_attribute :content, :body
|
|
139
156
|
|
|
140
157
|
default_scope { where("'default_scope' = 'default_scope'") }
|
|
158
|
+
scope :latest_comment_cont, lambda { |msg|
|
|
159
|
+
join = <<-SQL
|
|
160
|
+
(LEFT OUTER JOIN (
|
|
161
|
+
SELECT
|
|
162
|
+
comments.*,
|
|
163
|
+
row_number() OVER (PARTITION BY comments.article_id ORDER BY comments.id DESC) AS rownum
|
|
164
|
+
FROM comments
|
|
165
|
+
) AS latest_comment
|
|
166
|
+
ON latest_comment.article_id = article.id
|
|
167
|
+
AND latest_comment.rownum = 1
|
|
168
|
+
)
|
|
169
|
+
SQL
|
|
170
|
+
.squish
|
|
171
|
+
|
|
172
|
+
joins(join).where("latest_comment.body ILIKE ?", "%#{msg}%")
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
ransacker :title_type, formatter: lambda { |tuples|
|
|
176
|
+
title, type = JSON.parse(tuples)
|
|
177
|
+
Arel::Nodes::Grouping.new(
|
|
178
|
+
[
|
|
179
|
+
Arel::Nodes.build_quoted(title),
|
|
180
|
+
Arel::Nodes.build_quoted(type)
|
|
181
|
+
]
|
|
182
|
+
)
|
|
183
|
+
} do |_parent|
|
|
184
|
+
articles = Article.arel_table
|
|
185
|
+
Arel::Nodes::Grouping.new(
|
|
186
|
+
%i[title type].map do |field|
|
|
187
|
+
Arel::Nodes::NamedFunction.new(
|
|
188
|
+
'COALESCE',
|
|
189
|
+
[
|
|
190
|
+
Arel::Nodes::NamedFunction.new('TRIM', [articles[field]]),
|
|
191
|
+
Arel::Nodes.build_quoted('')
|
|
192
|
+
]
|
|
193
|
+
)
|
|
194
|
+
end
|
|
195
|
+
)
|
|
196
|
+
end
|
|
141
197
|
end
|
|
142
198
|
|
|
143
199
|
class StoryArticle < Article
|
|
144
200
|
end
|
|
145
201
|
|
|
146
|
-
class Recommendation <
|
|
202
|
+
class Recommendation < ApplicationRecord
|
|
147
203
|
belongs_to :person
|
|
148
204
|
belongs_to :target_person, class_name: 'Person'
|
|
149
205
|
belongs_to :article
|
|
@@ -161,21 +217,26 @@ module Namespace
|
|
|
161
217
|
end
|
|
162
218
|
end
|
|
163
219
|
|
|
164
|
-
class Comment <
|
|
220
|
+
class Comment < ApplicationRecord
|
|
165
221
|
belongs_to :article
|
|
166
222
|
belongs_to :person
|
|
167
223
|
|
|
168
224
|
default_scope { where(disabled: false) }
|
|
169
225
|
end
|
|
170
226
|
|
|
171
|
-
class Tag <
|
|
227
|
+
class Tag < ApplicationRecord
|
|
172
228
|
has_and_belongs_to_many :articles
|
|
173
229
|
end
|
|
174
230
|
|
|
175
|
-
class Note <
|
|
231
|
+
class Note < ApplicationRecord
|
|
176
232
|
belongs_to :notable, polymorphic: true
|
|
177
233
|
end
|
|
178
234
|
|
|
235
|
+
class Account < ApplicationRecord
|
|
236
|
+
belongs_to :agent_account, class_name: "Account"
|
|
237
|
+
belongs_to :trade_account, class_name: "Account"
|
|
238
|
+
end
|
|
239
|
+
|
|
179
240
|
module Schema
|
|
180
241
|
def self.create
|
|
181
242
|
ActiveRecord::Migration.verbose = false
|
|
@@ -234,6 +295,11 @@ module Schema
|
|
|
234
295
|
t.integer :target_person_id
|
|
235
296
|
t.integer :article_id
|
|
236
297
|
end
|
|
298
|
+
|
|
299
|
+
create_table :accounts, force: true do |t|
|
|
300
|
+
t.belongs_to :agent_account
|
|
301
|
+
t.belongs_to :trade_account
|
|
302
|
+
end
|
|
237
303
|
end
|
|
238
304
|
|
|
239
305
|
10.times do
|
|
@@ -259,7 +325,7 @@ module Schema
|
|
|
259
325
|
end
|
|
260
326
|
|
|
261
327
|
module SubDB
|
|
262
|
-
class Base <
|
|
328
|
+
class Base < ApplicationRecord
|
|
263
329
|
self.abstract_class = true
|
|
264
330
|
establish_connection(
|
|
265
331
|
adapter: 'sqlite3',
|