ransack 2.3.2 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) 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 +251 -1
  13. data/CONTRIBUTING.md +51 -29
  14. data/Gemfile +12 -10
  15. data/README.md +45 -907
  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/join_association.rb +70 -0
  67. data/{polyamorous/lib/polyamorous/activerecord_6.0_ruby_2 → lib/polyamorous/activerecord}/join_dependency.rb +33 -12
  68. data/lib/polyamorous/activerecord/reflection.rb +11 -0
  69. data/{polyamorous/lib → lib/polyamorous}/polyamorous.rb +3 -4
  70. data/lib/ransack/adapters/active_record/base.rb +83 -10
  71. data/lib/ransack/adapters/active_record/context.rb +56 -44
  72. data/lib/ransack/configuration.rb +53 -10
  73. data/lib/ransack/constants.rb +126 -4
  74. data/lib/ransack/context.rb +34 -5
  75. data/lib/ransack/helpers/form_builder.rb +6 -6
  76. data/lib/ransack/helpers/form_helper.rb +14 -5
  77. data/lib/ransack/helpers.rb +1 -1
  78. data/lib/ransack/locale/sv.yml +70 -0
  79. data/lib/ransack/nodes/attribute.rb +3 -3
  80. data/lib/ransack/nodes/condition.rb +80 -9
  81. data/lib/ransack/nodes/grouping.rb +4 -4
  82. data/lib/ransack/nodes/node.rb +1 -1
  83. data/lib/ransack/nodes/sort.rb +3 -3
  84. data/lib/ransack/nodes/value.rb +3 -3
  85. data/lib/ransack/predicate.rb +1 -1
  86. data/lib/ransack/ransacker.rb +1 -1
  87. data/lib/ransack/search.rb +15 -7
  88. data/lib/ransack/translate.rb +6 -6
  89. data/lib/ransack/version.rb +1 -1
  90. data/lib/ransack/visitor.rb +38 -2
  91. data/lib/ransack.rb +5 -8
  92. data/ransack.gemspec +9 -15
  93. data/spec/blueprints/articles.rb +1 -1
  94. data/spec/blueprints/comments.rb +1 -1
  95. data/spec/blueprints/notes.rb +1 -1
  96. data/spec/blueprints/tags.rb +1 -1
  97. data/spec/console.rb +5 -5
  98. data/spec/helpers/polyamorous_helper.rb +2 -8
  99. data/spec/helpers/ransack_helper.rb +1 -1
  100. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  101. data/spec/{ransack → polyamorous}/join_association_spec.rb +3 -1
  102. data/spec/{ransack → polyamorous}/join_dependency_spec.rb +0 -16
  103. data/spec/ransack/adapters/active_record/base_spec.rb +125 -16
  104. data/spec/ransack/adapters/active_record/context_spec.rb +19 -18
  105. data/spec/ransack/configuration_spec.rb +33 -9
  106. data/spec/ransack/helpers/form_builder_spec.rb +8 -8
  107. data/spec/ransack/helpers/form_helper_spec.rb +109 -20
  108. data/spec/ransack/nodes/condition_spec.rb +37 -0
  109. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  110. data/spec/ransack/nodes/value_spec.rb +115 -0
  111. data/spec/ransack/predicate_spec.rb +37 -2
  112. data/spec/ransack/search_spec.rb +238 -30
  113. data/spec/ransack/translate_spec.rb +1 -1
  114. data/spec/spec_helper.rb +7 -5
  115. data/spec/support/schema.rb +108 -11
  116. metadata +98 -62
  117. data/.travis.yml +0 -47
  118. data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -128
  119. data/lib/ransack/adapters/active_record/ransack/context.rb +0 -55
  120. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +0 -61
  121. data/lib/ransack/adapters/active_record/ransack/translate.rb +0 -8
  122. data/lib/ransack/adapters/active_record/ransack/visitor.rb +0 -47
  123. data/lib/ransack/adapters.rb +0 -64
  124. data/lib/ransack/nodes.rb +0 -8
  125. data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +0 -20
  126. data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -79
  127. data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -12
  128. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -2
  129. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -2
  130. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -2
  131. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -2
  132. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +0 -2
  133. data/polyamorous/lib/polyamorous/version.rb +0 -3
  134. data/polyamorous/polyamorous.gemspec +0 -27
  135. /data/{logo → docs/static/logo}/ransack-h.png +0 -0
  136. /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
  137. /data/{logo → docs/static/logo}/ransack-v.png +0 -0
  138. /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
  139. /data/{logo → docs/static/logo}/ransack.png +0 -0
  140. /data/{logo → docs/static/logo}/ransack.svg +0 -0
  141. /data/{polyamorous/lib → lib}/polyamorous/join.rb +0 -0
  142. /data/{polyamorous/lib → lib}/polyamorous/swapping_reflection_class.rb +0 -0
  143. /data/{polyamorous/lib → lib}/polyamorous/tree_node.rb +0 -0
  144. /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
  145. /data/spec/{ransack → polyamorous}/join_spec.rb +0 -0
