ransack 2.3.0 → 4.1.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.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/SECURITY.md +12 -0
  4. data/.github/workflows/codeql.yml +72 -0
  5. data/.github/workflows/cronjob.yml +99 -0
  6. data/.github/workflows/deploy.yml +35 -0
  7. data/.github/workflows/rubocop.yml +20 -0
  8. data/.github/workflows/test-deploy.yml +29 -0
  9. data/.github/workflows/test.yml +131 -0
  10. data/.nojekyll +0 -0
  11. data/.rubocop.yml +50 -0
  12. data/CHANGELOG.md +263 -1
  13. data/CONTRIBUTING.md +51 -29
  14. data/Gemfile +24 -10
  15. data/README.md +49 -917
  16. data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
  17. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +75 -0
  18. data/docs/.gitignore +19 -0
  19. data/docs/.nojekyll +0 -0
  20. data/docs/babel.config.js +3 -0
  21. data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
  22. data/docs/docs/getting-started/_category_.json +4 -0
  23. data/docs/docs/getting-started/advanced-mode.md +46 -0
  24. data/docs/docs/getting-started/configuration.md +47 -0
  25. data/docs/docs/getting-started/search-matches.md +67 -0
  26. data/docs/docs/getting-started/simple-mode.md +288 -0
  27. data/docs/docs/getting-started/sorting.md +71 -0
  28. data/docs/docs/getting-started/using-predicates.md +282 -0
  29. data/docs/docs/going-further/_category_.json +4 -0
  30. data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
  31. data/docs/docs/going-further/associations.md +70 -0
  32. data/docs/docs/going-further/custom-predicates.md +52 -0
  33. data/docs/docs/going-further/documentation.md +43 -0
  34. data/docs/docs/going-further/exporting-to-csv.md +49 -0
  35. data/docs/docs/going-further/external-guides.md +57 -0
  36. data/docs/docs/going-further/form-customisation.md +63 -0
  37. data/docs/docs/going-further/i18n.md +53 -0
  38. data/docs/docs/going-further/img/create_release.png +0 -0
  39. data/docs/docs/going-further/merging-searches.md +41 -0
  40. data/docs/docs/going-further/other-notes.md +428 -0
  41. data/docs/docs/going-further/polymorphic-search.md +46 -0
  42. data/docs/docs/going-further/ransackers.md +331 -0
  43. data/docs/docs/going-further/release_process.md +36 -0
  44. data/docs/docs/going-further/saving-queries.md +82 -0
  45. data/docs/docs/going-further/searching-postgres.md +57 -0
  46. data/docs/docs/going-further/wiki-contributors.md +82 -0
  47. data/docs/docs/intro.md +99 -0
  48. data/docs/docusaurus.config.js +120 -0
  49. data/docs/package.json +42 -0
  50. data/docs/sidebars.js +31 -0
  51. data/docs/src/components/HomepageFeatures/index.js +64 -0
  52. data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  53. data/docs/src/css/custom.css +39 -0
  54. data/docs/src/pages/index.module.css +23 -0
  55. data/docs/src/pages/markdown-page.md +7 -0
  56. data/docs/static/.nojekyll +0 -0
  57. data/docs/static/img/docusaurus.png +0 -0
  58. data/docs/static/img/favicon.ico +0 -0
  59. data/docs/static/img/logo.svg +1 -0
  60. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  61. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  62. data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  63. data/docs/static/img/undraw_docusaurus_react.svg +170 -0
  64. data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  65. data/docs/yarn.lock +8879 -0
  66. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +70 -0
  67. data/{polyamorous/lib/polyamorous/activerecord_6.0_ruby_2 → lib/polyamorous/activerecord_6.1_ruby_2}/join_dependency.rb +33 -12
  68. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -0
  69. data/lib/polyamorous/activerecord_7.0_ruby_2/join_association.rb +1 -0
  70. data/lib/polyamorous/activerecord_7.0_ruby_2/join_dependency.rb +1 -0
  71. data/lib/polyamorous/activerecord_7.0_ruby_2/reflection.rb +1 -0
  72. data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
  73. data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
  74. data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
  75. data/{polyamorous/lib → lib/polyamorous}/polyamorous.rb +3 -8
  76. data/lib/ransack/adapters/active_record/base.rb +83 -10
  77. data/lib/ransack/adapters/active_record/context.rb +61 -116
  78. data/lib/ransack/configuration.rb +53 -10
  79. data/lib/ransack/constants.rb +126 -7
  80. data/lib/ransack/context.rb +34 -5
  81. data/lib/ransack/helpers/form_builder.rb +11 -17
  82. data/lib/ransack/helpers/form_helper.rb +14 -5
  83. data/lib/ransack/helpers.rb +1 -1
  84. data/lib/ransack/locale/sk.yml +70 -0
  85. data/lib/ransack/locale/sv.yml +70 -0
  86. data/lib/ransack/nodes/attribute.rb +3 -3
  87. data/lib/ransack/nodes/condition.rb +87 -8
  88. data/lib/ransack/nodes/grouping.rb +4 -4
  89. data/lib/ransack/nodes/node.rb +1 -1
  90. data/lib/ransack/nodes/sort.rb +3 -3
  91. data/lib/ransack/nodes/value.rb +3 -3
  92. data/lib/ransack/predicate.rb +3 -2
  93. data/lib/ransack/ransacker.rb +1 -1
  94. data/lib/ransack/search.rb +15 -7
  95. data/lib/ransack/translate.rb +6 -6
  96. data/lib/ransack/version.rb +1 -1
  97. data/lib/ransack/visitor.rb +38 -2
  98. data/lib/ransack.rb +6 -10
  99. data/ransack.gemspec +9 -24
  100. data/spec/blueprints/articles.rb +1 -1
  101. data/spec/blueprints/comments.rb +1 -1
  102. data/spec/blueprints/notes.rb +1 -1
  103. data/spec/blueprints/tags.rb +1 -1
  104. data/spec/console.rb +5 -5
  105. data/spec/helpers/polyamorous_helper.rb +2 -17
  106. data/spec/helpers/ransack_helper.rb +1 -1
  107. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  108. data/spec/{ransack → polyamorous}/join_association_spec.rb +3 -1
  109. data/spec/{ransack → polyamorous}/join_dependency_spec.rb +0 -16
  110. data/spec/ransack/adapters/active_record/base_spec.rb +125 -16
  111. data/spec/ransack/adapters/active_record/context_spec.rb +19 -18
  112. data/spec/ransack/configuration_spec.rb +33 -9
  113. data/spec/ransack/helpers/form_builder_spec.rb +8 -8
  114. data/spec/ransack/helpers/form_helper_spec.rb +109 -20
  115. data/spec/ransack/nodes/condition_spec.rb +37 -0
  116. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  117. data/spec/ransack/nodes/value_spec.rb +115 -0
  118. data/spec/ransack/predicate_spec.rb +75 -2
  119. data/spec/ransack/search_spec.rb +239 -38
  120. data/spec/ransack/translate_spec.rb +1 -1
  121. data/spec/spec_helper.rb +9 -5
  122. data/spec/support/schema.rb +111 -12
  123. metadata +105 -195
  124. data/.travis.yml +0 -49
  125. data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -116
  126. data/lib/ransack/adapters/active_record/ransack/context.rb +0 -60
  127. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +0 -61
  128. data/lib/ransack/adapters/active_record/ransack/translate.rb +0 -8
  129. data/lib/ransack/adapters/active_record/ransack/visitor.rb +0 -47
  130. data/lib/ransack/adapters.rb +0 -64
  131. data/lib/ransack/nodes.rb +0 -8
  132. data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +0 -2
  133. data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +0 -2
  134. data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +0 -31
  135. data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +0 -112
  136. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +0 -31
  137. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +0 -112
  138. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/reflection.rb +0 -12
  139. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_association.rb +0 -22
  140. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_dependency.rb +0 -81
  141. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/reflection.rb +0 -2
  142. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -2
  143. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -2
  144. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -2
  145. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -2
  146. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +0 -2
  147. data/polyamorous/lib/polyamorous/version.rb +0 -3
  148. data/polyamorous/polyamorous.gemspec +0 -35
  149. /data/{logo → docs/static/logo}/ransack-h.png +0 -0
  150. /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
  151. /data/{logo → docs/static/logo}/ransack-v.png +0 -0
  152. /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
  153. /data/{logo → docs/static/logo}/ransack.png +0 -0
  154. /data/{logo → docs/static/logo}/ransack.svg +0 -0
  155. /data/{polyamorous/lib → lib}/polyamorous/join.rb +0 -0
  156. /data/{polyamorous/lib → lib}/polyamorous/swapping_reflection_class.rb +0 -0
  157. /data/{polyamorous/lib → lib}/polyamorous/tree_node.rb +0 -0
  158. /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
  159. /data/spec/{ransack → polyamorous}/join_spec.rb +0 -0
