ransack 2.1.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/SECURITY.md +12 -0
- data/.github/workflows/cronjob.yml +102 -0
- data/.github/workflows/rubocop.yml +20 -0
- data/.github/workflows/test.yml +163 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +44 -0
- data/CHANGELOG.md +64 -1
- data/CONTRIBUTING.md +16 -11
- data/Gemfile +23 -17
- data/README.md +190 -57
- data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
- data/bug_report_templates/test-ransacker-arel-present-predicate.rb +71 -0
- data/docs/img/create_release.png +0 -0
- data/docs/release_process.md +17 -0
- data/lib/polyamorous/{activerecord_5.2.1_ruby_2 → activerecord_5.2_ruby_2}/join_association.rb +2 -9
- data/lib/polyamorous/{activerecord_5.2.1_ruby_2 → activerecord_5.2_ruby_2}/join_dependency.rb +25 -3
- data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +11 -0
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +1 -0
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +80 -0
- data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +1 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +74 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +93 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +1 -0
- data/lib/polyamorous/activerecord_7.0_ruby_2/join_association.rb +1 -0
- data/lib/polyamorous/activerecord_7.0_ruby_2/join_dependency.rb +1 -0
- data/lib/polyamorous/activerecord_7.0_ruby_2/reflection.rb +1 -0
- data/lib/polyamorous/polyamorous.rb +24 -0
- data/lib/polyamorous.rb +1 -25
- data/lib/ransack/adapters/active_record/base.rb +5 -1
- data/lib/ransack/adapters/active_record/context.rb +71 -68
- data/lib/ransack/adapters/active_record/ransack/constants.rb +18 -3
- data/lib/ransack/adapters/active_record/ransack/context.rb +2 -6
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +13 -5
- data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -1
- data/lib/ransack/configuration.rb +31 -1
- data/lib/ransack/constants.rb +3 -5
- data/lib/ransack/context.rb +19 -18
- data/lib/ransack/helpers/form_builder.rb +8 -14
- data/lib/ransack/helpers/form_helper.rb +1 -1
- data/lib/ransack/helpers.rb +1 -1
- data/lib/ransack/locale/az.yml +1 -1
- data/lib/ransack/locale/ca.yml +70 -0
- data/lib/ransack/locale/es.yml +22 -22
- data/lib/ransack/locale/fa.yml +70 -0
- data/lib/ransack/locale/fi.yml +71 -0
- data/lib/ransack/locale/sk.yml +70 -0
- data/lib/ransack/locale/sv.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +1 -1
- data/lib/ransack/nodes/condition.rb +7 -1
- data/lib/ransack/nodes/grouping.rb +1 -1
- data/lib/ransack/nodes/sort.rb +3 -3
- data/lib/ransack/nodes/value.rb +1 -1
- data/lib/ransack/predicate.rb +2 -1
- data/lib/ransack/search.rb +4 -1
- data/lib/ransack/translate.rb +115 -115
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack.rb +3 -3
- data/ransack.gemspec +8 -23
- data/spec/blueprints/articles.rb +1 -1
- data/spec/blueprints/comments.rb +1 -1
- data/spec/blueprints/notes.rb +1 -1
- data/spec/blueprints/tags.rb +1 -1
- data/spec/console.rb +5 -5
- data/spec/helpers/polyamorous_helper.rb +3 -8
- data/spec/helpers/ransack_helper.rb +1 -1
- data/spec/{ransack → polyamorous}/join_association_spec.rb +8 -1
- data/spec/{ransack → polyamorous}/join_dependency_spec.rb +18 -7
- data/spec/{ransack → polyamorous}/join_spec.rb +0 -0
- data/spec/ransack/adapters/active_record/base_spec.rb +26 -15
- data/spec/ransack/adapters/active_record/context_spec.rb +60 -18
- data/spec/ransack/configuration_spec.rb +24 -0
- data/spec/ransack/helpers/form_helper_spec.rb +16 -16
- data/spec/ransack/nodes/condition_spec.rb +13 -0
- data/spec/ransack/nodes/grouping_spec.rb +2 -2
- data/spec/ransack/predicate_spec.rb +54 -2
- data/spec/ransack/search_spec.rb +238 -36
- data/spec/spec_helper.rb +10 -5
- data/spec/support/schema.rb +37 -3
- metadata +45 -139
- data/.travis.yml +0 -37
- data/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +0 -2
- data/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +0 -2
- data/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +0 -32
- data/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +0 -112
- data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +0 -32
- data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +0 -113
data/spec/ransack/search_spec.rb
CHANGED
@@ -20,6 +20,44 @@ module Ransack
|
|
20
20
|
Search.new(Person, name_eq: 'foobar')
|
21
21
|
end
|
22
22
|
|
23
|
+
context 'whitespace stripping' do
|
24
|
+
context 'when whitespace_strip option is true' do
|
25
|
+
before do
|
26
|
+
Ransack.configure { |c| c.strip_whitespace = true }
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'strips leading & trailing whitespace before building' do
|
30
|
+
expect_any_instance_of(Search).to receive(:build)
|
31
|
+
.with({ 'name_eq' => 'foobar' })
|
32
|
+
Search.new(Person, name_eq: ' foobar ')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when whitespace_strip option is false' do
|
37
|
+
before do
|
38
|
+
Ransack.configure { |c| c.strip_whitespace = false }
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'doesn\'t strip leading & trailing whitespace before building' do
|
42
|
+
expect_any_instance_of(Search).to receive(:build)
|
43
|
+
.with({ 'name_eq' => ' foobar ' })
|
44
|
+
Search.new(Person, name_eq: ' foobar ')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'strips leading & trailing whitespace when strip_whitespace search parameter is true' do
|
49
|
+
expect_any_instance_of(Search).to receive(:build)
|
50
|
+
.with({ 'name_eq' => 'foobar' })
|
51
|
+
Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: true })
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'doesn\'t strip leading & trailing whitespace when strip_whitespace search parameter is false' do
|
55
|
+
expect_any_instance_of(Search).to receive(:build)
|
56
|
+
.with({ 'name_eq' => ' foobar ' })
|
57
|
+
Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: false })
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
23
61
|
it 'removes empty suffixed conditions before building' do
|
24
62
|
expect_any_instance_of(Search).to receive(:build).with({})
|
25
63
|
Search.new(Person, name_eq_any: [''])
|
@@ -109,6 +147,43 @@ module Ransack
|
|
109
147
|
expect(s.result.to_sql).to include 'published'
|
110
148
|
end
|
111
149
|
|
150
|
+
# The failure/oversight in Ransack::Nodes::Condition#arel_predicate or deeper is beyond my understanding of the structures
|
151
|
+
it 'preserves (inverts) default scope and conditions for negative subqueries' do
|
152
|
+
# the positive case (published_articles_title_eq) is
|
153
|
+
# SELECT "people".* FROM "people"
|
154
|
+
# LEFT OUTER JOIN "articles" ON "articles"."person_id" = "people"."id"
|
155
|
+
# AND "articles"."published" = 't'
|
156
|
+
# AND ('default_scope' = 'default_scope')
|
157
|
+
# WHERE "articles"."title" = 'Test' ORDER BY "people"."id" DESC
|
158
|
+
#
|
159
|
+
# negative case was
|
160
|
+
# SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
|
161
|
+
# SELECT "articles"."person_id" FROM "articles"
|
162
|
+
# WHERE "articles"."person_id" = "people"."id"
|
163
|
+
# AND NOT ("articles"."title" != 'Test')
|
164
|
+
# ) ORDER BY "people"."id" DESC
|
165
|
+
#
|
166
|
+
# Should have been like
|
167
|
+
# SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
|
168
|
+
# SELECT "articles"."person_id" FROM "articles"
|
169
|
+
# WHERE "articles"."person_id" = "people"."id"
|
170
|
+
# AND "articles"."title" = 'Test' AND "articles"."published" = 't' AND ('default_scope' = 'default_scope')
|
171
|
+
# ) ORDER BY "people"."id" DESC
|
172
|
+
#
|
173
|
+
# With tenanting (eg default_scope with column reference), NOT IN should be like
|
174
|
+
# SELECT "people".* FROM "people" WHERE "people"."tenant_id" = 'tenant_id' AND "people"."id" NOT IN (
|
175
|
+
# SELECT "articles"."person_id" FROM "articles"
|
176
|
+
# WHERE "articles"."person_id" = "people"."id"
|
177
|
+
# AND "articles"."tenant_id" = 'tenant_id'
|
178
|
+
# AND "articles"."title" = 'Test' AND "articles"."published" = 't' AND ('default_scope' = 'default_scope')
|
179
|
+
# ) ORDER BY "people"."id" DESC
|
180
|
+
|
181
|
+
pending("spec should pass, but I do not know how/where to fix lib code")
|
182
|
+
s = Search.new(Person, published_articles_title_not_eq: 'Test')
|
183
|
+
expect(s.result.to_sql).to include 'default_scope'
|
184
|
+
expect(s.result.to_sql).to include 'published'
|
185
|
+
end
|
186
|
+
|
112
187
|
it 'discards empty conditions' do
|
113
188
|
s = Search.new(Person, children_name_eq: '')
|
114
189
|
condition = s.base[:children_name_eq]
|
@@ -189,7 +264,7 @@ module Ransack
|
|
189
264
|
context 'with an invalid condition' do
|
190
265
|
subject { Search.new(Person, unknown_attr_eq: 'Ernie') }
|
191
266
|
|
192
|
-
context 'when ignore_unknown_conditions is false' do
|
267
|
+
context 'when ignore_unknown_conditions configuration option is false' do
|
193
268
|
before do
|
194
269
|
Ransack.configure { |c| c.ignore_unknown_conditions = false }
|
195
270
|
end
|
@@ -197,13 +272,39 @@ module Ransack
|
|
197
272
|
specify { expect { subject }.to raise_error ArgumentError }
|
198
273
|
end
|
199
274
|
|
200
|
-
context 'when ignore_unknown_conditions is true' do
|
275
|
+
context 'when ignore_unknown_conditions configuration option is true' do
|
201
276
|
before do
|
202
277
|
Ransack.configure { |c| c.ignore_unknown_conditions = true }
|
203
278
|
end
|
204
279
|
|
205
280
|
specify { expect { subject }.not_to raise_error }
|
206
281
|
end
|
282
|
+
|
283
|
+
subject(:with_ignore_unknown_conditions_false) {
|
284
|
+
Search.new(Person,
|
285
|
+
{ unknown_attr_eq: 'Ernie' },
|
286
|
+
{ ignore_unknown_conditions: false }
|
287
|
+
)
|
288
|
+
}
|
289
|
+
|
290
|
+
subject(:with_ignore_unknown_conditions_true) {
|
291
|
+
Search.new(Person,
|
292
|
+
{ unknown_attr_eq: 'Ernie' },
|
293
|
+
{ ignore_unknown_conditions: true }
|
294
|
+
)
|
295
|
+
}
|
296
|
+
|
297
|
+
context 'when ignore_unknown_conditions search parameter is absent' do
|
298
|
+
specify { expect { subject }.not_to raise_error }
|
299
|
+
end
|
300
|
+
|
301
|
+
context 'when ignore_unknown_conditions search parameter is false' do
|
302
|
+
specify { expect { with_ignore_unknown_conditions_false }.to raise_error ArgumentError }
|
303
|
+
end
|
304
|
+
|
305
|
+
context 'when ignore_unknown_conditions search parameter is true' do
|
306
|
+
specify { expect { with_ignore_unknown_conditions_true }.not_to raise_error }
|
307
|
+
end
|
207
308
|
end
|
208
309
|
|
209
310
|
it 'does not modify the parameters' do
|
@@ -220,6 +321,9 @@ module Ransack
|
|
220
321
|
let(:children_people_name_field) {
|
221
322
|
"#{quote_table_name("children_people")}.#{quote_column_name("name")}"
|
222
323
|
}
|
324
|
+
let(:notable_type_field) {
|
325
|
+
"#{quote_table_name("notes")}.#{quote_column_name("notable_type")}"
|
326
|
+
}
|
223
327
|
it 'evaluates conditions contextually' do
|
224
328
|
s = Search.new(Person, children_name_eq: 'Ernie')
|
225
329
|
expect(s.result).to be_an ActiveRecord::Relation
|
@@ -227,13 +331,32 @@ module Ransack
|
|
227
331
|
children_people_name_field} = 'Ernie'/
|
228
332
|
end
|
229
333
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
334
|
+
it 'use appropriate table alias' do
|
335
|
+
s = Search.new(Person, {
|
336
|
+
name_eq: "person_name_query",
|
337
|
+
articles_title_eq: "person_article_title_query",
|
338
|
+
parent_name_eq: "parent_name_query",
|
339
|
+
parent_articles_title_eq: 'parents_article_title_query'
|
340
|
+
}).result
|
341
|
+
|
342
|
+
real_query = remove_quotes_and_backticks(s.to_sql)
|
343
|
+
|
344
|
+
expect(real_query)
|
345
|
+
.to match(%r{LEFT OUTER JOIN articles ON (\('default_scope' = 'default_scope'\) AND )?articles.person_id = people.id})
|
346
|
+
expect(real_query)
|
347
|
+
.to match(%r{LEFT OUTER JOIN articles articles_people ON (\('default_scope' = 'default_scope'\) AND )?articles_people.person_id = parents_people.id})
|
348
|
+
|
349
|
+
expect(real_query)
|
350
|
+
.to include "people.name = 'person_name_query'"
|
351
|
+
expect(real_query)
|
352
|
+
.to include "articles.title = 'person_article_title_query'"
|
353
|
+
expect(real_query)
|
354
|
+
.to include "parents_people.name = 'parent_name_query'"
|
355
|
+
expect(real_query)
|
356
|
+
.to include "articles_people.title = 'parents_article_title_query'"
|
357
|
+
end
|
358
|
+
|
359
|
+
it 'evaluates conditions for multiple `belongs_to` associations to the same table contextually' do
|
237
360
|
s = Search.new(
|
238
361
|
Recommendation,
|
239
362
|
person_name_eq: 'Ernie',
|
@@ -248,7 +371,7 @@ module Ransack
|
|
248
371
|
ON target_people_recommendations.id = recommendations.target_person_id
|
249
372
|
LEFT OUTER JOIN people parents_people
|
250
373
|
ON parents_people.id = target_people_recommendations.parent_id
|
251
|
-
WHERE (
|
374
|
+
WHERE (people.name = 'Ernie' AND parents_people.name = 'Test')
|
252
375
|
SQL
|
253
376
|
.squish
|
254
377
|
expect(real_query).to eq expected_query
|
@@ -265,6 +388,7 @@ module Ransack
|
|
265
388
|
s = Search.new(Note, notable_of_Person_type_name_eq: 'Ernie').result
|
266
389
|
expect(s).to be_an ActiveRecord::Relation
|
267
390
|
expect(s.to_sql).to match /#{people_name_field} = 'Ernie'/
|
391
|
+
expect(s.to_sql).to match /#{notable_type_field} = 'Person'/
|
268
392
|
end
|
269
393
|
|
270
394
|
it 'evaluates nested conditions' do
|
@@ -303,11 +427,8 @@ module Ransack
|
|
303
427
|
{ m: 'or', comments_body_cont: 'e', articles_comments_body_cont: 'e' }
|
304
428
|
]
|
305
429
|
)
|
306
|
-
|
307
|
-
|
308
|
-
else
|
309
|
-
all_or_load, uniq_or_distinct = :load, :distinct
|
310
|
-
end
|
430
|
+
|
431
|
+
all_or_load, uniq_or_distinct = :load, :distinct
|
311
432
|
expect(s.result.send(all_or_load).size)
|
312
433
|
.to eq(9000)
|
313
434
|
expect(s.result(distinct: true).size)
|
@@ -360,88 +481,169 @@ module Ransack
|
|
360
481
|
expect(sort.dir).to eq 'asc'
|
361
482
|
end
|
362
483
|
|
363
|
-
it 'creates sorts based on
|
364
|
-
@s.sorts =
|
484
|
+
it 'creates sorts based on a single alias/direction' do
|
485
|
+
@s.sorts = 'daddy desc'
|
486
|
+
expect(@s.sorts.size).to eq(1)
|
487
|
+
sort = @s.sorts.first
|
488
|
+
expect(sort).to be_a Nodes::Sort
|
489
|
+
expect(sort.name).to eq 'parent_name'
|
490
|
+
expect(sort.dir).to eq 'desc'
|
491
|
+
end
|
492
|
+
|
493
|
+
it 'creates sorts based on a single alias and uppercase direction' do
|
494
|
+
@s.sorts = 'daddy DESC'
|
495
|
+
expect(@s.sorts.size).to eq(1)
|
496
|
+
sort = @s.sorts.first
|
497
|
+
expect(sort).to be_a Nodes::Sort
|
498
|
+
expect(sort.name).to eq 'parent_name'
|
499
|
+
expect(sort.dir).to eq 'desc'
|
500
|
+
end
|
501
|
+
|
502
|
+
it 'creates sorts based on a single alias and without direction' do
|
503
|
+
@s.sorts = 'daddy'
|
504
|
+
expect(@s.sorts.size).to eq(1)
|
505
|
+
sort = @s.sorts.first
|
506
|
+
expect(sort).to be_a Nodes::Sort
|
507
|
+
expect(sort.name).to eq 'parent_name'
|
508
|
+
expect(sort.dir).to eq 'asc'
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'creates sorts based on attributes, alias and directions in array format' do
|
512
|
+
@s.sorts = ['id desc', { name: 'daddy', dir: 'asc' }]
|
365
513
|
expect(@s.sorts.size).to eq(2)
|
366
514
|
sort1, sort2 = @s.sorts
|
367
515
|
expect(sort1).to be_a Nodes::Sort
|
368
516
|
expect(sort1.name).to eq 'id'
|
369
517
|
expect(sort1.dir).to eq 'desc'
|
370
518
|
expect(sort2).to be_a Nodes::Sort
|
371
|
-
expect(sort2.name).to eq '
|
519
|
+
expect(sort2.name).to eq 'parent_name'
|
372
520
|
expect(sort2.dir).to eq 'asc'
|
373
521
|
end
|
374
522
|
|
375
|
-
it 'creates sorts based on
|
376
|
-
@s.sorts = ['id DESC', { name: '
|
523
|
+
it 'creates sorts based on attributes, alias and uppercase directions in array format' do
|
524
|
+
@s.sorts = ['id DESC', { name: 'daddy', dir: 'ASC' }]
|
377
525
|
expect(@s.sorts.size).to eq(2)
|
378
526
|
sort1, sort2 = @s.sorts
|
379
527
|
expect(sort1).to be_a Nodes::Sort
|
380
528
|
expect(sort1.name).to eq 'id'
|
381
529
|
expect(sort1.dir).to eq 'desc'
|
382
530
|
expect(sort2).to be_a Nodes::Sort
|
383
|
-
expect(sort2.name).to eq '
|
531
|
+
expect(sort2.name).to eq 'parent_name'
|
384
532
|
expect(sort2.dir).to eq 'asc'
|
385
533
|
end
|
386
534
|
|
387
|
-
it 'creates sorts based on
|
535
|
+
it 'creates sorts based on attributes, alias and different directions
|
388
536
|
in array format' do
|
389
|
-
@s.sorts = ['id DESC', { name: '
|
537
|
+
@s.sorts = ['id DESC', { name: 'daddy', dir: nil }]
|
390
538
|
expect(@s.sorts.size).to eq(2)
|
391
539
|
sort1, sort2 = @s.sorts
|
392
540
|
expect(sort1).to be_a Nodes::Sort
|
393
541
|
expect(sort1.name).to eq 'id'
|
394
542
|
expect(sort1.dir).to eq 'desc'
|
395
543
|
expect(sort2).to be_a Nodes::Sort
|
396
|
-
expect(sort2.name).to eq '
|
544
|
+
expect(sort2.name).to eq 'parent_name'
|
397
545
|
expect(sort2.dir).to eq 'asc'
|
398
546
|
end
|
399
547
|
|
400
|
-
it 'creates sorts based on
|
548
|
+
it 'creates sorts based on attributes, alias and directions in hash format' do
|
401
549
|
@s.sorts = {
|
402
550
|
'0' => { name: 'id', dir: 'desc' },
|
403
|
-
'1' => { name: '
|
551
|
+
'1' => { name: 'daddy', dir: 'asc' }
|
404
552
|
}
|
405
553
|
expect(@s.sorts.size).to eq(2)
|
406
554
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
407
555
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
408
|
-
|
556
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
409
557
|
expect(id_sort.dir).to eq 'desc'
|
410
|
-
expect(
|
558
|
+
expect(daddy_sort.dir).to eq 'asc'
|
411
559
|
end
|
412
560
|
|
413
|
-
it 'creates sorts based on
|
561
|
+
it 'creates sorts based on attributes, alias and uppercase directions
|
414
562
|
in hash format' do
|
415
563
|
@s.sorts = {
|
416
564
|
'0' => { name: 'id', dir: 'DESC' },
|
417
|
-
'1' => { name: '
|
565
|
+
'1' => { name: 'daddy', dir: 'ASC' }
|
418
566
|
}
|
419
567
|
expect(@s.sorts.size).to eq(2)
|
420
568
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
421
569
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
422
|
-
|
570
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
423
571
|
expect(id_sort.dir).to eq 'desc'
|
424
|
-
expect(
|
572
|
+
expect(daddy_sort.dir).to eq 'asc'
|
425
573
|
end
|
426
574
|
|
427
|
-
it 'creates sorts based on
|
575
|
+
it 'creates sorts based on attributes, alias and different directions
|
428
576
|
in hash format' do
|
429
577
|
@s.sorts = {
|
430
578
|
'0' => { name: 'id', dir: 'DESC' },
|
431
|
-
'1' => { name: '
|
579
|
+
'1' => { name: 'daddy', dir: nil }
|
432
580
|
}
|
433
581
|
expect(@s.sorts.size).to eq(2)
|
434
582
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
435
583
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
436
|
-
|
584
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
437
585
|
expect(id_sort.dir).to eq 'desc'
|
438
|
-
expect(
|
586
|
+
expect(daddy_sort.dir).to eq 'asc'
|
439
587
|
end
|
440
588
|
|
441
589
|
it 'overrides existing sort' do
|
442
590
|
@s.sorts = 'id asc'
|
443
591
|
expect(@s.result.first.id).to eq 1
|
444
592
|
end
|
593
|
+
|
594
|
+
it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
|
595
|
+
default = Ransack.options.clone
|
596
|
+
|
597
|
+
s = Search.new(Person, s: 'name asc')
|
598
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC"
|
599
|
+
|
600
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
|
601
|
+
s = Search.new(Person, s: 'name asc')
|
602
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS FIRST"
|
603
|
+
s = Search.new(Person, s: 'name desc')
|
604
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS LAST"
|
605
|
+
|
606
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
|
607
|
+
s = Search.new(Person, s: 'name asc')
|
608
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS LAST"
|
609
|
+
s = Search.new(Person, s: 'name desc')
|
610
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS FIRST"
|
611
|
+
|
612
|
+
Ransack.options = default
|
613
|
+
end
|
614
|
+
|
615
|
+
it "PG's sort option with double name", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
|
616
|
+
default = Ransack.options.clone
|
617
|
+
|
618
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
619
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC"
|
620
|
+
|
621
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
|
622
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
623
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
|
624
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
625
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
|
626
|
+
|
627
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
|
628
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
629
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
|
630
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
631
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
|
632
|
+
|
633
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_first }
|
634
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
635
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
|
636
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
637
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
|
638
|
+
|
639
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_last }
|
640
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
641
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
|
642
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
643
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
|
644
|
+
|
645
|
+
Ransack.options = default
|
646
|
+
end
|
445
647
|
end
|
446
648
|
|
447
649
|
describe '#method_missing' do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
require 'machinist/active_record'
|
2
|
+
require 'polyamorous/polyamorous'
|
2
3
|
require 'sham'
|
3
4
|
require 'faker'
|
4
5
|
require 'ransack'
|
6
|
+
require 'action_controller'
|
7
|
+
require 'ransack/helpers'
|
5
8
|
require 'pry'
|
6
9
|
require 'simplecov'
|
10
|
+
require 'byebug'
|
7
11
|
|
8
12
|
SimpleCov.start
|
9
13
|
I18n.enforce_available_locales = false
|
@@ -13,16 +17,17 @@ I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'support', '*.yml')]
|
|
13
17
|
Dir[File.expand_path('../{helpers,support,blueprints}/*.rb', __FILE__)]
|
14
18
|
.each { |f| require f }
|
15
19
|
|
20
|
+
Faker::Config.random = Random.new(0)
|
16
21
|
Sham.define do
|
17
22
|
name { Faker::Name.name }
|
18
23
|
title { Faker::Lorem.sentence }
|
19
24
|
body { Faker::Lorem.paragraph }
|
20
25
|
salary { |index| 30000 + (index * 1000) }
|
21
|
-
tag_name { Faker::Lorem.words(3).join(' ') }
|
22
|
-
note { Faker::Lorem.words(7).join(' ') }
|
23
|
-
only_admin { Faker::Lorem.words(3).join(' ') }
|
24
|
-
only_search { Faker::Lorem.words(3).join(' ') }
|
25
|
-
only_sort { Faker::Lorem.words(3).join(' ') }
|
26
|
+
tag_name { Faker::Lorem.words(number: 3).join(' ') }
|
27
|
+
note { Faker::Lorem.words(number: 7).join(' ') }
|
28
|
+
only_admin { Faker::Lorem.words(number: 3).join(' ') }
|
29
|
+
only_search { Faker::Lorem.words(number: 3).join(' ') }
|
30
|
+
only_sort { Faker::Lorem.words(number: 3).join(' ') }
|
26
31
|
notable_id { |id| id }
|
27
32
|
end
|
28
33
|
|
data/spec/support/schema.rb
CHANGED
@@ -6,6 +6,8 @@ when 'mysql', 'mysql2'
|
|
6
6
|
ActiveRecord::Base.establish_connection(
|
7
7
|
adapter: 'mysql2',
|
8
8
|
database: 'ransack',
|
9
|
+
username: ENV.fetch("MYSQL_USERNAME") { "root" },
|
10
|
+
password: ENV.fetch("MYSQL_PASSWORD") { "" },
|
9
11
|
encoding: 'utf8'
|
10
12
|
)
|
11
13
|
when 'pg', 'postgres', 'postgresql'
|
@@ -13,7 +15,9 @@ when 'pg', 'postgres', 'postgresql'
|
|
13
15
|
ActiveRecord::Base.establish_connection(
|
14
16
|
adapter: 'postgresql',
|
15
17
|
database: 'ransack',
|
16
|
-
|
18
|
+
username: ENV.fetch("DATABASE_USERNAME") { "postgres" },
|
19
|
+
password: ENV.fetch("DATABASE_PASSWORD") { "" },
|
20
|
+
host: ENV.fetch("DATABASE_HOST") { "localhost" },
|
17
21
|
min_messages: 'warning'
|
18
22
|
)
|
19
23
|
else
|
@@ -29,6 +33,8 @@ class Person < ActiveRecord::Base
|
|
29
33
|
belongs_to :parent, class_name: 'Person', foreign_key: :parent_id
|
30
34
|
has_many :children, class_name: 'Person', foreign_key: :parent_id
|
31
35
|
has_many :articles
|
36
|
+
has_many :story_articles
|
37
|
+
|
32
38
|
has_many :published_articles, ->{ where(published: true) },
|
33
39
|
class_name: "Article"
|
34
40
|
has_many :comments
|
@@ -81,7 +87,6 @@ class Person < ActiveRecord::Base
|
|
81
87
|
)
|
82
88
|
end
|
83
89
|
|
84
|
-
|
85
90
|
ransacker :sql_literal_id do
|
86
91
|
Arel.sql('people.id')
|
87
92
|
end
|
@@ -104,7 +109,6 @@ class Person < ActiveRecord::Base
|
|
104
109
|
Arel.sql(query)
|
105
110
|
end
|
106
111
|
|
107
|
-
|
108
112
|
def self.ransackable_attributes(auth_object = nil)
|
109
113
|
if auth_object == :admin
|
110
114
|
super - ['only_sort']
|
@@ -134,6 +138,32 @@ class Article < ActiveRecord::Base
|
|
134
138
|
alias_attribute :content, :body
|
135
139
|
|
136
140
|
default_scope { where("'default_scope' = 'default_scope'") }
|
141
|
+
|
142
|
+
ransacker :title_type, formatter: lambda { |tuples|
|
143
|
+
title, type = JSON.parse(tuples)
|
144
|
+
Arel::Nodes::Grouping.new(
|
145
|
+
[
|
146
|
+
Arel::Nodes.build_quoted(title),
|
147
|
+
Arel::Nodes.build_quoted(type)
|
148
|
+
]
|
149
|
+
)
|
150
|
+
} do |_parent|
|
151
|
+
articles = Article.arel_table
|
152
|
+
Arel::Nodes::Grouping.new(
|
153
|
+
%i[title type].map do |field|
|
154
|
+
Arel::Nodes::NamedFunction.new(
|
155
|
+
'COALESCE',
|
156
|
+
[
|
157
|
+
Arel::Nodes::NamedFunction.new('TRIM', [articles[field]]),
|
158
|
+
Arel::Nodes.build_quoted('')
|
159
|
+
]
|
160
|
+
)
|
161
|
+
end
|
162
|
+
)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class StoryArticle < Article
|
137
167
|
end
|
138
168
|
|
139
169
|
class Recommendation < ActiveRecord::Base
|
@@ -157,6 +187,8 @@ end
|
|
157
187
|
class Comment < ActiveRecord::Base
|
158
188
|
belongs_to :article
|
159
189
|
belongs_to :person
|
190
|
+
|
191
|
+
default_scope { where(disabled: false) }
|
160
192
|
end
|
161
193
|
|
162
194
|
class Tag < ActiveRecord::Base
|
@@ -194,6 +226,7 @@ module Schema
|
|
194
226
|
t.string :title
|
195
227
|
t.text :subject_header
|
196
228
|
t.text :body
|
229
|
+
t.string :type
|
197
230
|
t.boolean :published, default: true
|
198
231
|
end
|
199
232
|
|
@@ -201,6 +234,7 @@ module Schema
|
|
201
234
|
t.integer :article_id
|
202
235
|
t.integer :person_id
|
203
236
|
t.text :body
|
237
|
+
t.boolean :disabled, default: false
|
204
238
|
end
|
205
239
|
|
206
240
|
create_table :tags, force: true do |t|
|