ransack 2.3.2 → 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/.rubocop.yml +44 -0
- data/CHANGELOG.md +28 -1
- data/CONTRIBUTING.md +16 -11
- data/Gemfile +5 -3
- data/README.md +167 -30
- 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/{polyamorous/lib → lib}/polyamorous/activerecord_5.2_ruby_2/join_association.rb +4 -0
- data/{polyamorous/lib → lib}/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -0
- data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +11 -0
- data/{polyamorous/lib → lib}/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -1
- data/{polyamorous/lib → lib}/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +0 -1
- data/{polyamorous/lib → lib}/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -1
- 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/{polyamorous/lib → lib}/polyamorous/activerecord_6.1_ruby_2/reflection.rb +0 -1
- 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/{polyamorous/lib → lib}/polyamorous/join.rb +0 -0
- data/{polyamorous/lib → lib/polyamorous}/polyamorous.rb +1 -1
- data/{polyamorous/lib → lib}/polyamorous/swapping_reflection_class.rb +0 -0
- data/{polyamorous/lib → lib}/polyamorous/tree_node.rb +0 -0
- data/lib/polyamorous.rb +1 -0
- data/lib/ransack/adapters/active_record/base.rb +5 -1
- data/lib/ransack/adapters/active_record/context.rb +55 -13
- data/lib/ransack/adapters/active_record/ransack/constants.rb +1 -1
- data/lib/ransack/adapters/active_record/ransack/context.rb +1 -0
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +11 -3
- data/lib/ransack/configuration.rb +31 -1
- data/lib/ransack/constants.rb +2 -2
- data/lib/ransack/helpers/form_builder.rb +3 -3
- data/lib/ransack/helpers.rb +1 -1
- data/lib/ransack/locale/sv.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +1 -1
- data/lib/ransack/nodes/condition.rb +0 -2
- 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/search.rb +4 -1
- data/lib/ransack/translate.rb +4 -4
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack.rb +2 -2
- data/ransack.gemspec +8 -14
- 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/ransack_helper.rb +1 -1
- data/spec/{ransack → polyamorous}/join_association_spec.rb +8 -1
- data/spec/{ransack → polyamorous}/join_dependency_spec.rb +0 -0
- 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 +19 -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 +1 -1
- data/spec/ransack/search_spec.rb +215 -30
- data/spec/spec_helper.rb +7 -5
- data/spec/support/schema.rb +28 -2
- metadata +45 -47
- data/.travis.yml +0 -47
- data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -12
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -2
- data/polyamorous/lib/polyamorous/version.rb +0 -3
- data/polyamorous/polyamorous.gemspec +0 -27
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
|
@@ -234,12 +338,14 @@ module Ransack
|
|
234
338
|
parent_name_eq: "parent_name_query",
|
235
339
|
parent_articles_title_eq: 'parents_article_title_query'
|
236
340
|
}).result
|
341
|
+
|
237
342
|
real_query = remove_quotes_and_backticks(s.to_sql)
|
238
343
|
|
239
344
|
expect(real_query)
|
240
|
-
|
345
|
+
.to match(%r{LEFT OUTER JOIN articles ON (\('default_scope' = 'default_scope'\) AND )?articles.person_id = people.id})
|
241
346
|
expect(real_query)
|
242
|
-
|
347
|
+
.to match(%r{LEFT OUTER JOIN articles articles_people ON (\('default_scope' = 'default_scope'\) AND )?articles_people.person_id = parents_people.id})
|
348
|
+
|
243
349
|
expect(real_query)
|
244
350
|
.to include "people.name = 'person_name_query'"
|
245
351
|
expect(real_query)
|
@@ -282,6 +388,7 @@ module Ransack
|
|
282
388
|
s = Search.new(Note, notable_of_Person_type_name_eq: 'Ernie').result
|
283
389
|
expect(s).to be_an ActiveRecord::Relation
|
284
390
|
expect(s.to_sql).to match /#{people_name_field} = 'Ernie'/
|
391
|
+
expect(s.to_sql).to match /#{notable_type_field} = 'Person'/
|
285
392
|
end
|
286
393
|
|
287
394
|
it 'evaluates nested conditions' do
|
@@ -320,11 +427,8 @@ module Ransack
|
|
320
427
|
{ m: 'or', comments_body_cont: 'e', articles_comments_body_cont: 'e' }
|
321
428
|
]
|
322
429
|
)
|
323
|
-
|
324
|
-
|
325
|
-
else
|
326
|
-
all_or_load, uniq_or_distinct = :load, :distinct
|
327
|
-
end
|
430
|
+
|
431
|
+
all_or_load, uniq_or_distinct = :load, :distinct
|
328
432
|
expect(s.result.send(all_or_load).size)
|
329
433
|
.to eq(9000)
|
330
434
|
expect(s.result(distinct: true).size)
|
@@ -377,88 +481,169 @@ module Ransack
|
|
377
481
|
expect(sort.dir).to eq 'asc'
|
378
482
|
end
|
379
483
|
|
380
|
-
it 'creates sorts based on
|
381
|
-
@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' }]
|
382
513
|
expect(@s.sorts.size).to eq(2)
|
383
514
|
sort1, sort2 = @s.sorts
|
384
515
|
expect(sort1).to be_a Nodes::Sort
|
385
516
|
expect(sort1.name).to eq 'id'
|
386
517
|
expect(sort1.dir).to eq 'desc'
|
387
518
|
expect(sort2).to be_a Nodes::Sort
|
388
|
-
expect(sort2.name).to eq '
|
519
|
+
expect(sort2.name).to eq 'parent_name'
|
389
520
|
expect(sort2.dir).to eq 'asc'
|
390
521
|
end
|
391
522
|
|
392
|
-
it 'creates sorts based on
|
393
|
-
@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' }]
|
394
525
|
expect(@s.sorts.size).to eq(2)
|
395
526
|
sort1, sort2 = @s.sorts
|
396
527
|
expect(sort1).to be_a Nodes::Sort
|
397
528
|
expect(sort1.name).to eq 'id'
|
398
529
|
expect(sort1.dir).to eq 'desc'
|
399
530
|
expect(sort2).to be_a Nodes::Sort
|
400
|
-
expect(sort2.name).to eq '
|
531
|
+
expect(sort2.name).to eq 'parent_name'
|
401
532
|
expect(sort2.dir).to eq 'asc'
|
402
533
|
end
|
403
534
|
|
404
|
-
it 'creates sorts based on
|
535
|
+
it 'creates sorts based on attributes, alias and different directions
|
405
536
|
in array format' do
|
406
|
-
@s.sorts = ['id DESC', { name: '
|
537
|
+
@s.sorts = ['id DESC', { name: 'daddy', dir: nil }]
|
407
538
|
expect(@s.sorts.size).to eq(2)
|
408
539
|
sort1, sort2 = @s.sorts
|
409
540
|
expect(sort1).to be_a Nodes::Sort
|
410
541
|
expect(sort1.name).to eq 'id'
|
411
542
|
expect(sort1.dir).to eq 'desc'
|
412
543
|
expect(sort2).to be_a Nodes::Sort
|
413
|
-
expect(sort2.name).to eq '
|
544
|
+
expect(sort2.name).to eq 'parent_name'
|
414
545
|
expect(sort2.dir).to eq 'asc'
|
415
546
|
end
|
416
547
|
|
417
|
-
it 'creates sorts based on
|
548
|
+
it 'creates sorts based on attributes, alias and directions in hash format' do
|
418
549
|
@s.sorts = {
|
419
550
|
'0' => { name: 'id', dir: 'desc' },
|
420
|
-
'1' => { name: '
|
551
|
+
'1' => { name: 'daddy', dir: 'asc' }
|
421
552
|
}
|
422
553
|
expect(@s.sorts.size).to eq(2)
|
423
554
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
424
555
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
425
|
-
|
556
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
426
557
|
expect(id_sort.dir).to eq 'desc'
|
427
|
-
expect(
|
558
|
+
expect(daddy_sort.dir).to eq 'asc'
|
428
559
|
end
|
429
560
|
|
430
|
-
it 'creates sorts based on
|
561
|
+
it 'creates sorts based on attributes, alias and uppercase directions
|
431
562
|
in hash format' do
|
432
563
|
@s.sorts = {
|
433
564
|
'0' => { name: 'id', dir: 'DESC' },
|
434
|
-
'1' => { name: '
|
565
|
+
'1' => { name: 'daddy', dir: 'ASC' }
|
435
566
|
}
|
436
567
|
expect(@s.sorts.size).to eq(2)
|
437
568
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
438
569
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
439
|
-
|
570
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
440
571
|
expect(id_sort.dir).to eq 'desc'
|
441
|
-
expect(
|
572
|
+
expect(daddy_sort.dir).to eq 'asc'
|
442
573
|
end
|
443
574
|
|
444
|
-
it 'creates sorts based on
|
575
|
+
it 'creates sorts based on attributes, alias and different directions
|
445
576
|
in hash format' do
|
446
577
|
@s.sorts = {
|
447
578
|
'0' => { name: 'id', dir: 'DESC' },
|
448
|
-
'1' => { name: '
|
579
|
+
'1' => { name: 'daddy', dir: nil }
|
449
580
|
}
|
450
581
|
expect(@s.sorts.size).to eq(2)
|
451
582
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
452
583
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
453
|
-
|
584
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
454
585
|
expect(id_sort.dir).to eq 'desc'
|
455
|
-
expect(
|
586
|
+
expect(daddy_sort.dir).to eq 'asc'
|
456
587
|
end
|
457
588
|
|
458
589
|
it 'overrides existing sort' do
|
459
590
|
@s.sorts = 'id asc'
|
460
591
|
expect(@s.result.first.id).to eq 1
|
461
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
|
462
647
|
end
|
463
648
|
|
464
649
|
describe '#method_missing' do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'machinist/active_record'
|
2
|
+
require 'polyamorous/polyamorous'
|
2
3
|
require 'sham'
|
3
4
|
require 'faker'
|
4
5
|
require 'ransack'
|
@@ -16,16 +17,17 @@ I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'support', '*.yml')]
|
|
16
17
|
Dir[File.expand_path('../{helpers,support,blueprints}/*.rb', __FILE__)]
|
17
18
|
.each { |f| require f }
|
18
19
|
|
20
|
+
Faker::Config.random = Random.new(0)
|
19
21
|
Sham.define do
|
20
22
|
name { Faker::Name.name }
|
21
23
|
title { Faker::Lorem.sentence }
|
22
24
|
body { Faker::Lorem.paragraph }
|
23
25
|
salary { |index| 30000 + (index * 1000) }
|
24
|
-
tag_name { Faker::Lorem.words(3).join(' ') }
|
25
|
-
note { Faker::Lorem.words(7).join(' ') }
|
26
|
-
only_admin { Faker::Lorem.words(3).join(' ') }
|
27
|
-
only_search { Faker::Lorem.words(3).join(' ') }
|
28
|
-
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(' ') }
|
29
31
|
notable_id { |id| id }
|
30
32
|
end
|
31
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'
|
@@ -85,7 +87,6 @@ class Person < ActiveRecord::Base
|
|
85
87
|
)
|
86
88
|
end
|
87
89
|
|
88
|
-
|
89
90
|
ransacker :sql_literal_id do
|
90
91
|
Arel.sql('people.id')
|
91
92
|
end
|
@@ -108,7 +109,6 @@ class Person < ActiveRecord::Base
|
|
108
109
|
Arel.sql(query)
|
109
110
|
end
|
110
111
|
|
111
|
-
|
112
112
|
def self.ransackable_attributes(auth_object = nil)
|
113
113
|
if auth_object == :admin
|
114
114
|
super - ['only_sort']
|
@@ -138,6 +138,29 @@ class Article < ActiveRecord::Base
|
|
138
138
|
alias_attribute :content, :body
|
139
139
|
|
140
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
|
141
164
|
end
|
142
165
|
|
143
166
|
class StoryArticle < Article
|
@@ -164,6 +187,8 @@ end
|
|
164
187
|
class Comment < ActiveRecord::Base
|
165
188
|
belongs_to :article
|
166
189
|
belongs_to :person
|
190
|
+
|
191
|
+
default_scope { where(disabled: false) }
|
167
192
|
end
|
168
193
|
|
169
194
|
class Tag < ActiveRecord::Base
|
@@ -209,6 +234,7 @@ module Schema
|
|
209
234
|
t.integer :article_id
|
210
235
|
t.integer :person_id
|
211
236
|
t.text :body
|
237
|
+
t.boolean :disabled, default: false
|
212
238
|
end
|
213
239
|
|
214
240
|
create_table :tags, force: true do |t|
|