ransack 1.8.4 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
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