ransack 1.6.6 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.travis.yml +35 -53
  4. data/CHANGELOG.md +530 -9
  5. data/CONTRIBUTING.md +63 -26
  6. data/Gemfile +24 -24
  7. data/README.md +371 -96
  8. data/Rakefile +6 -29
  9. data/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +24 -0
  10. data/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +79 -0
  11. data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +11 -0
  12. data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +1 -0
  13. data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +80 -0
  14. data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +1 -0
  15. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +74 -0
  16. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +93 -0
  17. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +1 -0
  18. data/lib/polyamorous/join.rb +70 -0
  19. data/lib/polyamorous/polyamorous.rb +24 -0
  20. data/lib/polyamorous/swapping_reflection_class.rb +11 -0
  21. data/lib/polyamorous/tree_node.rb +7 -0
  22. data/lib/ransack/adapters/active_record/base.rb +23 -2
  23. data/lib/ransack/adapters/active_record/context.rb +206 -139
  24. data/lib/ransack/adapters/active_record/ransack/constants.rb +70 -55
  25. data/lib/ransack/adapters/active_record/ransack/context.rb +10 -18
  26. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +43 -36
  27. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
  28. data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
  29. data/lib/ransack/adapters/active_record.rb +11 -10
  30. data/lib/ransack/adapters.rb +64 -0
  31. data/lib/ransack/configuration.rb +91 -4
  32. data/lib/ransack/constants.rb +13 -26
  33. data/lib/ransack/context.rb +45 -40
  34. data/lib/ransack/helpers/form_builder.rb +21 -12
  35. data/lib/ransack/helpers/form_helper.rb +99 -69
  36. data/lib/ransack/locale/ar.yml +70 -0
  37. data/lib/ransack/locale/az.yml +70 -0
  38. data/lib/ransack/locale/bg.yml +70 -0
  39. data/lib/ransack/locale/ca.yml +70 -0
  40. data/lib/ransack/locale/da.yml +70 -0
  41. data/lib/ransack/locale/de.yml +70 -0
  42. data/lib/ransack/locale/el.yml +70 -0
  43. data/lib/ransack/locale/es.yml +22 -22
  44. data/lib/ransack/locale/fa.yml +70 -0
  45. data/lib/ransack/locale/fi.yml +71 -0
  46. data/lib/ransack/locale/id.yml +70 -0
  47. data/lib/ransack/locale/it.yml +70 -0
  48. data/lib/ransack/locale/ja.yml +70 -0
  49. data/lib/ransack/locale/nl.yml +4 -4
  50. data/lib/ransack/locale/pt-BR.yml +70 -0
  51. data/lib/ransack/locale/ru.yml +70 -0
  52. data/lib/ransack/locale/sk.yml +70 -0
  53. data/lib/ransack/locale/tr.yml +70 -0
  54. data/lib/ransack/locale/{zh.yml → zh-CN.yml} +13 -13
  55. data/lib/ransack/locale/zh-TW.yml +70 -0
  56. data/lib/ransack/nodes/attribute.rb +6 -3
  57. data/lib/ransack/nodes/bindable.rb +18 -6
  58. data/lib/ransack/nodes/condition.rb +85 -24
  59. data/lib/ransack/nodes/grouping.rb +17 -11
  60. data/lib/ransack/nodes/sort.rb +9 -5
  61. data/lib/ransack/nodes/value.rb +74 -68
  62. data/lib/ransack/nodes.rb +2 -3
  63. data/lib/ransack/predicate.rb +17 -20
  64. data/lib/ransack/search.rb +15 -15
  65. data/lib/ransack/translate.rb +115 -115
  66. data/lib/ransack/version.rb +1 -1
  67. data/lib/ransack/visitor.rb +1 -12
  68. data/lib/ransack.rb +11 -17
  69. data/logo/ransack-h.png +0 -0
  70. data/logo/ransack-h.svg +34 -0
  71. data/logo/ransack-v.png +0 -0
  72. data/logo/ransack-v.svg +34 -0
  73. data/logo/ransack.png +0 -0
  74. data/logo/ransack.svg +21 -0
  75. data/ransack.gemspec +9 -27
  76. data/spec/console.rb +4 -0
  77. data/spec/helpers/polyamorous_helper.rb +19 -0
  78. data/spec/polyamorous/join_association_spec.rb +35 -0
  79. data/spec/polyamorous/join_dependency_spec.rb +97 -0
  80. data/spec/polyamorous/join_spec.rb +19 -0
  81. data/spec/ransack/adapters/active_record/base_spec.rb +380 -91
  82. data/spec/ransack/adapters/active_record/context_spec.rb +72 -33
  83. data/spec/ransack/configuration_spec.rb +87 -14
  84. data/spec/ransack/helpers/form_builder_spec.rb +2 -11
  85. data/spec/ransack/helpers/form_helper_spec.rb +481 -113
  86. data/spec/ransack/nodes/condition_spec.rb +25 -1
  87. data/spec/ransack/nodes/grouping_spec.rb +62 -6
  88. data/spec/ransack/predicate_spec.rb +79 -5
  89. data/spec/ransack/search_spec.rb +159 -79
  90. data/spec/spec_helper.rb +8 -0
  91. data/spec/support/schema.rb +99 -37
  92. metadata +57 -184
  93. data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
  94. data/lib/ransack/adapters/active_record/3.0/context.rb +0 -205
  95. data/lib/ransack/adapters/active_record/3.1/context.rb +0 -219
  96. data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
  97. data/lib/ransack/adapters/active_record/compat.rb +0 -14
  98. data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
  99. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
  100. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
  101. data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
  102. data/lib/ransack/adapters/mongoid/base.rb +0 -126
  103. data/lib/ransack/adapters/mongoid/context.rb +0 -208
  104. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
  105. data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
  106. data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
  107. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
  108. data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
  109. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
  110. data/lib/ransack/adapters/mongoid/table.rb +0 -35
  111. data/lib/ransack/adapters/mongoid.rb +0 -13
  112. data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -276
  113. data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
  114. data/spec/mongoid/configuration_spec.rb +0 -102
  115. data/spec/mongoid/dependencies_spec.rb +0 -8
  116. data/spec/mongoid/helpers/ransack_helper.rb +0 -11
  117. data/spec/mongoid/nodes/condition_spec.rb +0 -34
  118. data/spec/mongoid/nodes/grouping_spec.rb +0 -13
  119. data/spec/mongoid/predicate_spec.rb +0 -155
  120. data/spec/mongoid/search_spec.rb +0 -446
  121. data/spec/mongoid/support/mongoid.yml +0 -6
  122. data/spec/mongoid/support/schema.rb +0 -128
  123. data/spec/mongoid/translate_spec.rb +0 -14
  124. data/spec/mongoid_spec_helper.rb +0 -59
  125. data/spec/ransack/dependencies_spec.rb +0 -10
