ransack 1.8.4 → 3.2.1

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.
Files changed (184) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/SECURITY.md +12 -0
  4. data/.github/workflows/cronjob.yml +102 -0
  5. data/.github/workflows/deploy.yml +35 -0
  6. data/.github/workflows/rubocop.yml +20 -0
  7. data/.github/workflows/test-deploy.yml +29 -0
  8. data/.github/workflows/test.yml +130 -0
  9. data/.gitignore +3 -0
  10. data/{lib/ransack/adapters/mongoid/3.2/.gitkeep → .nojekyll} +0 -0
  11. data/.rubocop.yml +44 -0
  12. data/CHANGELOG.md +352 -0
  13. data/CONTRIBUTING.md +25 -13
  14. data/Gemfile +26 -27
  15. data/README.md +65 -815
  16. data/Rakefile +1 -22
  17. data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
  18. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +71 -0
  19. data/docs/.gitignore +19 -0
  20. data/docs/.nojekyll +0 -0
  21. data/docs/babel.config.js +3 -0
  22. data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
  23. data/docs/docs/getting-started/_category_.json +4 -0
  24. data/docs/docs/getting-started/advanced-mode.md +46 -0
  25. data/docs/docs/getting-started/configuration.md +47 -0
  26. data/docs/docs/getting-started/search-matches.md +67 -0
  27. data/docs/docs/getting-started/simple-mode.md +284 -0
  28. data/docs/docs/getting-started/sorting.md +79 -0
  29. data/docs/docs/getting-started/using-predicates.md +282 -0
  30. data/docs/docs/going-further/_category_.json +4 -0
  31. data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
  32. data/docs/docs/going-further/associations.md +70 -0
  33. data/docs/docs/going-further/custom-predicates.md +52 -0
  34. data/docs/docs/going-further/documentation.md +43 -0
  35. data/docs/docs/going-further/exporting-to-csv.md +49 -0
  36. data/docs/docs/going-further/external-guides.md +57 -0
  37. data/docs/docs/going-further/form-customisation.md +63 -0
  38. data/docs/docs/going-further/i18n.md +53 -0
  39. data/docs/docs/going-further/img/create_release.png +0 -0
  40. data/docs/docs/going-further/merging-searches.md +41 -0
  41. data/docs/docs/going-further/other-notes.md +428 -0
  42. data/docs/docs/going-further/polymorphic-search.md +40 -0
  43. data/docs/docs/going-further/ransackers.md +331 -0
  44. data/docs/docs/going-further/release_process.md +36 -0
  45. data/docs/docs/going-further/saving-queries.md +82 -0
  46. data/docs/docs/going-further/searching-postgres.md +57 -0
  47. data/docs/docs/going-further/wiki-contributors.md +82 -0
  48. data/docs/docs/intro.md +99 -0
  49. data/docs/docusaurus.config.js +120 -0
  50. data/docs/package.json +38 -0
  51. data/docs/sidebars.js +31 -0
  52. data/docs/src/components/HomepageFeatures/index.js +64 -0
  53. data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  54. data/docs/src/css/custom.css +39 -0
  55. data/docs/src/pages/index.module.css +23 -0
  56. data/docs/src/pages/markdown-page.md +7 -0
  57. data/docs/static/.nojekyll +0 -0
  58. data/docs/static/img/docusaurus.png +0 -0
  59. data/docs/static/img/favicon.ico +0 -0
  60. data/docs/static/img/logo.svg +1 -0
  61. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  62. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  63. data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  64. data/docs/static/img/undraw_docusaurus_react.svg +170 -0
  65. data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  66. data/docs/static/logo/ransack-h.png +0 -0
  67. data/docs/static/logo/ransack-h.svg +34 -0
  68. data/docs/static/logo/ransack-v.png +0 -0
  69. data/docs/static/logo/ransack-v.svg +34 -0
  70. data/docs/static/logo/ransack.png +0 -0
  71. data/docs/static/logo/ransack.svg +21 -0
  72. data/docs/yarn.lock +8436 -0
  73. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +70 -0
  74. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +92 -0
  75. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -0
  76. data/lib/polyamorous/activerecord_7.0_ruby_2/join_association.rb +1 -0
  77. data/lib/polyamorous/activerecord_7.0_ruby_2/join_dependency.rb +1 -0
  78. data/lib/polyamorous/activerecord_7.0_ruby_2/reflection.rb +1 -0
  79. data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
  80. data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
  81. data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
  82. data/lib/polyamorous/join.rb +70 -0
  83. data/lib/polyamorous/polyamorous.rb +24 -0
  84. data/lib/polyamorous/swapping_reflection_class.rb +11 -0
  85. data/lib/polyamorous/tree_node.rb +7 -0
  86. data/lib/polyamorous.rb +1 -0
  87. data/lib/ransack/adapters/active_record/base.rb +14 -3
  88. data/lib/ransack/adapters/active_record/context.rb +140 -196
  89. data/lib/ransack/adapters/active_record/ransack/constants.rb +19 -4
  90. data/lib/ransack/adapters/active_record/ransack/context.rb +9 -19
  91. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +7 -7
  92. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
  93. data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
  94. data/lib/ransack/adapters/active_record.rb +0 -9
  95. data/lib/ransack/adapters.rb +2 -0
  96. data/lib/ransack/configuration.rb +52 -2
  97. data/lib/ransack/constants.rb +1 -5
  98. data/lib/ransack/context.rb +29 -24
  99. data/lib/ransack/helpers/form_builder.rb +12 -6
  100. data/lib/ransack/helpers/form_helper.rb +11 -3
  101. data/lib/ransack/helpers.rb +1 -1
  102. data/lib/ransack/locale/ar.yml +70 -0
  103. data/lib/ransack/locale/az.yml +70 -0
  104. data/lib/ransack/locale/bg.yml +70 -0
  105. data/lib/ransack/locale/ca.yml +70 -0
  106. data/lib/ransack/locale/el.yml +70 -0
  107. data/lib/ransack/locale/es.yml +22 -22
  108. data/lib/ransack/locale/fa.yml +70 -0
  109. data/lib/ransack/locale/fi.yml +71 -0
  110. data/lib/ransack/locale/nl.yml +4 -4
  111. data/lib/ransack/locale/ru.yml +70 -0
  112. data/lib/ransack/locale/sk.yml +70 -0
  113. data/lib/ransack/locale/sv.yml +70 -0
  114. data/lib/ransack/locale/tr.yml +70 -0
  115. data/lib/ransack/locale/zh-CN.yml +12 -12
  116. data/lib/ransack/nodes/attribute.rb +2 -2
  117. data/lib/ransack/nodes/condition.rb +7 -1
  118. data/lib/ransack/nodes/grouping.rb +3 -8
  119. data/lib/ransack/nodes/sort.rb +3 -3
  120. data/lib/ransack/nodes/value.rb +3 -3
  121. data/lib/ransack/predicate.rb +13 -20
  122. data/lib/ransack/search.rb +7 -4
  123. data/lib/ransack/translate.rb +115 -115
  124. data/lib/ransack/version.rb +1 -1
  125. data/lib/ransack/visitor.rb +1 -12
  126. data/lib/ransack.rb +7 -5
  127. data/ransack.gemspec +9 -25
  128. data/spec/blueprints/articles.rb +1 -1
  129. data/spec/blueprints/comments.rb +1 -1
  130. data/spec/blueprints/notes.rb +1 -1
  131. data/spec/blueprints/tags.rb +1 -1
  132. data/spec/console.rb +5 -5
  133. data/spec/helpers/polyamorous_helper.rb +13 -0
  134. data/spec/helpers/ransack_helper.rb +1 -1
  135. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  136. data/spec/polyamorous/join_association_spec.rb +30 -0
  137. data/spec/polyamorous/join_dependency_spec.rb +81 -0
  138. data/spec/polyamorous/join_spec.rb +19 -0
  139. data/spec/ransack/adapters/active_record/base_spec.rb +105 -11
  140. data/spec/ransack/adapters/active_record/context_spec.rb +63 -24
  141. data/spec/ransack/configuration_spec.rb +24 -0
  142. data/spec/ransack/helpers/form_builder_spec.rb +3 -15
  143. data/spec/ransack/helpers/form_helper_spec.rb +135 -168
  144. data/spec/ransack/nodes/condition_spec.rb +13 -0
  145. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  146. data/spec/ransack/nodes/value_spec.rb +115 -0
  147. data/spec/ransack/predicate_spec.rb +54 -2
  148. data/spec/ransack/search_spec.rb +266 -36
  149. data/spec/spec_helper.rb +14 -5
  150. data/spec/support/schema.rb +99 -21
  151. metadata +117 -187
  152. data/.travis.yml +0 -86
  153. data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
  154. data/lib/ransack/adapters/active_record/3.0/context.rb +0 -203
  155. data/lib/ransack/adapters/active_record/3.1/context.rb +0 -212
  156. data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
  157. data/lib/ransack/adapters/active_record/compat.rb +0 -14
  158. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
  159. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
  160. data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
  161. data/lib/ransack/adapters/mongoid/base.rb +0 -134
  162. data/lib/ransack/adapters/mongoid/context.rb +0 -212
  163. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
  164. data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
  165. data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
  166. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
  167. data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
  168. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
  169. data/lib/ransack/adapters/mongoid/table.rb +0 -35
  170. data/lib/ransack/adapters/mongoid.rb +0 -15
  171. data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -314
  172. data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
  173. data/spec/mongoid/configuration_spec.rb +0 -162
  174. data/spec/mongoid/dependencies_spec.rb +0 -8
  175. data/spec/mongoid/helpers/ransack_helper.rb +0 -11
  176. data/spec/mongoid/nodes/condition_spec.rb +0 -49
  177. data/spec/mongoid/nodes/grouping_spec.rb +0 -13
  178. data/spec/mongoid/predicate_spec.rb +0 -155
  179. data/spec/mongoid/search_spec.rb +0 -445
  180. data/spec/mongoid/support/mongoid.yml +0 -11
  181. data/spec/mongoid/support/schema.rb +0 -135
  182. data/spec/mongoid/translate_spec.rb +0 -14
  183. data/spec/mongoid_spec_helper.rb +0 -63
  184. data/spec/ransack/dependencies_spec.rb +0 -12
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ module Polyamorous
4
+ describe JoinDependency do
5
+
6
+ context 'with symbol joins' do
7
+ subject { new_join_dependency Person, articles: :comments }
8
+
9
+ specify { expect(subject.send(:join_root).drop(1).size)
10
+ .to eq(2) }
11
+ specify { expect(subject.send(:join_root).drop(1).map(&:join_type).uniq)
12
+ .to eq [Polyamorous::InnerJoin] }
13
+ end
14
+
15
+ context 'with has_many :through association' do
16
+ subject { new_join_dependency Person, :authored_article_comments }
17
+
18
+ specify { expect(subject.send(:join_root).drop(1).size)
19
+ .to eq 1 }
20
+ specify { expect(subject.send(:join_root).drop(1).first.table_name)
21
+ .to eq 'comments' }
22
+ end
23
+
24
+ context 'with outer join' do
25
+ subject { new_join_dependency Person, new_join(:articles, :outer) }
26
+
27
+ specify { expect(subject.send(:join_root).drop(1).size)
28
+ .to eq 1 }
29
+ specify { expect(subject.send(:join_root).drop(1).first.join_type)
30
+ .to eq Polyamorous::OuterJoin }
31
+ end
32
+
33
+ context 'with nested outer joins' do
34
+ subject { new_join_dependency Person,
35
+ new_join(:articles, :outer) => new_join(:comments, :outer) }
36
+
37
+ specify { expect(subject.send(:join_root).drop(1).size)
38
+ .to eq 2 }
39
+ specify { expect(subject.send(:join_root).drop(1).map(&:join_type))
40
+ .to eq [Polyamorous::OuterJoin, Polyamorous::OuterJoin] }
41
+ specify { expect(subject.send(:join_root).drop(1).map(&:join_type).uniq)
42
+ .to eq [Polyamorous::OuterJoin] }
43
+ end
44
+
45
+ context 'with polymorphic belongs_to join' do
46
+ subject { new_join_dependency Note, new_join(:notable, :inner, Person) }
47
+
48
+ specify { expect(subject.send(:join_root).drop(1).size)
49
+ .to eq 1 }
50
+ specify { expect(subject.send(:join_root).drop(1).first.join_type)
51
+ .to eq Polyamorous::InnerJoin }
52
+ specify { expect(subject.send(:join_root).drop(1).first.table_name)
53
+ .to eq 'people' }
54
+ end
55
+
56
+ context 'with polymorphic belongs_to join and nested symbol join' do
57
+ subject { new_join_dependency Note,
58
+ new_join(:notable, :inner, Person) => :comments }
59
+
60
+ specify { expect(subject.send(:join_root).drop(1).size)
61
+ .to eq 2 }
62
+ specify { expect(subject.send(:join_root).drop(1).map(&:join_type).uniq)
63
+ .to eq [Polyamorous::InnerJoin] }
64
+ specify { expect(subject.send(:join_root).drop(1).first.table_name)
65
+ .to eq 'people' }
66
+ specify { expect(subject.send(:join_root).drop(1)[1].table_name)
67
+ .to eq 'comments' }
68
+ end
69
+
70
+ context 'with polymorphic belongs_to join and nested join' do
71
+ subject { new_join_dependency Note,
72
+ new_join(:notable, :outer, Person) => :comments }
73
+ specify { expect(subject.send(:join_root).drop(1).size).to eq 2 }
74
+ specify { expect(subject.send(:join_root).drop(1).map(&:join_type)).to eq [Polyamorous::OuterJoin, Polyamorous::InnerJoin] }
75
+ specify { expect(subject.send(:join_root).drop(1).first.table_name)
76
+ .to eq 'people' }
77
+ specify { expect(subject.send(:join_root).drop(1)[1].table_name)
78
+ .to eq 'comments' }
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module Polyamorous
4
+ describe Join do
5
+ it 'is a tree node' do
6
+ join = new_join(:articles, :outer)
7
+ expect(join).to be_kind_of(TreeNode)
8
+ end
9
+
10
+ it 'can be added to a tree' do
11
+ join = new_join(:articles, :outer)
12
+
13
+ tree_hash = {}
14
+ join.add_to_tree(tree_hash)
15
+
16
+ expect(tree_hash[join]).to be {}
17
+ end
18
+ end
19
+ end
@@ -8,7 +8,6 @@ module Ransack
8
8
  subject { ::ActiveRecord::Base }