@@ -8,7 +8,6 @@ module Ransack
8
8
  subject { ::ActiveRecord::Base }
9
9
 
10
10
  it { should respond_to :ransack }
11
- it { should respond_to :search }
12
11
 
13
12
  describe '#search' do
14
13
  subject { Person.ransack }
@@ -44,12 +43,12 @@ module Ransack
44
43
 
45
44
  it 'applies stringy boolean scopes with true value in an array' do
46
45
  s = Person.ransack('of_age' => ['true'])
47
- expect(s.result.to_sql).to (include 'age >= 18')
46
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{(age >= '18')} : 'age >= 18')
48
47
  end
49
48
 
50
49
  it 'applies stringy boolean scopes with false value in an array' do
51
50
  s = Person.ransack('of_age' => ['false'])
52
- expect(s.result.to_sql).to (include 'age < 18')
51
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age < '18'} : 'age < 18')
53
52
  end
54
53
 
55
54
  it 'ignores unlisted scopes' do
@@ -69,15 +68,25 @@ module Ransack
69
68
 
70
69
  it 'passes values to scopes' do
71
70
  s = Person.ransack('over_age' => 18)
72
- expect(s.result.to_sql).to (include 'age > 18')
71
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
73
72
  end
74
73
 
75
74
  it 'chains scopes' do