@@ -18,56 +18,104 @@ module Ransack
18
18
  expect(subject.object).to be_an ::ActiveRecord::Relation
19
19
  end
20
20
 
21
+ context "multiple database connection" do
22
+ it "does not raise error" do
23
+ expect { Person.ransack(name_cont: "test") }.not_to raise_error
24
+ expect { SubDB::OperationHistory.ransack(people_id_eq: 1) }.not_to raise_error
25
+ end
26
+ end
27
+
21
28
  context 'with scopes' do
22
29
  before do
23
- Person.stub :ransackable_scopes => [:active, :over_age, :of_age]
30
+ allow(Person)
31
+ .to receive(:ransackable_scopes)
32
+ .and_return([:active, :over_age, :of_age])
24
33
  end
25
34
 
26
- it "applies true scopes" do
35
+ it 'applies true scopes' do
27
36
  s = Person.ransack('active' => true)
28
- s.result.to_sql.should include "active = 1"
37
+ expect(s.result.to_sql).to (include 'active = 1')
29
38
  end
30
39
 
31
- it "applies stringy true scopes" do
40
+ it 'applies stringy true scopes' do
32
41
  s = Person.ransack('active' => 'true')
33
- s.result.to_sql.should include "active = 1"
42
+ expect(s.result.to_sql).to (include 'active = 1')
34
43
  end
35
44
 
36
- it "applies stringy boolean scopes with true value in an array" do
45
+ it 'applies stringy boolean scopes with true value in an array' do
37
46
  s = Person.ransack('of_age' => ['true'])
38
- s.result.to_sql.should include "age >= 18"
47
+ expect(s.result.to_sql).to (include 'age >= 18')
39
48
  end
40
49
 
41
- it "applies stringy boolean scopes with false value in an array" do
50
+ it 'applies stringy boolean scopes with false value in an array' do
42
51
  s = Person.ransack('of_age' => ['false'])
43
- s.result.to_sql.should include "age < 18"
52
+ expect(s.result.to_sql).to (include 'age < 18')
44
53
  end
45
54
 
46
- it "ignores unlisted scopes" do
55
+ it 'ignores unlisted scopes' do
47
56
  s = Person.ransack('restricted' => true)
48
- s.result.to_sql.should_not include "restricted"
57
+ expect(s.result.to_sql).to_not (include 'restricted')
49
58
  end
50
59
 
51
- it "ignores false scopes" do
60
+ it 'ignores false scopes' do
52
61
  s = Person.ransack('active' => false)