9
9
 
10
10
  it { should respond_to :ransack }
11
- it { should respond_to :search }
12
11
 
13
12
  describe '#search' do
14
13
  subject { Person.ransack }
@@ -18,6 +17,13 @@ module Ransack
18
17
  expect(subject.object).to be_an ::ActiveRecord::Relation
19
18
  end
20
19
 
20
+ context "multiple database connection" do
21
+ it "does not raise error" do
22
+ expect { Person.ransack(name_cont: "test") }.not_to raise_error
23
+ expect { SubDB::OperationHistory.ransack(people_id_eq: 1) }.not_to raise_error
24
+ end
25
+ end
26
+
21
27
  context 'with scopes' do
22
28
  before do
23
29
  allow(Person)
@@ -37,12 +43,12 @@ module Ransack
37
43
 
38
44
  it 'applies stringy boolean scopes with true value in an array' do
39
45
  s = Person.ransack('of_age' => ['true'])
40
- expect(s.result.to_sql).to (include 'age >= 18')
46
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{(age >= '18')} : 'age >= 18')
41
47
  end
42
48
 
43
49
  it 'applies stringy boolean scopes with false value in an array' do
44
50
  s = Person.ransack('of_age' => ['false'])
45
- expect(s.result.to_sql).to (include 'age < 18')
51
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age < '18'} : 'age < 18')
46
52
  end