@@ -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 '▼'
50
64
  expect(Ransack.options[:down_arrow]).to eq '▲'
@@ -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
@@ -140,6 +140,32 @@ module Ransack
140
140
  }
141
141
  end
142
142
 
143
+ describe '#sort_link works even if search params are a string' do
144
+ before { @controller.view_context.params[:q] = 'input error' }
145
+ specify {
146
+ expect { @controller.view_context
147
+ .sort_link(
148
+ Person.ransack({}),
149
+ :name,
150
+ controller: 'people'
151
+ )
152
+ }.not_to raise_error
153
+ }
154
+ end
155
+
156
+ describe '#sort_url works even if search params are a string' do
157
+ before { @controller.view_context.params[:q] = 'input error' }
158
+ specify {
159
+ expect { @controller.view_context
160
+ .sort_url(
161
+ Person.ransack({}),
162
+ :name,
163
+ controller: 'people'
164
+ )
165
+ }.not_to raise_error
166
+ }
167
+ end
168
+
143
169
  describe '#sort_link with search_key defined as a string' do
144
170
  subject { @controller.view_context
145
171
  .sort_link(
@@ -186,7 +212,7 @@ module Ransack
186
212
  )
187
213
  }
188
214
  it {
189
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
215
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
190
216
  )
191
217
  }
192
218
  it { should match /sort_link desc/ }
@@ -202,7 +228,7 @@ module Ransack
202
228
  )
203
229
  }
204
230
  it {
205
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
231
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
206
232
  )
207
233
  }
208
234
  end
@@ -216,7 +242,7 @@ module Ransack
216
242
  )
217
243
  }
218
244
  it {
219
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
245
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
220
246
  )
221
247
  }
222
248
  it { should match /sort_link desc/ }
@@ -232,7 +258,7 @@ module Ransack
232
258
  )
233
259
  }
234
260
  it {
235
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
261
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
236
262
  )
237
263
  }
238
264
  end
@@ -258,7 +284,7 @@ module Ransack
258
284
  )
259
285
  }
260
286
  it {
261
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
287
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
262
288
  )
263
289
  }
264
290
  it { should match /sort_link desc/ }
@@ -274,7 +300,7 @@ module Ransack
274
300
  )
275
301
  }
276
302
  it {
277
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
303
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
278
304
  )
279
305
  }
280
306
  end
@@ -289,7 +315,7 @@ module Ransack
289
315
  )
290
316
  }
291
317
  it {
292
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
318
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
293
319
  )
294
320
  }
295
321
  it { should match /sort_link/ }
@@ -306,7 +332,7 @@ module Ransack
306
332
  )
307
333
  }
308
334
  it {
309
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
335
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
310
336
  )
311
337
  }
312
338
  end
@@ -321,7 +347,7 @@ module Ransack
321
347
  )
322
348
  }
323
349
  it {
324
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
350
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
325
351
  )
326
352
  }
327
353
  it { should match /sort_link/ }
@@ -338,7 +364,7 @@ module Ransack
338
364
  )
339
365
  }
340
366
  it {
341
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
367
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
342
368
  )
343
369
  }
344
370
  end
@@ -353,7 +379,7 @@ module Ransack
353
379
  )
354
380
  }
355
381
  it {
356
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/
382
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/
357
383
  )
358
384
  }
359
385
  it { should match /sort_link/ }
@@ -370,7 +396,7 @@ module Ransack
370
396
  )
371
397
  }
372
398
  it {
373
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/
399
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/
374
400
  )
375
401
  }
376
402
  end
@@ -385,7 +411,7 @@ module Ransack
385
411
  )
386
412
  }
387
413
  it {
388
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
414
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
389
415
  )
390
416
  }
391
417
  it { should match /sort_link/ }
@@ -402,7 +428,7 @@ module Ransack
402
428
  )
403
429
  }
404
430
  it {
405
- should match( /people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
431
+ should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
406
432
  )
407
433
  }
408
434
  end
@@ -469,13 +495,12 @@ module Ransack
469
495
  it { should match /exist\=existing/ }
470
496
  end
471
497
 