53
- s.result.to_sql.should_not include "active"
62
+ expect(s.result.to_sql).not_to (include 'active')
54
63
  end
55
64
 
56
- it "ignores stringy false scopes" do
65
+ it 'ignores stringy false scopes' do
57
66
  s = Person.ransack('active' => 'false')
58
- s.result.to_sql.should_not include "active"
67
+ expect(s.result.to_sql).to_not (include 'active')
59
68
  end
60
69
 
61
- it "passes values to scopes" do
70
+ it 'passes values to scopes' do
62
71
  s = Person.ransack('over_age' => 18)
63
- s.result.to_sql.should include "age > 18"
72
+ expect(s.result.to_sql).to (include 'age > 18')
64
73
  end
65
74
 
66
- it "chains scopes" do
75
+ it 'chains scopes' do
67
76
  s = Person.ransack('over_age' => 18, 'active' => true)
68
- s.result.to_sql.should include "age > 18"
69
- s.result.to_sql.should include "active = 1"
77
+ expect(s.result.to_sql).to (include 'age > 18')
78
+ expect(s.result.to_sql).to (include 'active = 1')
70
79
  end
80
+
81
+ context "with sanitize_custom_scope_booleans set to false" do
82
+ before(:all) do
83
+ Ransack.configure { |c| c.sanitize_custom_scope_booleans = false }
84
+ end
85
+
86
+ after(:all) do
87
+ Ransack.configure { |c| c.sanitize_custom_scope_booleans = true }
88
+ end
89
+
90
+ it 'passes true values to scopes' do
91
+ s = Person.ransack('over_age' => 1)
92
+ expect(s.result.to_sql).to (include 'age > 1')
93
+ end
94
+
95
+ it 'passes false values to scopes' do
96
+ s = Person.ransack('over_age' => 0)
97
+ expect(s.result.to_sql).to (include 'age > 0')
98
+ end
99
+ end
100
+
101
+ context "with ransackable_scopes_skip_sanitize_args enabled for scope" do
102
+ before do
103
+ allow(Person)
104
+ .to receive(:ransackable_scopes_skip_sanitize_args)
105
+ .and_return([:over_age])
106
+ end
107
+
108
+ it 'passes true values to scopes' do
109
+ s = Person.ransack('over_age' => 1)
110
+ expect(s.result.to_sql).to (include 'age > 1')
111
+ end
112
+
113
+ it 'passes false values to scopes' do
114
+ s = Person.ransack('over_age' => 0)
115
+ expect(s.result.to_sql).to (include 'age > 0')
116
+ end
117
+ end
118
+
71
119
  end
72
120
 
73
121
  it 'does not raise exception for string :params argument' do
@@ -75,16 +123,131 @@ module Ransack
75
123
  end
76
124
 
77
125
  it 'does not modify the parameters' do
78
- params = { :name_eq => '' }
126
+ params = { name_eq: '' }
79
127
  expect { Person.ransack(params) }.not_to change { params }
80
128
  end
81
129
  end
82
130
 