47
53
 
48
54
  it 'ignores unlisted scopes' do
@@ -62,15 +68,25 @@ module Ransack
62
68
 
63
69
  it 'passes values to scopes' do
64
70
  s = Person.ransack('over_age' => 18)
65
- expect(s.result.to_sql).to (include 'age > 18')
71
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
66
72
  end
67
73
 
68
74
  it 'chains scopes' do
69
75
  s = Person.ransack('over_age' => 18, 'active' => true)
70
- expect(s.result.to_sql).to (include 'age > 18')
76
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
71
77
  expect(s.result.to_sql).to (include 'active = 1')
72
78
  end
73
79
 
80
+ it 'applies scopes that define string SQL joins' do
81
+ allow(Article)
82
+ .to receive(:ransackable_scopes)
83
+ .and_return([:latest_comment_cont])
84
+
85
+ # Including a negative condition to test removing the scope
86
+ s = Search.new(Article, notes_note_not_eq: 'Test', latest_comment_cont: 'Test')
87
+ expect(s.result.to_sql).to include 'latest_comment'
88
+ end
89
+
74
90
  context "with sanitize_custom_scope_booleans set to false" do
75
91
  before(:all) do
76
92
  Ransack.configure { |c| c.sanitize_custom_scope_booleans = false }
@@ -82,20 +98,43 @@ module Ransack
82
98
 
