ransack 1.7.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -0
  3. data/.travis.yml +16 -48
  4. data/CHANGELOG.md +409 -26
  5. data/CONTRIBUTING.md +48 -20
  6. data/Gemfile +9 -13
  7. data/README.md +352 -92
  8. data/Rakefile +6 -25
  9. data/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +2 -0
  10. data/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +2 -0
  11. data/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +32 -0
  12. data/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +112 -0
  13. data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +32 -0
  14. data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +113 -0
  15. data/lib/polyamorous/activerecord_5.2.1_ruby_2/join_association.rb +31 -0
  16. data/lib/polyamorous/activerecord_5.2.1_ruby_2/join_dependency.rb +57 -0
  17. data/lib/polyamorous/join.rb +70 -0
  18. data/lib/polyamorous/swapping_reflection_class.rb +11 -0
  19. data/lib/polyamorous/tree_node.rb +7 -0
  20. data/lib/polyamorous.rb +25 -0
  21. data/lib/ransack/adapters/active_record/base.rb +23 -2
  22. data/lib/ransack/adapters/active_record/context.rb +210 -135
  23. data/lib/ransack/adapters/active_record/ransack/constants.rb +53 -53
  24. data/lib/ransack/adapters/active_record/ransack/context.rb +11 -15
  25. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +33 -30
  26. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
  27. data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
  28. data/lib/ransack/adapters/active_record.rb +11 -10
  29. data/lib/ransack/adapters.rb +45 -23
  30. data/lib/ransack/configuration.rb +91 -4
  31. data/lib/ransack/constants.rb +14 -26
  32. data/lib/ransack/context.rb +29 -18
  33. data/lib/ransack/helpers/form_builder.rb +27 -12
  34. data/lib/ransack/helpers/form_helper.rb +75 -70
  35. data/lib/ransack/locale/ar.yml +70 -0
  36. data/lib/ransack/locale/az.yml +70 -0
  37. data/lib/ransack/locale/bg.yml +70 -0
  38. data/lib/ransack/locale/da.yml +70 -0
  39. data/lib/ransack/locale/el.yml +70 -0
  40. data/lib/ransack/locale/id.yml +70 -0
  41. data/lib/ransack/locale/it.yml +70 -0
  42. data/lib/ransack/locale/ja.yml +70 -0
  43. data/lib/ransack/locale/nl.yml +4 -4
  44. data/lib/ransack/locale/pt-BR.yml +70 -0
  45. data/lib/ransack/locale/ru.yml +70 -0
  46. data/lib/ransack/locale/tr.yml +70 -0
  47. data/lib/ransack/locale/{zh.yml → zh-CN.yml} +13 -13
  48. data/lib/ransack/locale/zh-TW.yml +70 -0
  49. data/lib/ransack/nodes/attribute.rb +5 -2
  50. data/lib/ransack/nodes/bindable.rb +18 -6
  51. data/lib/ransack/nodes/condition.rb +77 -28
  52. data/lib/ransack/nodes/grouping.rb +16 -10
  53. data/lib/ransack/nodes/sort.rb +9 -5
  54. data/lib/ransack/nodes/value.rb +74 -68
  55. data/lib/ransack/nodes.rb +1 -1
  56. data/lib/ransack/predicate.rb +15 -19
  57. data/lib/ransack/search.rb +14 -7
  58. data/lib/ransack/translate.rb +3 -3
  59. data/lib/ransack/version.rb +1 -1
  60. data/lib/ransack/visitor.rb +1 -12
  61. data/lib/ransack.rb +7 -7
  62. data/logo/ransack-h.png +0 -0
  63. data/logo/ransack-h.svg +34 -0
  64. data/logo/ransack-v.png +0 -0
  65. data/logo/ransack-v.svg +34 -0
  66. data/logo/ransack.png +0 -0
  67. data/logo/ransack.svg +21 -0
  68. data/ransack.gemspec +9 -10
  69. data/spec/console.rb +4 -0
  70. data/spec/helpers/polyamorous_helper.rb +24 -0
  71. data/spec/ransack/adapters/active_record/base_spec.rb +365 -74
  72. data/spec/ransack/adapters/active_record/context_spec.rb +14 -19
  73. data/spec/ransack/configuration_spec.rb +87 -14
  74. data/spec/ransack/helpers/form_builder_spec.rb +2 -11
  75. data/spec/ransack/helpers/form_helper_spec.rb +481 -113
  76. data/spec/ransack/join_association_spec.rb +28 -0
  77. data/spec/ransack/join_dependency_spec.rb +86 -0
  78. data/spec/ransack/join_spec.rb +19 -0
  79. data/spec/ransack/nodes/condition_spec.rb +24 -0
  80. data/spec/ransack/nodes/grouping_spec.rb +56 -0
  81. data/spec/ransack/predicate_spec.rb +27 -5
  82. data/spec/ransack/search_spec.rb +84 -70
  83. data/spec/spec_helper.rb +4 -0
  84. data/spec/support/schema.rb +86 -41
  85. metadata +60 -81
  86. data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
  87. data/lib/ransack/adapters/active_record/3.0/context.rb +0 -201
  88. data/lib/ransack/adapters/active_record/3.1/context.rb +0 -215
  89. data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
  90. data/lib/ransack/adapters/active_record/compat.rb +0 -14
  91. data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
  92. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
  93. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
  94. data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
  95. data/lib/ransack/adapters/mongoid/base.rb +0 -130
  96. data/lib/ransack/adapters/mongoid/context.rb +0 -208
  97. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
  98. data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
  99. data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
  100. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
  101. data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
  102. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
  103. data/lib/ransack/adapters/mongoid/table.rb +0 -35
  104. data/lib/ransack/adapters/mongoid.rb +0 -13
  105. data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -276
  106. data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
  107. data/spec/mongoid/configuration_spec.rb +0 -102
  108. data/spec/mongoid/dependencies_spec.rb +0 -8
  109. data/spec/mongoid/helpers/ransack_helper.rb +0 -11
  110. data/spec/mongoid/nodes/condition_spec.rb +0 -34
  111. data/spec/mongoid/nodes/grouping_spec.rb +0 -13
  112. data/spec/mongoid/predicate_spec.rb +0 -155
  113. data/spec/mongoid/search_spec.rb +0 -446
  114. data/spec/mongoid/support/mongoid.yml +0 -6
  115. data/spec/mongoid/support/schema.rb +0 -128
  116. data/spec/mongoid/translate_spec.rb +0 -14
  117. data/spec/mongoid_spec_helper.rb +0 -59
  118. data/spec/ransack/dependencies_spec.rb +0 -12