131
+ context 'negative conditions on HABTM associations' do
132
+ let(:medieval) { Tag.create!(name: 'Medieval') }
133
+ let(:fantasy) { Tag.create!(name: 'Fantasy') }
134
+ let(:arthur) { Article.create!(title: 'King Arthur') }
135
+ let(:marco) { Article.create!(title: 'Marco Polo') }
136
+
137
+ before do
138
+ marco.tags << medieval
139
+ arthur.tags << medieval
140
+ arthur.tags << fantasy
141
+ end
142
+
143
+ it 'removes redundant joins from top query' do
144
+ s = Article.ransack(tags_name_not_eq: "Fantasy")
145
+ sql = s.result.to_sql
146
+ expect(sql).to_not include('LEFT OUTER JOIN')
147
+ end
148
+
149
+ it 'handles != for single values' do
150
+ s = Article.ransack(tags_name_not_eq: "Fantasy")
151
+ articles = s.result.to_a
152
+ expect(articles).to include marco
153
+ expect(articles).to_not include arthur
154
+ end
155
+
156
+ it 'handles NOT IN for multiple attributes' do
157
+ s = Article.ransack(tags_name_not_in: ["Fantasy", "Scifi"])
158
+ articles = s.result.to_a
159
+
160
+ expect(articles).to include marco
161
+ expect(articles).to_not include arthur
162
+ end
163
+ end
164
+
165
+ context 'negative conditions on self-referenced associations' do
166
+ let(:pop) { Person.create!(name: 'Grandpa') }
167
+ let(:dad) { Person.create!(name: 'Father') }
168
+ let(:mom) { Person.create!(name: 'Mother') }
169
+ let(:son) { Person.create!(name: 'Grandchild') }
170
+
171
+ before do
172
+ son.parent = dad
173
+ dad.parent = pop
174
+ dad.children << son
175
+ mom.children << son
176
+ pop.children << dad
177
+ son.save! && dad.save! && mom.save! && pop.save!
178
+ end
179
+
180
+ it 'handles multiple associations and aliases' do
181
+ s = Person.ransack(
182
+ c: {
183
+ '0' => { a: ['name'], p: 'not_eq', v: ['Father'] },
184
+ '1' => {
185
+ a: ['children_name', 'parent_name'],
186
+ p: 'not_eq', v: ['Father'], m: 'or'
187
+ },
188
+ '2' => { a: ['children_salary'], p: 'eq', v: [nil] }
189
+ })
190
+ people = s.result
191
+
192
+ expect(people.to_a).to include son
193
+ expect(people.to_a).to include mom
194
+ expect(people.to_a).to_not include dad # rule '0': 'name'
195
+ expect(people.to_a).to_not include pop # rule '1': 'children_name'
196
+ end
197
+ end
198
+
199
+ describe '#ransack_alias' do
200
+ it 'translates an alias to the correct attributes' do
201
+ p = Person.create!(name: 'Meatloaf', email: 'babies@example.com')
202
+
203
+ s = Person.ransack(term_cont: 'atlo')
204
+ expect(s.result.to_a).to eq [p]
205
+
206
+ s = Person.ransack(term_cont: 'babi')
207
+ expect(s.result.to_a).to eq [p]
208
+
209
+ s = Person.ransack(term_cont: 'nomatch')
210
+ expect(s.result.to_a).to eq []
211
+ end
212
+
213
+ it 'also works with associations' do
214
+ dad = Person.create!(name: 'Birdman')
215
+ son = Person.create!(name: 'Weezy', parent: dad)
216
+
217
+ s = Person.ransack(daddy_eq: 'Birdman')
218
+ expect(s.result.to_a).to eq [son]
219
+
220
+ s = Person.ransack(daddy_eq: 'Drake')
221
+ expect(s.result.to_a).to eq []
222
+ end
223
+
224
+ it 'makes aliases available to subclasses' do
225
+ yngwie = Musician.create!(name: 'Yngwie Malmsteen')
226
+
227
+ musicians = Musician.ransack(term_cont: 'ngw').result
228
+ expect(musicians).to eq([yngwie])
229
+ end
230
+
231
+ it 'handles naming collisions gracefully' do
232
+ frank = Person.create!(name: 'Frank Stallone')
233
+
234
+ people = Person.ransack(term_cont: 'allon').result
235
+ expect(people).to eq([frank])
236
+
237
+ Class.new(Article) do
238
+ ransack_alias :term, :title
239
+ end
240
+
241
+ people = Person.ransack(term_cont: 'allon').result
242
+ expect(people).to eq([frank])
243
+ end
244
+ end
245
+
83
246
  describe '#ransacker' do
84
247
  # For infix tests
85
248
  def self.sane_adapter?
86
249
  case ::ActiveRecord::Base.connection.adapter_name
87
- when "SQLite3", "PostgreSQL"
250
+ when 'SQLite3', 'PostgreSQL'
88
251
  true
89
252
  else
90
253
  false
@@ -102,87 +265,128 @@ module Ransack
102
265
  # end
103
266
 
104
267
  it 'creates ransack attributes' do
105
- s = Person.ransack(:reversed_name_eq => 'htimS cirA')
268
+ person = Person.create!(name: 'Aric Smith')
269
+
270
+ s = Person.ransack(reversed_name_eq: 'htimS cirA')
106
271
  expect(s.result.size).to eq(1)
107
272
 
108
- expect(s.result.first).to eq Person.where(name: 'Aric Smith').first
273
+ expect(s.result.first).to eq person
109
274
  end
110
275
 
111
276
  it 'can be accessed through associations' do
112
- s = Person.ransack(:children_reversed_name_eq => 'htimS cirA')
277
+ s = Person.ransack(children_reversed_name_eq: 'htimS cirA')
113
278
  expect(s.result.to_sql).to match(
114
279
  /#{quote_table_name("children_people")}.#{
115
280
  quote_column_name("name")} = 'Aric Smith'/
116
281
  )
117
282
  end
118
283
 
119
- it 'allows an "attribute" to be an InfixOperation' do
120
- s = Person.ransack(:doubled_name_eq => 'Aric SmithAric Smith')
284
+ it 'allows an attribute to be an InfixOperation' do
285
+ s = Person.ransack(doubled_name_eq: 'Aric SmithAric Smith')
121
286
  expect(s.result.first).to eq Person.where(name: 'Aric Smith').first