83
99
  it 'passes true values to scopes' do
84
100
  s = Person.ransack('over_age' => 1)
85
- expect(s.result.to_sql).to (include 'age > 1')
101
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
86
102
  end
87
103
 
88
104
  it 'passes false values to scopes' do
89
105
  s = Person.ransack('over_age' => 0)
90
- expect(s.result.to_sql).to (include 'age > 0')
106
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
91
107
  end
92
108
  end
109
+
110
+ context "with ransackable_scopes_skip_sanitize_args enabled for scope" do
111
+ before do
112
+ allow(Person)
113
+ .to receive(:ransackable_scopes_skip_sanitize_args)
114
+ .and_return([:over_age])
115
+ end
116
+
117
+ it 'passes true values to scopes' do
118
+ s = Person.ransack('over_age' => 1)
119
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
120
+ end
121
+
122
+ it 'passes false values to scopes' do
123
+ s = Person.ransack('over_age' => 0)
124
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
125
+ end
126
+ end
127
+
93
128
  end
94
129
 
95
130
  it 'does not raise exception for string :params argument' do
96
131
  expect { Person.ransack('') }.to_not raise_error
97
132
  end
98
133
 
134
+ it 'raises exception if ransack! called with unknown condition' do
135
+ expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error(ArgumentError)
136
+ end
137
+
99
138
  it 'does not modify the parameters' do