@@ -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
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
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
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
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
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
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
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
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
77
  expect(s.result.to_sql).to (include 'age > 18')
69
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,133 @@ 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
+
147
+ expect(sql).to_not include('LEFT OUTER JOIN')
148
+ end
149
+
150
+ it 'handles != for single values' do
151
+ s = Article.ransack(tags_name_not_eq: "Fantasy")
152
+ articles = s.result.to_a
153
+
154
+ expect(articles).to include marco
155
+ expect(articles).to_not include arthur
156
+ end
157
+
158
+ it 'handles NOT IN for multiple attributes' do
159
+ s = Article.ransack(tags_name_not_in: ["Fantasy", "Scifi"])
160
+ articles = s.result.to_a
161
+
162
+ expect(articles).to include marco
163
+ expect(articles).to_not include arthur
164
+ end
165
+ end
166
+
167
+ context 'negative conditions on self-referenced associations' do
168
+ let(:pop) { Person.create!(name: 'Grandpa') }
169
+ let(:dad) { Person.create!(name: 'Father') }
170
+ let(:mom) { Person.create!(name: 'Mother') }
171
+ let(:son) { Person.create!(name: 'Grandchild') }
172
+
173
+ before do
174
+ son.parent = dad
175
+ dad.parent = pop
176
+ dad.children << son
177
+ mom.children << son
178
+ pop.children << dad
179
+ son.save! && dad.save! && mom.save! && pop.save!
180
+ end
181
+
182
+ it 'handles multiple associations and aliases' do
183
+ s = Person.ransack(
184
+ c: {
185
+ '0' => { a: ['name'], p: 'not_eq', v: ['Father'] },
186
+ '1' => {
187
+ a: ['children_name', 'parent_name'],
188
+ p: 'not_eq', v: ['Father'], m: 'or'
189
+ },
190
+ '2' => { a: ['children_salary'], p: 'eq', v: [nil] }
191
+ })
192
+ people = s.result
193
+
194
+ expect(people.to_a).to include son
195
+ expect(people.to_a).to include mom
196
+ expect(people.to_a).to_not include dad # rule '0': 'name'
197
+ expect(people.to_a).to_not include pop # rule '1': 'children_name'
198
+ end
199
+ end
200
+
201
+ describe '#ransack_alias' do
202
+ it 'translates an alias to the correct attributes' do
203
+ p = Person.create!(name: 'Meatloaf', email: 'babies@example.com')
204
+
205
+ s = Person.ransack(term_cont: 'atlo')
206
+ expect(s.result.to_a).to eq [p]
207
+
208
+ s = Person.ransack(term_cont: 'babi')
209
+ expect(s.result.to_a).to eq [p]
210
+
211
+ s = Person.ransack(term_cont: 'nomatch')
212
+ expect(s.result.to_a).to eq []
213
+ end
214
+
215
+ it 'also works with associations' do
216
+ dad = Person.create!(name: 'Birdman')
217
+ son = Person.create!(name: 'Weezy', parent: dad)
218
+
219
+ s = Person.ransack(daddy_eq: 'Birdman')
220
+ expect(s.result.to_a).to eq [son]
221
+
222
+ s = Person.ransack(daddy_eq: 'Drake')
223
+ expect(s.result.to_a).to eq []
224
+ end
225
+
226
+ it 'makes aliases available to subclasses' do
227
+ yngwie = Musician.create!(name: 'Yngwie Malmsteen')
228
+
229
+ musicians = Musician.ransack(term_cont: 'ngw').result
230
+ expect(musicians).to eq([yngwie])
231
+ end
232
+
233
+ it 'handles naming collisions gracefully' do
234
+ frank = Person.create!(name: 'Frank Stallone')
235
+
236
+ people = Person.ransack(term_cont: 'allon').result
237
+ expect(people).to eq([frank])
238
+
239
+ Class.new(Article) do
240
+ ransack_alias :term, :title
241
+ end
242
+
243
+ people = Person.ransack(term_cont: 'allon').result
244
+ expect(people).to eq([frank])
245
+ end
246
+ end
247
+
83
248
  describe '#ransacker' do