122
287
  end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
123
288
 
124
- it "doesn't break #count if using InfixOperations" do
125
- s = Person.ransack(:doubled_name_eq => 'Aric SmithAric Smith')
289
+ it 'does not break #count if using InfixOperations' do
290
+ s = Person.ransack(doubled_name_eq: 'Aric SmithAric Smith')
126
291
  expect(s.result.count).to eq 1
127
292
  end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
128
293
 
129
- it "should remove empty key value pairs from the params hash" do
130
- s = Person.ransack(:children_reversed_name_eq => '')
294
+ it 'should remove empty key value pairs from the params hash' do
295
+ s = Person.ransack(children_reversed_name_eq: '')
131
296
  expect(s.result.to_sql).not_to match /LEFT OUTER JOIN/
132
297
  end
133
298
 
134
- it "should keep proper key value pairs in the params hash" do
135
- s = Person.ransack(:children_reversed_name_eq => 'Testing')
299
+ it 'should keep proper key value pairs in the params hash' do
300
+ s = Person.ransack(children_reversed_name_eq: 'Testing')
136
301
  expect(s.result.to_sql).to match /LEFT OUTER JOIN/
137
302
  end
138
303
 
139
- it "should function correctly when nil is passed in" do
304
+ it 'should function correctly when nil is passed in' do
140
305
  s = Person.ransack(nil)
141
306
  end
142
307
 
143
- it "should function correctly when a blank string is passed in" do
308
+ it 'should function correctly when a blank string is passed in' do
144
309
  s = Person.ransack('')
145
310
  end
146
311
 
147
- it "should function correctly with a multi-parameter attribute" do
312
+ it 'should function correctly with a multi-parameter attribute' do
313
+ ::ActiveRecord::Base.default_timezone = :utc
314
+ Time.zone = 'UTC'
315
+
148
316
  date = Date.current