100
139
  params = { name_eq: '' }
101
140
  expect { Person.ransack(params) }.not_to change { params }
@@ -117,14 +156,12 @@ module Ransack
117
156
  it 'removes redundant joins from top query' do
118
157
  s = Article.ransack(tags_name_not_eq: "Fantasy")
119
158
  sql = s.result.to_sql
120
-
121
159
  expect(sql).to_not include('LEFT OUTER JOIN')
122
160
  end
123
161
 
124
162
  it 'handles != for single values' do
125
163
  s = Article.ransack(tags_name_not_eq: "Fantasy")
126
164
  articles = s.result.to_a
127
-
128
165
  expect(articles).to include marco
129
166
  expect(articles).to_not include arthur
130
167
  end
@@ -241,10 +278,12 @@ module Ransack
241
278
  # end
242
279
 
243
280
  it 'creates ransack attributes' do
281
+ person = Person.create!(name: 'Aric Smith')
282
+
244
283
  s = Person.ransack(reversed_name_eq: 'htimS cirA')
245
284
  expect(s.result.size).to eq(1)
246
285
 
247
- expect(s.result.first).to eq Person.where(name: 'Aric Smith').first
286
+ expect(s.result.first).to eq person
248
287
  end
249
288
 
250
289
  it 'can be accessed through associations' do