76
75
  s = Person.ransack('over_age' => 18, 'active' => true)
77
- expect(s.result.to_sql).to (include 'age > 18')
76
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
78
77
  expect(s.result.to_sql).to (include 'active = 1')
79
78
  end
80
79
 
80
+ it 'applies scopes that define string SQL joins' do
81
+ allow(Article)
82
+ .to receive(:ransackable_scopes)
83
+ .and_return([:latest_comment_cont])
84
+
85
+ # Including a negative condition to test removing the scope
86
+ s = Search.new(Article, notes_note_not_eq: 'Test', latest_comment_cont: 'Test')
87
+ expect(s.result.to_sql).to include 'latest_comment'
88
+ end
89
+
81
90
  context "with sanitize_custom_scope_booleans set to false" do
82
91
  before(:all) do
83
92
  Ransack.configure { |c| c.sanitize_custom_scope_booleans = false }
@@ -89,12 +98,12 @@ module Ransack
89
98
 
90
99
  it 'passes true values to scopes' do
91
100
  s = Person.ransack('over_age' => 1)
92
- expect(s.result.to_sql).to (include 'age > 1')
101
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
93
102
  end
94
103
 
95
104
  it 'passes false values to scopes' do
96
105
  s = Person.ransack('over_age' => 0)
97
- expect(s.result.to_sql).to (include 'age > 0')
106
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
98
107
  end
99
108
  end
100
109
 
@@ -107,12 +116,12 @@ module Ransack
107
116
 
108
117
  it 'passes true values to scopes' do
109
118
  s = Person.ransack('over_age' => 1)
110
- expect(s.result.to_sql).to (include 'age > 1')
119
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
111
120
  end
112
121
 
113
122
  it 'passes false values to scopes' do
114
123
  s = Person.ransack('over_age' => 0)
115
- expect(s.result.to_sql).to (include 'age > 0')
124
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
116
125
  end
117
126
  end
118
127
 
@@ -122,12 +131,32 @@ module Ransack
122
131
  expect { Person.ransack('') }.to_not raise_error
123
132
  end