149
317
  s = Person.ransack(
150
- { "created_at_gteq(1i)" => date.year,
151
- "created_at_gteq(2i)" => date.month,
152
- "created_at_gteq(3i)" => date.day
318
+ { 'created_at_gteq(1i)' => date.year,
319
+ 'created_at_gteq(2i)' => date.month,
320
+ 'created_at_gteq(3i)' => date.day
153
321
  }
154
322
  )
155
323
  expect(s.result.to_sql).to match />=/
156
324
  expect(s.result.to_sql).to match date.to_s
157
325
  end
158
326
 
159
- it "should function correctly when using fields with dots in them" do
160
- s = Person.ransack(:email_cont => "example.com")
327
+ it 'should function correctly when using fields with dots in them' do
328
+ s = Person.ransack(email_cont: 'example.com')
161
329
  expect(s.result.exists?).to be true
162
330
  end
163
331
 
164
- it "should function correctly when using fields with % in them" do
165
- p = Person.create!(:name => "110%-er")
166
- s = Person.ransack(:name_cont => "10%")
332
+ it 'should function correctly when using fields with % in them' do
333
+ p = Person.create!(name: '110%-er')
334
+ s = Person.ransack(name_cont: '10%')
167
335
  expect(s.result.to_a).to eq [p]
168
336
  end
169
337
 
170
- it "should function correctly when using fields with backslashes in them" do
171
- p = Person.create!(:name => "\\WINNER\\")
172
- s = Person.ransack(:name_cont => "\\WINNER\\")
338
+ it 'should function correctly when using fields with backslashes in them' do
339
+ p = Person.create!(name: "\\WINNER\\")
340
+ s = Person.ransack(name_cont: "\\WINNER\\")
173
341
  expect(s.result.to_a).to eq [p]
174
342
  end
175
343
 
176
- context "searching on an `in` predicate with a ransacker" do
177
- it "should function correctly when passing an array of ids" do
178
- s = Person.ransack(array_users_in: true)
344
+ context 'searching by underscores' do
345
+ # when escaping is supported right in LIKE expression without adding extra expressions
346
+ def self.simple_escaping?
347
+ case ::ActiveRecord::Base.connection.adapter_name
348
+ when 'Mysql2', 'PostgreSQL'
349
+ true
350
+ else
351
+ false
352
+ end
353
+ end
354
+
355
+ it 'should search correctly if matches exist' do
356
+ p = Person.create!(name: 'name_with_underscore')
357
+ s = Person.ransack(name_cont: 'name_')
358
+ expect(s.result.to_a).to eq [p]
359
+ end if simple_escaping?
360
+
361
+ it 'should return empty result if no matches' do
362
+ Person.create!(name: 'name_with_underscore')
363
+ s = Person.ransack(name_cont: 'n_')
364
+ expect(s.result.to_a).to eq []
365
+ end if simple_escaping?
366
+ end
367
+
368
+ context 'searching on an `in` predicate with a ransacker' do
369
+ it 'should function correctly when passing an array of ids' do
370
+ s = Person.ransack(array_people_ids_in: true)
179
371
  expect(s.result.count).to be > 0
372
+
373
+ s = Person.ransack(array_where_people_ids_in: [1, '2', 3])
374
+ expect(s.result.count).to be 3
375
+ expect(s.result.map(&:id)).to eq [3, 2, 1]
180
376
  end
181
377
 
182
- it "should function correctly when passing an array of strings" do
183
- Person.create!(name: Person.first.id.to_s)
184
- s = Person.ransack(array_names_in: true)
378
+ it 'should function correctly when passing an array of strings' do
379
+ a, b = Person.select(:id).order(:id).limit(2).map { |a| a.id.to_s }
380
+
381
+ Person.create!(name: a)
382
+ s = Person.ransack(array_people_names_in: true)
185
383
  expect(s.result.count).to be > 0
384
+ s = Person.ransack(array_where_people_names_in: a)
385
+ expect(s.result.count).to be 1
386
+
387
+ Person.create!(name: b)
388
+ s = Person.ransack(array_where_people_names_in: [a, b])
389
+ expect(s.result.count).to be 2
186
390
  end
187
391
 
188
392
  it 'should function correctly with an Arel SqlLiteral' do
@@ -193,54 +397,77 @@ module Ransack
193
397
  end
194
398
  end
195
399
 
196
- context "search on an `in` predicate with an array" do
197
- it "should function correctly when passing an array of ids" do
400
+ context 'search on an `in` predicate with an array' do
401
+ it 'should function correctly when passing an array of ids' do
198
402
  array = Person.all.map(&:id)
199
403
  s = Person.ransack(id_in: array)
200
404
  expect(s.result.count).to eq array.size
201
405
  end
202
406
  end
203
407
 
204
- it "should function correctly when an attribute name ends with '_start'" do
205
- p = Person.create!(:new_start => 'Bar and foo', :name => 'Xiang')
408
+ it 'should work correctly when an attribute name ends with _start' do
409
+ p = Person.create!(new_start: 'Bar and foo', name: 'Xiang')
206
410
 
207
- s = Person.ransack(:new_start_end => ' and foo')
411
+ s = Person.ransack(new_start_end: ' and foo')
208
412
  expect(s.result.to_a).to eq [p]
209
413
 
210
- s = Person.ransack(:name_or_new_start_start => 'Xia')
414
+ s = Person.ransack(name_or_new_start_start: 'Xia')
211
415
  expect(s.result.to_a).to eq [p]
212
416
 
213
- s = Person.ransack(:new_start_or_name_end => 'iang')
417
+ s = Person.ransack(new_start_or_name_end: 'iang')
214
418
  expect(s.result.to_a).to eq [p]
215
419
  end
216
420
 
217
- it "should function correctly when an attribute name ends with '_end'" do
218
- p = Person.create!(:stop_end => 'Foo and bar', :name => 'Marianne')
421
+ it 'should work correctly when an attribute name ends with _end' do
422
+ p = Person.create!(stop_end: 'Foo and bar', name: 'Marianne')
219
423
 
220
- s = Person.ransack(:stop_end_start => 'Foo and')
424
+ s = Person.ransack(stop_end_start: 'Foo and')
221
425
  expect(s.result.to_a).to eq [p]
222
426
 
223
- s = Person.ransack(:stop_end_or_name_end => 'anne')
427
+ s = Person.ransack(stop_end_or_name_end: 'anne')
224
428
  expect(s.result.to_a).to eq [p]
225
429
 
226
- s = Person.ransack(:name_or_stop_end_end => ' bar')
430
+ s = Person.ransack(name_or_stop_end_end: ' bar')
431
+ expect(s.result.to_a).to eq [p]
432
+ end
433
+
434
+ it 'should work correctly when an attribute name has `and` in it' do
435
+ p = Person.create!(terms_and_conditions: true)
436
+ s = Person.ransack(terms_and_conditions_eq: true)
227
437
  expect(s.result.to_a).to eq [p]
228
438
  end
229
439
 
230
- it "should function correctly when an attribute name has 'and' in it" do
231
- # FIXME: this test does not pass!
232
- p = Person.create!(:terms_and_conditions => true)
233
- s = Person.ransack(:terms_and_conditions_eq => true)
234
- # search is not detecting the attribute
235
- puts "
236
- FIXME: Search not detecting the `terms_and_conditions` attribute in
237
- base_spec.rb, line 178: #{s.result.to_sql}"
238
- # expect(s.result.to_a).to eq [p]
440
+ context 'attribute aliased column names',
441
+ if: Ransack::SUPPORTS_ATTRIBUTE_ALIAS do
442
+ it 'should be translated to original column name' do
443
+ s = Person.ransack(full_name_eq: 'Nicolas Cage')
444
+ expect(s.result.to_sql).to match(
445
+ /WHERE #{quote_table_name("people")}.#{quote_column_name("name")}/
446
+ )
447
+ end
448
+
449
+ it 'should translate on associations' do
450
+ s = Person.ransack(articles_content_cont: 'Nicolas Cage')
451
+ expect(s.result.to_sql).to match(
452
+ /#{quote_table_name("articles")}.#{
453
+ quote_column_name("body")} I?LIKE '%Nicolas Cage%'/
454
+ )
455
+ end
456
+ end
457
+
458
+ it 'sorts with different join variants' do
459
+ comments = [
460
+ Comment.create(article: Article.create(title: 'Avenger'), person: Person.create(salary: 100_000)),
461
+ Comment.create(article: Article.create(title: 'Avenge'), person: Person.create(salary: 50_000)),
462
+ ]
463
+ expect(Comment.ransack(article_title_cont: 'aven',s: 'person_salary desc').result).to eq(comments)
464
+ expect(Comment.joins(:person).ransack(s: 'persons_salarydesc', article_title_cont: 'aven').result).to eq(comments)
465
+ expect(Comment.joins(:person).ransack(article_title_cont: 'aven',s: 'persons_salary desc').result).to eq(comments)
239
466
  end