@@ -284,7 +323,11 @@ module Ransack
284
323
  end
285
324
 
286
325
  it 'should function correctly with a multi-parameter attribute' do
287
- ::ActiveRecord::Base.default_timezone = :utc
326
+ if ::ActiveRecord::VERSION::MAJOR >= 7
327
+ ::ActiveRecord.default_timezone = :utc
328
+ else
329
+ ::ActiveRecord::Base.default_timezone = :utc
330
+ end
288
331
  Time.zone = 'UTC'
289
332
 
290
333
  date = Date.current
@@ -429,6 +472,16 @@ module Ransack
429
472
  end
430
473
  end
431
474
 
475
+ it 'sorts with different join variants' do
476
+ comments = [
477
+ Comment.create(article: Article.create(title: 'Avenger'), person: Person.create(salary: 100_000)),
478
+ Comment.create(article: Article.create(title: 'Avenge'), person: Person.create(salary: 50_000)),
479
+ ]
480
+ expect(Comment.ransack(article_title_cont: 'aven', s: 'person_salary desc').result).to eq(comments)
481
+ expect(Comment.joins(:person).ransack(s: 'persons_salarydesc', article_title_cont: 'aven').result).to eq(comments)
482
+ expect(Comment.joins(:person).ransack(article_title_cont: 'aven', s: 'persons_salary desc').result).to eq(comments)
483
+ end
484
+
432
485
  it 'allows sort by `only_sort` field' do
433
486
  s = Person.ransack(
434
487
  's' => { '0' => { 'dir' => 'asc', 'name' => 'only_sort' } }
@@ -544,6 +597,37 @@ module Ransack
544
597
  /BETWEEN 2 AND 6 GROUP BY articles.person_id \) DESC/
545
598
  )
546
599
  end
600
+
601
+ context 'case insensitive sorting' do
602
+ it 'allows sort by desc' do
603
+ search = Person.ransack(sorts: ['name_case_insensitive desc'])
604
+ expect(search.result.to_sql).to match /ORDER BY LOWER(.*) DESC/
605
+ end
606
+
607
+ it 'allows sort by asc' do
608
+ search = Person.ransack(sorts: ['name_case_insensitive asc'])
609
+ expect(search.result.to_sql).to match /ORDER BY LOWER(.*) ASC/
610
+ end
611
+ end
612
+
613
+ context 'regular sorting' do
614
+ it 'allows sort by desc' do
615
+ search = Person.ransack(sorts: ['name desc'])
616
+ expect(search.result.to_sql).to match /ORDER BY .* DESC/
617
+ end
618
+
619
+ it 'allows sort by asc' do
620
+ search = Person.ransack(sorts: ['name asc'])
621
+ expect(search.result.to_sql).to match /ORDER BY .* ASC/
622
+ end
623
+ end
624
+
625
+ context 'sorting by a scope' do
626
+ it 'applies the correct scope' do
627
+ search = Person.ransack(sorts: ['reverse_name asc'])
628
+ expect(search.result.to_sql).to include("ORDER BY REVERSE(name) ASC")
629
+ end
630
+ end
547
631
  end
548
632
 
549
633
  describe '#ransackable_attributes' do
@@ -613,6 +697,16 @@ module Ransack
613
697
  it { should eq [] }
614
698
  end
615
699
 
700
+ describe '#ransackable_scopes_skip_sanitize_args' do
701
+ subject { Person.ransackable_scopes_skip_sanitize_args }
702
+
703
+ it { should eq [] }
704
+ end
705
+
706
+ private
707
+ def rails7_and_mysql
708
+ ::ActiveRecord::VERSION::MAJOR >= 7 && ENV['DB'] == 'mysql'
709
+ end
616
710
  end
617
711
  end
618
712
  end
@@ -9,9 +9,7 @@ module Ransack
9
9
  describe Context do