472
- context 'using a real ActionController::Parameter object',
473
- if: ::ActiveRecord::VERSION::MAJOR > 3 do
498
+ context 'using a real ActionController::Parameter object' do
474
499
 
475
500
  describe 'with symbol q:, #sort_link should include search params' do
476
501
  subject { @controller.view_context.sort_link(Person.ransack, :name) }
477
502
  let(:params) { ActionController::Parameters.new(
478
- { :q => { name_eq: 'TEST' }, controller: 'people' }
503
+ { q: { name_eq: 'TEST' }, controller: 'people' }
479
504
  ) }
480
505
  before { @controller.instance_variable_set(:@params, params) }
481
506
 
@@ -490,7 +515,7 @@ module Ransack
490
515
  describe 'with symbol q:, #sort_url should include search params' do
491
516
  subject { @controller.view_context.sort_url(Person.ransack, :name) }
492
517
  let(:params) { ActionController::Parameters.new(
493
- { :q => { name_eq: 'TEST' }, controller: 'people' }
518
+ { q: { name_eq: 'TEST' }, controller: 'people' }
494
519
  ) }
495
520
  before { @controller.instance_variable_set(:@params, params) }
496
521
 
@@ -643,13 +668,13 @@ module Ransack
643
668
  before do
644
669
  Ransack.configure do |c|
645
670
  c.hide_sort_order_indicators = false
646
- c.custom_arrows = { default_arrow: "defaultarrow"}
671
+ c.custom_arrows = { default_arrow: "defaultarrow" }
647
672
  end
648
673
  end
649
674
 
650
675
  after do
651
676
  Ransack.configure do |c|
652
- c.custom_arrows = { default_arrow: nil}
677
+ c.custom_arrows = { default_arrow: nil }
653
678
  end
654
679
  end
655
680
 