240
467
 
241
- it 'allows sort by "only_sort" field' do
468
+ it 'allows sort by `only_sort` field' do
242
469
  s = Person.ransack(
243
- "s" => { "0" => { "dir" => "asc", "name" => "only_sort" } }
470
+ 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_sort' } }
244
471
  )
245
472
  expect(s.result.to_sql).to match(
246
473
  /ORDER BY #{quote_table_name("people")}.#{
@@ -248,9 +475,9 @@ module Ransack
248
475
  )
249
476
  end
250
477
 
251
- it "doesn't sort by 'only_search' field" do
478
+ it 'does not sort by `only_search` field' do
252
479
  s = Person.ransack(
253
- "s" => { "0" => { "dir" => "asc", "name" => "only_search" } }
480
+ 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_search' } }
254
481
  )
255
482
  expect(s.result.to_sql).not_to match(
256
483
  /ORDER BY #{quote_table_name("people")}.#{
@@ -258,25 +485,25 @@ module Ransack
258
485
  )
259
486
  end
260
487
 
261
- it 'allows search by "only_search" field' do
262
- s = Person.ransack(:only_search_eq => 'htimS cirA')
488
+ it 'allows search by `only_search` field' do
489
+ s = Person.ransack(only_search_eq: 'htimS cirA')
263
490
  expect(s.result.to_sql).to match(
264
491
  /WHERE #{quote_table_name("people")}.#{
265
492
  quote_column_name("only_search")} = 'htimS cirA'/
266
493
  )
267
494
  end
268
495
 
269
- it "can't be searched by 'only_sort'" do
270
- s = Person.ransack(:only_sort_eq => 'htimS cirA')
496
+ it 'cannot be searched by `only_sort`' do
497
+ s = Person.ransack(only_sort_eq: 'htimS cirA')
271
498
  expect(s.result.to_sql).not_to match(
272
499
  /WHERE #{quote_table_name("people")}.#{
273
500
  quote_column_name("only_sort")} = 'htimS cirA'/
274
501
  )
275
502
  end
276
503
 