84
249
  # For infix tests
85
250
  def self.sane_adapter?
86
251
  case ::ActiveRecord::Base.connection.adapter_name
87
- when "SQLite3", "PostgreSQL"
252
+ when 'SQLite3', 'PostgreSQL'
88
253
  true
89
254
  else
90
255
  false
@@ -102,90 +267,126 @@ module Ransack
102
267
  # end
103
268
 
104
269
  it 'creates ransack attributes' do
105
- s = Person.ransack(:reversed_name_eq => 'htimS cirA')
270
+ s = Person.ransack(reversed_name_eq: 'htimS cirA')
106
271
  expect(s.result.size).to eq(1)
107
272
 
108
273
  expect(s.result.first).to eq Person.where(name: 'Aric Smith').first
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
148
313
  ::ActiveRecord::Base.default_timezone = :utc
149
314
  Time.zone = 'UTC'
150
315
 
151
316
  date = Date.current
152
317
  s = Person.ransack(
153
- { "created_at_gteq(1i)" => date.year,
154
- "created_at_gteq(2i)" => date.month,
155
- "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
156
321
  }
157
322
  )
158
323
  expect(s.result.to_sql).to match />=/
159
324
  expect(s.result.to_sql).to match date.to_s
160
325
  end
161
326
 
162
- it "should function correctly when using fields with dots in them" do
163
- 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')
164
329
  expect(s.result.exists?).to be true