10
10
  subject { Context.new(Person) }
11
11
 
12
-
13
- it 'has an Active Record alias tracker method',
14
- if: AR_version >= '3.1' do
12
+ it 'has an Active Record alias tracker method' do
15
13
  expect(subject.alias_tracker)
16
14
  .to be_an ::ActiveRecord::Associations::AliasTracker
17
15
  end
@@ -41,6 +39,66 @@ module Ransack
41
39
  end
42
40
  end
43
41
 
42
+ describe '#build_correlated_subquery' do
43
+ it 'build correlated subquery for Root STI model' do
44
+ search = Search.new(Person, { articles_title_not_eq: 'some_title' }, context: subject)
45
+ attribute = search.conditions.first.attributes.first
46
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
47
+ constraint = constraints.first
48
+
49
+ expect(constraints.length).to eql 1
50
+ expect(constraint.left.name).to eql 'person_id'
51
+ expect(constraint.left.relation.name).to eql 'articles'
52
+ expect(constraint.right.name).to eql 'id'
53
+ expect(constraint.right.relation.name).to eql 'people'
54
+ end
55
+
56
+ it 'build correlated subquery for Child STI model when predicate is not_eq' do
57
+ search = Search.new(Person, { story_articles_title_not_eq: 'some_title' }, context: subject)
58
+ attribute = search.conditions.first.attributes.first
59
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
60
+ constraint = constraints.first
61
+
62
+ expect(constraints.length).to eql 1
63
+ expect(constraint.left.relation.name).to eql 'articles'
64
+ expect(constraint.left.name).to eql 'person_id'
65
+ expect(constraint.right.relation.name).to eql 'people'
66
+ expect(constraint.right.name).to eql 'id'
67
+ end
68
+
69
+ it 'build correlated subquery for Child STI model when predicate is eq' do
70
+ search = Search.new(Person, { story_articles_title_not_eq: 'some_title' }, context: subject)
71
+ attribute = search.conditions.first.attributes.first
72
+ constraints = subject.build_correlated_subquery(attribute.parent).constraints
73
+ constraint = constraints.first
74
+
75
+ expect(constraints.length).to eql 1
76
+ expect(constraint.left.relation.name).to eql 'articles'
77
+ expect(constraint.left.name).to eql 'person_id'
78
+ expect(constraint.right.relation.name).to eql 'people'
79
+ expect(constraint.right.name).to eql 'id'
80
+ end
81
+
82
+ it 'build correlated subquery for multiple conditions (default scope)' do
83
+ search = Search.new(Person, { comments_body_not_eq: 'some_title' })
84
+
85
+ # Was
86
+ # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
87
+ # SELECT "comments"."disabled" FROM "comments"
88
+ # WHERE "comments"."disabled" = "people"."id"
89
+ # AND NOT ("comments"."body" != 'some_title')
90
+ # ) ORDER BY "people"."id" DESC
91
+ # Should Be
92
+ # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
93
+ # SELECT "comments"."person_id" FROM "comments"
94
+ # WHERE "comments"."person_id" = "people"."id"
95
+ # AND NOT ("comments"."body" != 'some_title')
96
+ # ) ORDER BY "people"."id" DESC
97
+
98
+ expect(search.result.to_sql).to match /.comments.\..person_id. = .people.\..id./
99
+ end
100
+ end
101
+
44
102
  describe 'sharing context across searches' do
45
103
  let(:shared_context) { Context.for(Person) }
46
104
 
@@ -51,34 +109,15 @@ module Ransack
51
109
  context: shared_context)
52
110
  end
53
111
 
54
- describe '#join_associations', if: AR_version <= '4.0' do
55
- it 'returns dependent join associations for all searches run
56
- against the context' do
57
- parents, children = shared_context.join_associations
58
-
59
- expect(children.aliased_table_name).to eq "children_people"
60
- expect(parents.aliased_table_name).to eq "parents_people"
61
- end
62
-
63
- it 'can be rejoined to execute a valid query' do
64
- parents, children = shared_context.join_associations
65
-
66
- expect { Person.joins(parents).joins(children).to_a }
67
- .to_not raise_error
68
- end
69
- end
70
-
71
112
  describe '#join_sources' do