124
133
 
134
+ it 'raises exception if ransack! called with unknown condition' do
135
+ expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error(ArgumentError)
136
+ end
137
+
125
138
  it 'does not modify the parameters' do
126
139
  params = { name_eq: '' }
127
140
  expect { Person.ransack(params) }.not_to change { params }
128
141
  end
129
142
  end
130
143
 
144
+ context 'has_one through associations' do
145
+ let(:address) { Address.create!(city: 'Boston') }
146
+ let(:org) { Organization.create!(name: 'Testorg', address: address) }
147
+ let!(:employee) { Employee.create!(name: 'Ernie', organization: org) }
148
+
149
+ it 'works when has_one through association is first' do
150
+ s = Employee.ransack(address_city_eq: 'Boston', organization_name_eq: 'Testorg')
151
+ expect(s.result.to_a).to include(employee)
152
+ end
153
+
154
+ it 'works when has_one through association is last' do
155
+ s = Employee.ransack(organization_name_eq: 'Testorg', address_city_eq: 'Boston')
156
+ expect(s.result.to_a).to include(employee)
157
+ end
158
+ end
159
+
131
160
  context 'negative conditions on HABTM associations' do
132
161
  let(:medieval) { Tag.create!(name: 'Medieval') }
133
162
  let(:fantasy) { Tag.create!(name: 'Fantasy') }
@@ -143,14 +172,12 @@ module Ransack
143
172
  it 'removes redundant joins from top query' do
144
173
  s = Article.ransack(tags_name_not_eq: "Fantasy")
145
174
  sql = s.result.to_sql
146
-
147
175
  expect(sql).to_not include('LEFT OUTER JOIN')
148
176
  end
149
177
 
150
178
  it 'handles != for single values' do
151
179
  s = Article.ransack(tags_name_not_eq: "Fantasy")
152
180
  articles = s.result.to_a
153
-
154
181
  expect(articles).to include marco
155
182
  expect(articles).to_not include arthur
156
183
  end
@@ -267,10 +294,12 @@ module Ransack
267
294
  # end
268
295
 
269
296
  it 'creates ransack attributes' do
297
+ person = Person.create!(name: 'Aric Smith')
298
+
270
299
  s = Person.ransack(reversed_name_eq: 'htimS cirA')
271
300
  expect(s.result.size).to eq(1)
272
301
 
273
- expect(s.result.first).to eq Person.where(name: 'Aric Smith').first
302
+ expect(s.result.first).to eq person
274
303
  end
275
304
 
276
305
  it 'can be accessed through associations' do
@@ -310,7 +339,11 @@ module Ransack
310
339
  end
311
340
 
312
341
  it 'should function correctly with a multi-parameter attribute' do
313
- ::ActiveRecord::Base.default_timezone = :utc
342
+ if ::ActiveRecord::VERSION::MAJOR >= 7
343
+ ::ActiveRecord.default_timezone = :utc
344
+ else
345
+ ::ActiveRecord::Base.default_timezone = :utc
346
+ end
314
347
  Time.zone = 'UTC'
315
348
 
316
349
  date = Date.current
@@ -460,9 +493,9 @@ module Ransack
460
493
  Comment.create(article: Article.create(title: 'Avenger'), person: Person.create(salary: 100_000)),
461
494
  Comment.create(article: Article.create(title: 'Avenge'), person: Person.create(salary: 50_000)),
462
495
  ]
463
- expect(Comment.ransack(article_title_cont: 'aven',s: 'person_salary desc').result).to eq(comments)
496
+ expect(Comment.ransack(article_title_cont: 'aven', s: 'person_salary desc').result).to eq(comments)
464
497
  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)
498
+ expect(Comment.joins(:person).ransack(article_title_cont: 'aven', s: 'persons_salary desc').result).to eq(comments)
466
499
  end
467
500
 
468
501
  it 'allows sort by `only_sort` field' do
@@ -541,7 +574,6 @@ module Ransack
541
574
  )
542
575
  end
543
576
 
544
-
545
577
  it 'should allow passing ransacker arguments to a ransacker' do