165
330
  end
166
331
 
167
- it "should function correctly when using fields with % in them" do
168
- p = Person.create!(:name => "110%-er")
169
- 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%')
170
335
  expect(s.result.to_a).to eq [p]
171
336
  end
172
337
 
173
- it "should function correctly when using fields with backslashes in them" do
174
- p = Person.create!(:name => "\\WINNER\\")
175
- 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\\")
176
341
  expect(s.result.to_a).to eq [p]
177
342
  end
178
343
 
179
- context "searching on an `in` predicate with a ransacker" do
180
- it "should function correctly when passing an array of ids" do
181
- 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)
182
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]
183
376
  end
184
377
 
185
- it "should function correctly when passing an array of strings" do
186
- Person.create!(name: Person.first.id.to_s)
187
- 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)
188
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
189
390
  end
190
391
 
191
392
  it 'should function correctly with an Arel SqlLiteral' do
@@ -196,49 +397,77 @@ module Ransack
196
397
  end
197
398
  end
198
399
 
199
- context "search on an `in` predicate with an array" do
200
- 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
201
402
  array = Person.all.map(&:id)
202
403
  s = Person.ransack(id_in: array)
203
404
  expect(s.result.count).to eq array.size
204
405
  end
205
406
  end
206
407
 
207
- it "should function correctly when an attribute name ends with '_start'" do
208
- 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')
209
410
 
210
- s = Person.ransack(:new_start_end => ' and foo')
411
+ s = Person.ransack(new_start_end: ' and foo')
211
412
  expect(s.result.to_a).to eq [p]
212
413
 
213
- s = Person.ransack(:name_or_new_start_start => 'Xia')
414
+ s = Person.ransack(name_or_new_start_start: 'Xia')
214
415
  expect(s.result.to_a).to eq [p]
215
416
 
216
- s = Person.ransack(:new_start_or_name_end => 'iang')
417
+ s = Person.ransack(new_start_or_name_end: 'iang')
217
418
  expect(s.result.to_a).to eq [p]
218
419
  end
219
420
 
220
- it "should function correctly when an attribute name ends with '_end'" do
221
- 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')
222
423
 
223
- s = Person.ransack(:stop_end_start => 'Foo and')
424
+ s = Person.ransack(stop_end_start: 'Foo and')
224
425
  expect(s.result.to_a).to eq [p]
225
426
 
226
- s = Person.ransack(:stop_end_or_name_end => 'anne')
427
+ s = Person.ransack(stop_end_or_name_end: 'anne')
227
428
  expect(s.result.to_a).to eq [p]
228
429
 
229
- s = Person.ransack(:name_or_stop_end_end => ' bar')
430
+ s = Person.ransack(name_or_stop_end_end: ' bar')
230
431
  expect(s.result.to_a).to eq [p]
231
432
  end
232
433
 
233
- it "should function correctly when an attribute name has 'and' in it" do
234
- p = Person.create!(:terms_and_conditions => true)
235
- s = Person.ransack(:terms_and_conditions_eq => true)
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)
236
437
  expect(s.result.to_a).to eq [p]
237
438
  end
238
439
 
239
- it 'allows sort by "only_sort" field' do
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)
466
+ end
467
+
468
+ it 'allows sort by `only_sort` field' do
240
469
  s = Person.ransack(
241
- "s" => { "0" => { "dir" => "asc", "name" => "only_sort" } }
470
+ 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_sort' } }
242
471
  )
243
472
  expect(s.result.to_sql).to match(
244
473
  /ORDER BY #{quote_table_name("people")}.#{
@@ -246,9 +475,9 @@ module Ransack
246
475
  )
247
476
  end