72
- # FIXME: fix this test for Rails 4.2 and 5.0.
73
113
  it 'returns dependent arel join nodes for all searches run against
74
- the context', if: %w(3.1 3.2 4.0 4.1).include?(AR_version) do
114
+ the context' do
75
115
  parents, children = shared_context.join_sources
76
116
  expect(children.left.name).to eq "children_people"
77
117
  expect(parents.left.name).to eq "parents_people"
78
118
  end
79
119
 
80
- it 'can be rejoined to execute a valid query',
81
- if: AR_version >= '3.1' do
120
+ it 'can be rejoined to execute a valid query' do
82
121
  parents, children = shared_context.join_sources
83
122
 
84
123
  expect { Person.joins(parents).joins(children).to_a }
@@ -45,6 +45,20 @@ module Ransack
45
45
  Ransack.options = default
46
46
  end
47
47
 
48
+ it 'should have default value for strip_whitespace' do
49
+ expect(Ransack.options[:strip_whitespace]).to eq true
50
+ end
51
+
52
+ it 'changes default search key parameter' do
53
+ default = Ransack.options.clone
54
+
55
+ Ransack.configure { |c| c.strip_whitespace = false }
56
+
57
+ expect(Ransack.options[:strip_whitespace]).to eq false
58
+
59
+ Ransack.options = default
60
+ end
61
+
48
62
  it 'should have default values for arrows' do
49
63
  expect(Ransack.options[:up_arrow]).to eq '&#9660;'
50
64
  expect(Ransack.options[:down_arrow]).to eq '&#9650;'
@@ -173,5 +187,15 @@ module Ransack
173
187
  .to eq false
174
188
  end
175
189
  end
190
+
191
+ it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
192
+ default = Ransack.options.clone
193
+
194
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
195
+
196
+ expect(Ransack.options[:postgres_fields_sort_option]).to eq :nulls_first
197
+
198
+ Ransack.options = default
199
+ end
176
200
  end
177
201
  end
@@ -21,11 +21,7 @@ module Ransack
21
21
  @controller.view_context.search_form_for(@s) { |f| @f = f }
22
22
  end
23
23
 
24
- it 'selects previously-entered time values with datetime_select',
25
- unless: (
26
- RUBY_VERSION >= '2.3' &&
27
- ::ActiveRecord::VERSION::STRING.first(3) < '3.2'
28
- ) do
24
+ it 'selects previously-entered time values with datetime_select' do
29
25
  date_values = %w(2011 1 2 03 04 05)
30
26
  # @s.created_at_eq = date_values # This works in Rails 4.x but not 3.x
31
27
  @s.created_at_eq = [2011, 1, 2, 3, 4, 5] # so we have to do this
@@ -75,11 +71,7 @@ module Ransack
75
71
  describe '#sort_link' do
76
72
  it 'sort_link for ransack attribute' do
77
73
  sort_link = @f.sort_link :name, :controller => 'people'
78
- if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
79
- expect(sort_link).to match /people\?q%5Bs%5D=name\+asc/
80
- else
81
- expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
82
- end
74
+ expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
83
75
  expect(sort_link).to match /sort_link/
84
76
  expect(sort_link).to match /Full Name<\/a>/
85
77
  end
@@ -171,11 +163,7 @@ module Ransack
171
163
  # Starting from Rails 4.2, the date_select html attributes are no longer
172
164
  # `sort`ed (for a speed gain), so the tests have to be different:
173
165
  def date_select_html(val)
174
- if ::ActiveRecord::VERSION::STRING >= '4.2'
175
- %(<option value="#{val}" selected="selected">#{val}</option>)
176
- else
177
- %(<option selected="selected" value="#{val}">#{val}</option>)
178
- end
166
+ %(<option value="#{val}" selected="selected">#{val}</option>)
179
167
  end
180
168
 
181
169
  end