@@ -727,6 +752,70 @@ module Ransack
727
752
  it { should match /Block label&nbsp;&#9660;/ }
728
753
  end
729
754
 
755
+ describe '#sort_link with class option' do
756
+ subject { @controller.view_context
757
+ .sort_link(
758
+ [:main_app, Person.ransack(sorts: ['name desc'])],
759
+ :name,
760
+ class: 'people', controller: 'people'
761
+ )
762
+ }
763
+ it { should match /class="sort_link desc people"/ }
764
+ it { should_not match /people\?class=people/ }
765
+ end
766
+
767
+ describe '#sort_link with class option workaround' do
768
+ it "generates a correct link and prints a deprecation" do
769
+ expect do
770
+ link = @controller.view_context
771
+ .sort_link(
772
+ [:main_app, Person.ransack(sorts: ['name desc'])],
773
+ :name,
774
+ 'name',
775
+ { controller: 'people' },
776
+ class: 'people'
777
+ )
778
+
779
+ expect(link).to match(/class="sort_link desc people"/)
780
+ expect(link).not_to match(/people\?class=people/)
781
+ end.to output(
782
+ /Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one\. \(called at #{Regexp.escape(__FILE__)}:/
783
+ ).to_stderr
784
+ end
785
+ end
786
+
787
+ describe '#sort_link with data option' do
788
+ subject { @controller.view_context
789
+ .sort_link(
790
+ [:main_app, Person.ransack(sorts: ['name desc'])],
791
+ :name,
792
+ data: { turbo_action: :advance }, controller: 'people'
793
+ )
794
+ }
795
+ it { should match /data-turbo-action="advance"/ }
796
+ it { should_not match /people\?data%5Bturbo_action%5D=advance/ }
797
+ end
798
+
799
+ describe "#sort_link with host option" do
800
+ subject { @controller.view_context
801
+ .sort_link(
802
+ [:main_app, Person.ransack(sorts: ['name desc'])],
803
+ :name,
804
+ host: 'foo', controller: 'people'
805
+ )
806
+ }
807
+ it { should match /href="\/people\?q/ }
808
+ it { should_not match /href=".*foo/ }
809
+ end
810
+
811
+ describe "#sort_link ignores host in params" do
812
+ before { @controller.view_context.params[:host] = 'other_domain' }
813
+ subject { @controller.view_context.sort_link(Person.ransack, :name, controller: 'people') }
814
+
815
+ it { should match /href="\/people\?q/ }
816
+ it { should_not match /href=".*other_domain/ }
817
+ end
818
+
730
819
  describe '#search_form_for with default format' do
731
820
  subject { @controller.view_context
732
821
  .search_form_for(Person.ransack) {} }
@@ -3,6 +3,19 @@ require 'spec_helper'
3
3
  module Ransack
4
4
  module Nodes
5
5
  describe Condition do
6
+ context 'bug report #1245' do
7
+ it 'preserves tuple behavior' do
8
+ ransack_hash = {
9
+ m: 'and',
10
+ g: [
11
+ { title_type_in: ['["title 1", ""]'] }
12
+ ]
13
+ }
14
+
15
+ sql = Article.ransack(ransack_hash).result.to_sql
16
+ expect(sql).to include("IN (('title 1', ''))")
17
+ end
18
+ end
6
19
 
7
20
  context 'with an alias' do
8
21
  subject {
@@ -61,6 +74,30 @@ module Ransack
61
74
  specify { expect(subject).to be_nil }
62
75
  end
63
76
  end
77
+
78
+ context 'with an empty predicate' do
79
+ subject {
80
+ Condition.extract(
81
+ Context.for(Person), 'full_name', Person.first.name
82
+ )
83
+ }
84
+
85
+ context "when default_predicate = nil" do
86
+ before do
87
+ Ransack.configure { |c| c.default_predicate = nil }
88
+ end
89
+
90
+ specify { expect(subject).to be_nil }
91
+ end
92
+
93
+ context "when default_predicate = 'eq'" do
94
+ before do
95
+ Ransack.configure { |c| c.default_predicate = 'eq' }
96
+ end
97
+
98
+ specify { expect(subject).to eq Condition.extract(Context.for(Person), 'full_name_eq', Person.first.name) }
99
+ end
100
+ end
64
101
  end
65
102
  end
66
103
  end
@@ -80,7 +80,7 @@ module Ransack
80
80
  'a' => {
81
81
  '0' => {
82
82
  'name' => 'with_arguments',
83
- 'ransacker_args' => [1,2]
83
+ 'ransacker_args' => [1, 2]
84
84
  }
85
85
  },
86
86
  'p' => 'eq',
@@ -90,7 +90,7 @@ module Ransack
90
90
  'a' => {
91
91
  '0' => {
92
92
  'name' => 'with_arguments',
93
- 'ransacker_args' => [3,4]
93
+ 'ransacker_args' => [3, 4]
94
94
  }
95
95
  },
96
96
  'p' => 'eq',
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ module Nodes
5
+ describe Value do
6
+ let(:context) { Context.for(Person) }
7
+
8
+ subject do
9
+ Value.new(context, raw_value)
10
+ end
11
+
12
+ context "with a date value" do
13
+ let(:raw_value) { "2022-05-23" }
14
+
15
+ [:date].each do |type|
16
+ it "should cast #{type} correctly" do
17
+ result = subject.cast(type)
18
+
19
+ expect(result).to be_a_kind_of(Date)
20
+ expect(result).to eq(Date.parse(raw_value))
21
+ end
22
+ end
23
+ end
24
+
25
+ context "with a timestamp value" do
26
+ let(:raw_value) { "2022-05-23 10:40:02 -0400" }
27
+
28
+ [:datetime, :timestamp, :time, :timestamptz].each do |type|
29
+ it "should cast #{type} correctly" do
30
+ result = subject.cast(type)
31
+
32
+ expect(result).to be_a_kind_of(Time)
33
+ expect(result).to eq(Time.zone.parse(raw_value))
34
+ end
35
+ end
36
+ end
37
+
38
+ Constants::TRUE_VALUES.each do |value|
39
+ context "with a true boolean value (#{value})" do
40
+ let(:raw_value) { value.to_s }
41
+
42
+ it "should cast boolean correctly" do
43
+ result = subject.cast(:boolean)
44
+ expect(result).to eq(true)
45
+ end
46
+ end
47
+ end
48
+
49
+ Constants::FALSE_VALUES.each do |value|
50
+ context "with a false boolean value (#{value})" do
51
+ let(:raw_value) { value.to_s }
52
+
53
+ it "should cast boolean correctly" do
54
+ result = subject.cast(:boolean)
55
+
56
+ expect(result).to eq(false)
57
+ end
58
+ end
59
+ end
60
+
61
+ ["12", "101.5"].each do |value|
62
+ context "with an integer value (#{value})" do
63
+ let(:raw_value) { value }
64
+
65
+ it "should cast #{value} to integer correctly" do
66
+ result = subject.cast(:integer)
67
+
68
+ expect(result).to be_an(Integer)
69
+ expect(result).to eq(value.to_i)
70
+ end
71
+ end
72
+ end
73
+
74
+ ["12", "101.5"].each do |value|
75
+ context "with a float value (#{value})" do
76
+ let(:raw_value) { value }
77
+
78
+ it "should cast #{value} to float correctly" do
79
+ result = subject.cast(:float)
80
+
81
+ expect(result).to be_an(Float)
82
+ expect(result).to eq(value.to_f)
83
+ end
84
+ end
85
+ end
86
+
87
+ ["12", "101.5"].each do |value|
88
+ context "with a decimal value (#{value})" do
89
+ let(:raw_value) { value }
90
+
91
+ it "should cast #{value} to decimal correctly" do
92
+ result = subject.cast(:decimal)
93
+
94
+ expect(result).to be_a(BigDecimal)
95
+ expect(result).to eq(value.to_d)
96
+ end
97
+ end
98
+ end
99
+
100
+ ["12", "101.513"].each do |value|
101
+ context "with a money value (#{value})" do
102
+ let(:raw_value) { value }
103
+
104
+ it "should cast #{value} to money correctly" do
105
+ result = subject.cast(:money)
106
+
107
+ expect(result).to be_a(String)
108
+ expect(result).to eq(value.to_f.to_s)
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
@@ -35,6 +35,13 @@ module Ransack
35
35
  @s.awesome_eq = nil
36
36
  expect(@s.result.to_sql).not_to match /WHERE/
37
37
  end
38
+
39
+ it 'generates a = condition with a huge integer value' do
40
+ val = 123456789012345678901
41
+ @s.salary_eq = val
42
+ field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
43
+ expect(@s.result.to_sql).to match /#{field} = #{val}/
44
+ end
38
45
  end
39
46
 
40
47
  describe 'lteq' do
@@ -56,6 +63,13 @@ module Ransack
56
63
  @s.salary_lteq = nil
57
64
  expect(@s.result.to_sql).not_to match /WHERE/
58
65
  end
66
+
67
+ it 'generates a <= condition with a huge integer value' do
68
+ val = 123456789012345678901
69
+ @s.salary_lteq = val
70
+ field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
71
+ expect(@s.result.to_sql).to match /#{field} <= #{val}/
72
+ end
59
73
  end
60
74
 
61
75
  describe 'lt' do
@@ -77,6 +91,13 @@ module Ransack
77
91
  @s.salary_lt = nil
78
92
  expect(@s.result.to_sql).not_to match /WHERE/
79
93
  end
94
+
95
+ it 'generates a = condition with a huge integer value' do
96
+ val = 123456789012345678901
97
+ @s.salary_lt = val
98
+ field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
99
+ expect(@s.result.to_sql).to match /#{field} < #{val}/
100
+ end
80
101
  end
81
102
 
82
103
  describe 'gteq' do
@@ -98,6 +119,13 @@ module Ransack
98
119
  @s.salary_gteq = nil
99
120
  expect(@s.result.to_sql).not_to match /WHERE/
100
121
  end
122
+
123
+ it 'generates a >= condition with a huge integer value' do
124
+ val = 123456789012345678901
125
+ @s.salary_gteq = val
126
+ field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
127
+ expect(@s.result.to_sql).to match /#{field} >= #{val}/
128
+ end
101
129
  end
102
130
 
103
131
  describe 'gt' do
@@ -119,6 +147,13 @@ module Ransack
119
147
  @s.salary_gt = nil
120
148
  expect(@s.result.to_sql).not_to match /WHERE/
121
149
  end
150
+
151
+ it 'generates a > condition with a huge integer value' do
152
+ val = 123456789012345678901
153
+ @s.salary_gt = val
154
+ field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
155
+ expect(@s.result.to_sql).to match /#{field} > #{val}/
156
+ end
122
157
  end
123
158
 
124
159
  describe 'cont' do
@@ -368,7 +403,7 @@ module Ransack
368
403
  expect(@s.result.to_sql).to match /#{field} IS NULL/
369
404
  end
370
405
 
371
- describe 'with association qeury' do
406
+ describe 'with association query' do
372
407
  it 'generates a value IS NOT NULL query' do
373
408
  @s.comments_id_not_null = true
374
409
  sql = @s.result.to_sql
@@ -422,7 +457,7 @@ module Ransack
422
457
  context "defining custom predicates" do
423
458
  describe "with 'not_in' arel predicate" do
424
459
  before do
425
- Ransack.configure {|c| c.add_predicate "not_in_csv", arel_predicate: "not_in", formatter: proc { |v| v.split(",") } }
460
+ Ransack.configure { |c| c.add_predicate "not_in_csv", arel_predicate: "not_in", formatter: proc { |v| v.split(",") } }
426
461
  end
427
462
 
428
463
  it 'generates a value IS NOT NULL query' do