546
578
  s = Person.ransack(
547
579
  c: [{
@@ -641,6 +673,37 @@ module Ransack
641
673
  it { should_not include 'only_sort' }
642
674
  it { should include 'only_admin' }
643
675
  end
676
+
677
+ context 'when not defined in model, nor in ApplicationRecord' do
678
+ subject { Article.ransackable_attributes }
679
+
680
+ it "raises a helpful error" do
681
+ without_application_record_method(:ransackable_attributes) do
682
+ expect { subject }.to raise_error(RuntimeError, /Ransack needs Article attributes explicitly allowlisted/)
683
+ end
684
+ end
685
+ end
686
+
687
+ context 'when defined only in model by delegating to super' do
688
+ subject { Article.ransackable_attributes }
689
+
690
+ around do |example|
691
+ Article.singleton_class.define_method(:ransackable_attributes) do
692
+ super(nil) - super(nil)
693
+ end
694
+
695
+ example.run
696
+ ensure
697
+ Article.singleton_class.remove_method(:ransackable_attributes)
698
+ end
699
+
700
+ it "returns the allowlist in the model, and warns" do
701
+ without_application_record_method(:ransackable_attributes) do
702
+ expect { subject }.to output(/Ransack's builtin `ransackable_attributes` method is deprecated/).to_stderr
703
+ expect(subject).to be_empty
704
+ end
705
+ end
706
+ end
644
707
  end
645
708
 
646
709
  describe '#ransortable_attributes' do
@@ -673,6 +736,37 @@ module Ransack
673
736
  it { should include 'parent' }
674
737
  it { should include 'children' }
675
738
  it { should include 'articles' }
739
+
740
+ context 'when not defined in model, nor in ApplicationRecord' do
741
+ subject { Article.ransackable_associations }
742
+
743
+ it "raises a helpful error" do
744
+ without_application_record_method(:ransackable_associations) do
745
+ expect { subject }.to raise_error(RuntimeError, /Ransack needs Article associations explicitly allowlisted/)
746
+ end
747
+ end
748
+ end
749
+
750
+ context 'when defined only in model by delegating to super' do
751
+ subject { Article.ransackable_associations }
752
+
753
+ around do |example|
754
+ Article.singleton_class.define_method(:ransackable_associations) do
755
+ super(nil) - super(nil)
756
+ end
757
+
758
+ example.run
759
+ ensure
760
+ Article.singleton_class.remove_method(:ransackable_associations)
761
+ end
762
+
763
+ it "returns the allowlist in the model, and warns" do
764
+ without_application_record_method(:ransackable_associations) do
765
+ expect { subject }.to output(/Ransack's builtin `ransackable_associations` method is deprecated/).to_stderr
766
+ expect(subject).to be_empty
767
+ end
768
+ end
769
+ end
676
770
  end
677
771
 
678
772
  describe '#ransackable_scopes' do
@@ -687,6 +781,21 @@ module Ransack
687
781
  it { should eq [] }
688
782
  end
689
783
 
784
+ private
785
+
786
+ def without_application_record_method(method)
787
+ ApplicationRecord.singleton_class.alias_method :"original_#{method}", :"#{method}"
788
+ ApplicationRecord.singleton_class.remove_method :"#{method}"
789
+
790
+ yield
791
+ ensure
792
+ ApplicationRecord.singleton_class.alias_method :"#{method}", :"original_#{method}"
793
+ ApplicationRecord.singleton_class.remove_method :"original_#{method}"
794
+ end
795
+
796
+ def rails7_and_mysql
797
+ ::ActiveRecord::VERSION::MAJOR >= 7 && ENV['DB'] == 'mysql'
798
+ end
690
799
  end
691
800
  end
692
801
  end
@@ -9,7 +9,6 @@ module Ransack
9
9
  describe Context do
10
10
  subject { Context.new(Person) }
11
11
 
12
-
13
12
  it 'has an Active Record alias tracker method' do
14
13
  expect(subject.alias_tracker)
15
14
  .to be_an ::ActiveRecord::Associations::AliasTracker
@@ -79,6 +78,25 @@ module Ransack
79
78
  expect(constraint.right.relation.name).to eql 'people'
80
79
  expect(constraint.right.name).to eql 'id'
81
80
  end
81
+
82
+ it 'build correlated subquery for multiple conditions (default scope)' do
83
+ search = Search.new(Person, { comments_body_not_eq: 'some_title' })
84
+
85
+ # Was
86
+ # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
87
+ # SELECT "comments"."disabled" FROM "comments"
88
+ # WHERE "comments"."disabled" = "people"."id"
89
+ # AND NOT ("comments"."body" != 'some_title')
90
+ # ) ORDER BY "people"."id" DESC
91
+ # Should Be
92
+ # SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
93
+ # SELECT "comments"."person_id" FROM "comments"
94
+ # WHERE "comments"."person_id" = "people"."id"
95
+ # AND NOT ("comments"."body" != 'some_title')
96
+ # ) ORDER BY "people"."id" DESC
97
+
98
+ expect(search.result.to_sql).to match /.comments.\..person_id. = .people.\..id./
99
+ end
82
100
  end
83
101
 
84
102
  describe 'sharing context across searches' do
@@ -91,23 +109,6 @@ module Ransack
91
109
  context: shared_context)
92
110
  end
93
111
 
94
- describe '#join_associations', if: AR_version <= '4.0' do
95
- it 'returns dependent join associations for all searches run
96
- against the context' do
97
- parents, children = shared_context.join_associations
98
-
99
- expect(children.aliased_table_name).to eq "children_people"
100
- expect(parents.aliased_table_name).to eq "parents_people"
101
- end
102
-
103
- it 'can be rejoined to execute a valid query' do
104
- parents, children = shared_context.join_associations
105
-
106
- expect { Person.joins(parents).joins(children).to_a }
107
- .to_not raise_error
108
- end
109
- end
110
-
111
112
  describe '#join_sources' do
112
113
  it 'returns dependent arel join nodes for all searches run against
113
114
  the context' do
@@ -20,7 +20,7 @@ module Ransack
20
20
  Ransack.configure do |config|
21
21
  config.add_predicate(
22
22
  :test_predicate_without_compound,
23
- :compounds => false
23
+ compounds: false
24
24
  )
25
25
  end
26
26
  expect(Ransack.predicates)
@@ -45,6 +45,20 @@ module Ransack
45
45
  Ransack.options = default
46
46
  end
47
47
 
48
+ it 'should have default value for strip_whitespace' do
49
+ expect(Ransack.options[:strip_whitespace]).to eq true
50
+ end
51
+
52
+ it 'changes default search key parameter' do
53
+ default = Ransack.options.clone
54
+
55
+ Ransack.configure { |c| c.strip_whitespace = false }
56
+
57
+ expect(Ransack.options[:strip_whitespace]).to eq false
58
+
59
+ Ransack.options = default
60
+ end
61
+
48
62
  it 'should have default values for arrows' do
49
63
  expect(Ransack.options[:up_arrow]).to eq '&#9660;'
50
64
  expect(Ransack.options[:down_arrow]).to eq '&#9650;'
@@ -124,8 +138,8 @@ module Ransack
124
138
  Ransack.configure do |config|
125
139
  config.add_predicate(
126
140
  :test_array_predicate,
127
- :wants_array => true,
128
- :compounds => true
141
+ wants_array: true,
142
+ compounds: true
129
143
  )
130
144
  end
131
145
 
@@ -139,11 +153,11 @@ module Ransack
139
153
  Ransack.configure do |config|
140
154
  config.add_predicate(
141
155
  :test_in_predicate,
142
- :arel_predicate => 'in'
156
+ arel_predicate: 'in'
143
157
  )
144
158
  config.add_predicate(
145
159
  :test_not_in_predicate,
146
- :arel_predicate => 'not_in'
160
+ arel_predicate: 'not_in'
147
161
  )
148
162
  end
149
163
 
@@ -157,13 +171,13 @@ module Ransack
157
171
  Ransack.configure do |config|
158
172
  config.add_predicate(
159
173
  :test_in_predicate_no_array,
160
- :arel_predicate => 'in',
161
- :wants_array => false
174
+ arel_predicate: 'in',
175
+ wants_array: false
162
176
  )
163
177
  config.add_predicate(
164
178
  :test_not_in_predicate_no_array,
165
- :arel_predicate => 'not_in',
166
- :wants_array => false
179
+ arel_predicate: 'not_in',
180
+ wants_array: false
167
181
  )
168
182
  end
169
183
 
@@ -173,5 +187,15 @@ module Ransack
173
187
  .to eq false
174
188
  end
175
189
  end
190
+
191
+ it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
192
+ default = Ransack.options.clone
193
+
194
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
195
+
196
+ expect(Ransack.options[:postgres_fields_sort_option]).to eq :nulls_first
197
+
198
+ Ransack.options = default
199
+ end
176
200
  end
177
201
  end
@@ -26,7 +26,7 @@ module Ransack
26
26
  # @s.created_at_eq = date_values # This works in Rails 4.x but not 3.x
27
27
  @s.created_at_eq = [2011, 1, 2, 3, 4, 5] # so we have to do this
28
28
  html = @f.datetime_select(
29
- :created_at_eq, :use_month_numbers => true, :include_seconds => true
29
+ :created_at_eq, use_month_numbers: true, include_seconds: true
30
30
  )
31
31
  date_values.each { |val| expect(html).to include date_select_html(val) }
32
32
  end
@@ -70,13 +70,13 @@ module Ransack
70
70
 
71
71
  describe '#sort_link' do
72
72
  it 'sort_link for ransack attribute' do
73
- sort_link = @f.sort_link :name, :controller => 'people'
73
+ sort_link = @f.sort_link :name, controller: 'people'
74
74
  expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
75
75
  expect(sort_link).to match /sort_link/
76
76
  expect(sort_link).to match /Full Name<\/a>/
77
77
  end
78
78
  it 'sort_link for common attribute' do
79
- sort_link = @f.sort_link :id, :controller => 'people'
79
+ sort_link = @f.sort_link :id, controller: 'people'
80
80
  expect(sort_link).to match /id<\/a>/
81
81
  end
82
82
  end
@@ -99,14 +99,14 @@ module Ransack
99
99
  it 'returns ransackable attributes for associations with :associations' do
100
100
  attributes = Person.ransackable_attributes +
101
101
  Article.ransackable_attributes.map { |a| "articles_#{a}" }
102
- html = @f.attribute_select(:associations => ['articles'])
102
+ html = @f.attribute_select(associations: ['articles'])
103
103
  expect(html.split(/\n/).size).to eq(attributes.size)
104
104
  attributes.each do |attribute|
105
105
  expect(html).to match /<option value="#{attribute}">/
106
106
  end
107
107
  end
108
108
  it 'returns option groups for base and associations with :associations' do
109
- html = @f.attribute_select(:associations => ['articles'])
109
+ html = @f.attribute_select(associations: ['articles'])
110
110
  [Person, Article].each do |model|
111
111
  expect(html).to match /<optgroup label="#{model}">/
112
112
  end
@@ -121,19 +121,19 @@ module Ransack
121
121
  end
122
122
  end
123
123
  it 'filters predicates with single-value :only' do
124
- html = @f.predicate_select :only => 'eq'
124
+ html = @f.predicate_select only: 'eq'
125
125
  Predicate.names.reject { |k| k =~ /^eq/ }.each do |key|
126
126
  expect(html).not_to match /<option value="#{key}">/
127
127
  end
128
128
  end
129
129
  it 'filters predicates with multi-value :only' do
130
- html = @f.predicate_select :only => [:eq, :lt]
130
+ html = @f.predicate_select only: [:eq, :lt]
131
131
  Predicate.names.reject { |k| k =~ /^(eq|lt)/ }.each do |key|
132
132
  expect(html).not_to match /<option value="#{key}">/
133
133
  end
134
134
  end
135
135
  it 'excludes compounds when compounds: false' do
136
- html = @f.predicate_select :compounds => false
136
+ html = @f.predicate_select compounds: false
137
137
  Predicate.names.select { |k| k =~ /_(any|all)$/ }.each do |key|
138
138
  expect(html).not_to match /<option value="#{key}">/
139
139
  end