277
- it 'allows sort by "only_admin" field, if auth_object: :admin' do
504
+ it 'allows sort by `only_admin` field, if auth_object: :admin' do
278
505
  s = Person.ransack(
279
- { "s" => { "0" => { "dir" => "asc", "name" => "only_admin" } } },
506
+ { 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_admin' } } },
280
507
  { auth_object: :admin }
281
508
  )
282
509
  expect(s.result.to_sql).to match(
@@ -285,9 +512,9 @@ module Ransack
285
512
  )
286
513
  end
287
514
 
288
- it "doesn't sort by 'only_admin' field, if auth_object: nil" do
515
+ it 'does not sort by `only_admin` field, if auth_object: nil' do
289
516
  s = Person.ransack(
290
- "s" => { "0" => { "dir" => "asc", "name" => "only_admin" } }
517
+ 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_admin' } }
291
518
  )
292
519
  expect(s.result.to_sql).not_to match(
293
520
  /ORDER BY #{quote_table_name("people")}.#{
@@ -295,10 +522,10 @@ module Ransack
295
522
  )
296
523
  end
297
524
 
298
- it 'allows search by "only_admin" field, if auth_object: :admin' do
525
+ it 'allows search by `only_admin` field, if auth_object: :admin' do
299
526
  s = Person.ransack(
300
- { :only_admin_eq => 'htimS cirA' },
301
- { :auth_object => :admin }
527
+ { only_admin_eq: 'htimS cirA' },
528
+ { auth_object: :admin }
302
529
  )
303
530
  expect(s.result.to_sql).to match(
304
531
  /WHERE #{quote_table_name("people")}.#{
@@ -306,14 +533,15 @@ module Ransack
306
533
  )
307
534
  end
308
535
 
309
- it "can't be searched by 'only_admin', if auth_object: nil" do
310
- s = Person.ransack(:only_admin_eq => 'htimS cirA')
536
+ it 'cannot be searched by `only_admin`, if auth_object: nil' do
537
+ s = Person.ransack(only_admin_eq: 'htimS cirA')
311
538
  expect(s.result.to_sql).not_to match(
312
539
  /WHERE #{quote_table_name("people")}.#{
313
540
  quote_column_name("only_admin")} = 'htimS cirA'/
314
541
  )
315
542
  end
316
543
 
544
+
317
545
  it 'should allow passing ransacker arguments to a ransacker' do
318
546
  s = Person.ransack(
319
547
  c: [{
@@ -334,6 +562,56 @@ module Ransack
334
562
  )
335
563
  expect { s.result.first }.to_not raise_error
336
564
  end
565
+
566
+ it 'should allow sort passing arguments to a ransacker' do
567
+ s = Person.ransack(
568
+ s: {
569
+ '0' => {
570
+ name: 'with_arguments', dir: 'desc', ransacker_args: [2, 6]
571
+ }
572
+ }
573
+ )
574
+ expect(s.result.to_sql).to match(
575
+ /ORDER BY \(SELECT MAX\(articles.title\) FROM articles/
576
+ )
577
+ expect(s.result.to_sql).to match(
578
+ /WHERE articles.person_id = people.id AND LENGTH\(articles.body\)/
579
+ )
580
+ expect(s.result.to_sql).to match(
581
+ /BETWEEN 2 AND 6 GROUP BY articles.person_id \) DESC/
582
+ )
583
+ end
584
+
585
+ context 'case insensitive sorting' do
586
+ it 'allows sort by desc' do
587
+ search = Person.ransack(sorts: ['name_case_insensitive desc'])
588
+ expect(search.result.to_sql).to match /ORDER BY LOWER(.*) DESC/
589
+ end
590
+
591
+ it 'allows sort by asc' do
592
+ search = Person.ransack(sorts: ['name_case_insensitive asc'])
593
+ expect(search.result.to_sql).to match /ORDER BY LOWER(.*) ASC/
594
+ end
595
+ end
596
+
597
+ context 'regular sorting' do
598
+ it 'allows sort by desc' do
599
+ search = Person.ransack(sorts: ['name desc'])
600
+ expect(search.result.to_sql).to match /ORDER BY .* DESC/
601
+ end
602
+
603
+ it 'allows sort by asc' do
604
+ search = Person.ransack(sorts: ['name asc'])
605
+ expect(search.result.to_sql).to match /ORDER BY .* ASC/
606
+ end
607
+ end
608
+
609
+ context 'sorting by a scope' do
610
+ it 'applies the correct scope' do
611
+ search = Person.ransack(sorts: ['reverse_name asc'])
612
+ expect(search.result.to_sql).to include("ORDER BY REVERSE(name) ASC")
613
+ end
614
+ end
337
615
  end
338
616
 
339
617
  describe '#ransackable_attributes' do
@@ -343,9 +621,14 @@ module Ransack
343
621
  it { should include 'name' }
344
622
  it { should include 'reversed_name' }
345
623
  it { should include 'doubled_name' }
624
+ it { should include 'term' }
346
625
  it { should include 'only_search' }
347
626
  it { should_not include 'only_sort' }
348
627
  it { should_not include 'only_admin' }
628
+
629
+ if Ransack::SUPPORTS_ATTRIBUTE_ALIAS
630
+ it { should include 'full_name' }
631
+ end
349
632
  end
350
633
 
351
634
  context 'with auth_object :admin' do
@@ -398,6 +681,12 @@ module Ransack
398
681
  it { should eq [] }
399
682
  end
400
683
 
684
+ describe '#ransackable_scopes_skip_sanitize_args' do
685
+ subject { Person.ransackable_scopes_skip_sanitize_args }
686
+
687
+ it { should eq [] }
688
+ end
689
+
401
690
  end
402
691
  end
403
692
  end