248
477
 
249
- it "doesn't sort by 'only_search' field" do
478
+ it 'does not sort by `only_search` field' do
250
479
  s = Person.ransack(
251
- "s" => { "0" => { "dir" => "asc", "name" => "only_search" } }
480
+ 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_search' } }
252
481
  )
253
482
  expect(s.result.to_sql).not_to match(
254
483
  /ORDER BY #{quote_table_name("people")}.#{
@@ -256,25 +485,25 @@ module Ransack
256
485
  )
257
486
  end
258
487
 
259
- it 'allows search by "only_search" field' do
260
- 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')
261
490
  expect(s.result.to_sql).to match(
262
491
  /WHERE #{quote_table_name("people")}.#{
263
492
  quote_column_name("only_search")} = 'htimS cirA'/
264
493
  )
265
494
  end
266
495
 
267
- it "can't be searched by 'only_sort'" do
268
- 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')
269
498
  expect(s.result.to_sql).not_to match(
270
499
  /WHERE #{quote_table_name("people")}.#{
271
500
  quote_column_name("only_sort")} = 'htimS cirA'/
272
501
  )
273
502
  end
274
503
 
275
- 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
276
505
  s = Person.ransack(
277
- { "s" => { "0" => { "dir" => "asc", "name" => "only_admin" } } },
506
+ { 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_admin' } } },
278
507
  { auth_object: :admin }
279
508
  )
280
509
  expect(s.result.to_sql).to match(
@@ -283,9 +512,9 @@ module Ransack
283
512
  )
284
513
  end
285
514
 
286
- 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
287
516
  s = Person.ransack(
288
- "s" => { "0" => { "dir" => "asc", "name" => "only_admin" } }
517
+ 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_admin' } }
289
518
  )
290
519
  expect(s.result.to_sql).not_to match(
291
520
  /ORDER BY #{quote_table_name("people")}.#{
@@ -293,10 +522,10 @@ module Ransack
293
522
  )
294
523
  end
295
524
 
296
- 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
297
526
  s = Person.ransack(
298
- { :only_admin_eq => 'htimS cirA' },
299
- { :auth_object => :admin }
527
+ { only_admin_eq: 'htimS cirA' },
528
+ { auth_object: :admin }
300
529
  )
301
530
  expect(s.result.to_sql).to match(
302
531
  /WHERE #{quote_table_name("people")}.#{
@@ -304,14 +533,15 @@ module Ransack
304
533
  )
305
534
  end
306
535
 
307
- it "can't be searched by 'only_admin', if auth_object: nil" do
308
- 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')
309
538
  expect(s.result.to_sql).not_to match(
310
539
  /WHERE #{quote_table_name("people")}.#{
311
540
  quote_column_name("only_admin")} = 'htimS cirA'/
312
541
  )
313
542
  end
314
543
 
544
+
315
545
  it 'should allow passing ransacker arguments to a ransacker' do
316
546
  s = Person.ransack(
317
547
  c: [{
@@ -332,6 +562,56 @@ module Ransack
332
562
  )
333
563
  expect { s.result.first }.to_not raise_error
334
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
335
615
  end
336
616
 
337
617
  describe '#ransackable_attributes' do
@@ -341,9 +621,14 @@ module Ransack
341
621
  it { should include 'name' }
342
622
  it { should include 'reversed_name' }
343
623
  it { should include 'doubled_name' }
624
+ it { should include 'term' }
344
625
  it { should include 'only_search' }
345
626
  it { should_not include 'only_sort' }
346
627
  it { should_not include 'only_admin' }
628
+
629
+ if Ransack::SUPPORTS_ATTRIBUTE_ALIAS
630
+ it { should include 'full_name' }
631
+ end
347
632
  end
348
633
 
349
634
  context 'with auth_object :admin' do
@@ -396,6 +681,12 @@ module Ransack
396
681
  it { should eq [] }
397
682
  end
398
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
+
399
690
  end
400
691
  end
401
692
  end