ransack 1.8.4 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/SECURITY.md +12 -0
  4. data/.github/workflows/cronjob.yml +102 -0
  5. data/.github/workflows/deploy.yml +35 -0
  6. data/.github/workflows/rubocop.yml +20 -0
  7. data/.github/workflows/test-deploy.yml +29 -0
  8. data/.github/workflows/test.yml +130 -0
  9. data/.gitignore +3 -0
  10. data/{lib/ransack/adapters/mongoid/3.2/.gitkeep → .nojekyll} +0 -0
  11. data/.rubocop.yml +44 -0
  12. data/CHANGELOG.md +352 -0
  13. data/CONTRIBUTING.md +25 -13
  14. data/Gemfile +26 -27
  15. data/README.md +65 -815
  16. data/Rakefile +1 -22
  17. data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
  18. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +71 -0
  19. data/docs/.gitignore +19 -0
  20. data/docs/.nojekyll +0 -0
  21. data/docs/babel.config.js +3 -0
  22. data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
  23. data/docs/docs/getting-started/_category_.json +4 -0
  24. data/docs/docs/getting-started/advanced-mode.md +46 -0
  25. data/docs/docs/getting-started/configuration.md +47 -0
  26. data/docs/docs/getting-started/search-matches.md +67 -0
  27. data/docs/docs/getting-started/simple-mode.md +284 -0
  28. data/docs/docs/getting-started/sorting.md +79 -0
  29. data/docs/docs/getting-started/using-predicates.md +282 -0
  30. data/docs/docs/going-further/_category_.json +4 -0
  31. data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
  32. data/docs/docs/going-further/associations.md +70 -0
  33. data/docs/docs/going-further/custom-predicates.md +52 -0
  34. data/docs/docs/going-further/documentation.md +43 -0
  35. data/docs/docs/going-further/exporting-to-csv.md +49 -0
  36. data/docs/docs/going-further/external-guides.md +57 -0
  37. data/docs/docs/going-further/form-customisation.md +63 -0
  38. data/docs/docs/going-further/i18n.md +53 -0
  39. data/docs/docs/going-further/img/create_release.png +0 -0
  40. data/docs/docs/going-further/merging-searches.md +41 -0
  41. data/docs/docs/going-further/other-notes.md +428 -0
  42. data/docs/docs/going-further/polymorphic-search.md +40 -0
  43. data/docs/docs/going-further/ransackers.md +331 -0
  44. data/docs/docs/going-further/release_process.md +36 -0
  45. data/docs/docs/going-further/saving-queries.md +82 -0
  46. data/docs/docs/going-further/searching-postgres.md +57 -0
  47. data/docs/docs/going-further/wiki-contributors.md +82 -0
  48. data/docs/docs/intro.md +99 -0
  49. data/docs/docusaurus.config.js +120 -0
  50. data/docs/package.json +38 -0
  51. data/docs/sidebars.js +31 -0
  52. data/docs/src/components/HomepageFeatures/index.js +64 -0
  53. data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  54. data/docs/src/css/custom.css +39 -0
  55. data/docs/src/pages/index.module.css +23 -0
  56. data/docs/src/pages/markdown-page.md +7 -0
  57. data/docs/static/.nojekyll +0 -0
  58. data/docs/static/img/docusaurus.png +0 -0
  59. data/docs/static/img/favicon.ico +0 -0
  60. data/docs/static/img/logo.svg +1 -0
  61. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  62. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  63. data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  64. data/docs/static/img/undraw_docusaurus_react.svg +170 -0
  65. data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  66. data/docs/static/logo/ransack-h.png +0 -0
  67. data/docs/static/logo/ransack-h.svg +34 -0
  68. data/docs/static/logo/ransack-v.png +0 -0
  69. data/docs/static/logo/ransack-v.svg +34 -0
  70. data/docs/static/logo/ransack.png +0 -0
  71. data/docs/static/logo/ransack.svg +21 -0
  72. data/docs/yarn.lock +8436 -0
  73. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +70 -0
  74. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +92 -0
  75. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -0
  76. data/lib/polyamorous/activerecord_7.0_ruby_2/join_association.rb +1 -0
  77. data/lib/polyamorous/activerecord_7.0_ruby_2/join_dependency.rb +1 -0
  78. data/lib/polyamorous/activerecord_7.0_ruby_2/reflection.rb +1 -0
  79. data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
  80. data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
  81. data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
  82. data/lib/polyamorous/join.rb +70 -0
  83. data/lib/polyamorous/polyamorous.rb +24 -0
  84. data/lib/polyamorous/swapping_reflection_class.rb +11 -0
  85. data/lib/polyamorous/tree_node.rb +7 -0
  86. data/lib/polyamorous.rb +1 -0
  87. data/lib/ransack/adapters/active_record/base.rb +14 -3
  88. data/lib/ransack/adapters/active_record/context.rb +140 -196
  89. data/lib/ransack/adapters/active_record/ransack/constants.rb +19 -4
  90. data/lib/ransack/adapters/active_record/ransack/context.rb +9 -19
  91. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +7 -7
  92. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
  93. data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
  94. data/lib/ransack/adapters/active_record.rb +0 -9
  95. data/lib/ransack/adapters.rb +2 -0
  96. data/lib/ransack/configuration.rb +52 -2
  97. data/lib/ransack/constants.rb +1 -5
  98. data/lib/ransack/context.rb +29 -24
  99. data/lib/ransack/helpers/form_builder.rb +12 -6
  100. data/lib/ransack/helpers/form_helper.rb +11 -3
  101. data/lib/ransack/helpers.rb +1 -1
  102. data/lib/ransack/locale/ar.yml +70 -0
  103. data/lib/ransack/locale/az.yml +70 -0
  104. data/lib/ransack/locale/bg.yml +70 -0
  105. data/lib/ransack/locale/ca.yml +70 -0
  106. data/lib/ransack/locale/el.yml +70 -0
  107. data/lib/ransack/locale/es.yml +22 -22
  108. data/lib/ransack/locale/fa.yml +70 -0
  109. data/lib/ransack/locale/fi.yml +71 -0
  110. data/lib/ransack/locale/nl.yml +4 -4
  111. data/lib/ransack/locale/ru.yml +70 -0
  112. data/lib/ransack/locale/sk.yml +70 -0
  113. data/lib/ransack/locale/sv.yml +70 -0
  114. data/lib/ransack/locale/tr.yml +70 -0
  115. data/lib/ransack/locale/zh-CN.yml +12 -12
  116. data/lib/ransack/nodes/attribute.rb +2 -2
  117. data/lib/ransack/nodes/condition.rb +7 -1
  118. data/lib/ransack/nodes/grouping.rb +3 -8
  119. data/lib/ransack/nodes/sort.rb +3 -3
  120. data/lib/ransack/nodes/value.rb +3 -3
  121. data/lib/ransack/predicate.rb +13 -20
  122. data/lib/ransack/search.rb +7 -4
  123. data/lib/ransack/translate.rb +115 -115
  124. data/lib/ransack/version.rb +1 -1
  125. data/lib/ransack/visitor.rb +1 -12
  126. data/lib/ransack.rb +7 -5
  127. data/ransack.gemspec +9 -25
  128. data/spec/blueprints/articles.rb +1 -1
  129. data/spec/blueprints/comments.rb +1 -1
  130. data/spec/blueprints/notes.rb +1 -1
  131. data/spec/blueprints/tags.rb +1 -1
  132. data/spec/console.rb +5 -5
  133. data/spec/helpers/polyamorous_helper.rb +13 -0
  134. data/spec/helpers/ransack_helper.rb +1 -1
  135. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  136. data/spec/polyamorous/join_association_spec.rb +30 -0
  137. data/spec/polyamorous/join_dependency_spec.rb +81 -0
  138. data/spec/polyamorous/join_spec.rb +19 -0
  139. data/spec/ransack/adapters/active_record/base_spec.rb +105 -11
  140. data/spec/ransack/adapters/active_record/context_spec.rb +63 -24
  141. data/spec/ransack/configuration_spec.rb +24 -0
  142. data/spec/ransack/helpers/form_builder_spec.rb +3 -15
  143. data/spec/ransack/helpers/form_helper_spec.rb +135 -168
  144. data/spec/ransack/nodes/condition_spec.rb +13 -0
  145. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  146. data/spec/ransack/nodes/value_spec.rb +115 -0
  147. data/spec/ransack/predicate_spec.rb +54 -2
  148. data/spec/ransack/search_spec.rb +266 -36
  149. data/spec/spec_helper.rb +14 -5
  150. data/spec/support/schema.rb +99 -21
  151. metadata +117 -187
  152. data/.travis.yml +0 -86
  153. data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
  154. data/lib/ransack/adapters/active_record/3.0/context.rb +0 -203
  155. data/lib/ransack/adapters/active_record/3.1/context.rb +0 -212
  156. data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
  157. data/lib/ransack/adapters/active_record/compat.rb +0 -14
  158. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
  159. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
  160. data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
  161. data/lib/ransack/adapters/mongoid/base.rb +0 -134
  162. data/lib/ransack/adapters/mongoid/context.rb +0 -212
  163. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
  164. data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
  165. data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
  166. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
  167. data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
  168. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
  169. data/lib/ransack/adapters/mongoid/table.rb +0 -35
  170. data/lib/ransack/adapters/mongoid.rb +0 -15
  171. data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -314
  172. data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
  173. data/spec/mongoid/configuration_spec.rb +0 -162
  174. data/spec/mongoid/dependencies_spec.rb +0 -8
  175. data/spec/mongoid/helpers/ransack_helper.rb +0 -11
  176. data/spec/mongoid/nodes/condition_spec.rb +0 -49
  177. data/spec/mongoid/nodes/grouping_spec.rb +0 -13
  178. data/spec/mongoid/predicate_spec.rb +0 -155
  179. data/spec/mongoid/search_spec.rb +0 -445
  180. data/spec/mongoid/support/mongoid.yml +0 -11
  181. data/spec/mongoid/support/schema.rb +0 -135
  182. data/spec/mongoid/translate_spec.rb +0 -14
  183. data/spec/mongoid_spec_helper.rb +0 -63
  184. data/spec/ransack/dependencies_spec.rb +0 -12
@@ -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
@@ -211,6 +312,29 @@ module Ransack
211
312
  expect { Search.new(Person, params) }.not_to change { params }
212
313
  end
213
314
 
315
+ context "ransackable_scope" do
316
+ around(:each) do |example|
317
+ Person.define_singleton_method(:name_eq) do |name|
318
+ self.where(name: name)
319
+ end
320
+
321
+ begin
322
+ example.run
323
+ ensure
324
+ Person.singleton_class.undef_method :name_eq
325
+ end
326
+ end
327
+
328
+ it "is prioritized over base predicates" do
329
+ allow(Person).to receive(:ransackable_scopes)
330
+ .and_return(Person.ransackable_scopes + [:name_eq])
331
+
332
+ s = Search.new(Person, name_eq: "Johny")
333
+ expect(s.instance_variable_get(:@scope_args)["name_eq"]).to eq("Johny")
334
+ expect(s.base[:name_eq]).to be_nil
335
+ end
336
+ end
337
+
214
338
  end
215
339
 
216
340
  describe '#result' do
@@ -220,6 +344,9 @@ module Ransack
220
344
  let(:children_people_name_field) {
221
345
  "#{quote_table_name("children_people")}.#{quote_column_name("name")}"
222
346
  }
347
+ let(:notable_type_field) {
348
+ "#{quote_table_name("notes")}.#{quote_column_name("notable_type")}"
349
+ }
223
350
  it 'evaluates conditions contextually' do
224
351
  s = Search.new(Person, children_name_eq: 'Ernie')
225
352
  expect(s.result).to be_an ActiveRecord::Relation
@@ -227,13 +354,32 @@ module Ransack
227
354
  children_people_name_field} = 'Ernie'/
228
355
  end
229
356
 
230
- # FIXME: Make this spec pass for Rails 4.1 / 4.2 / 5.0 and not just 4.0 by
231
- # commenting out lines 221 and 242 to run the test. Addresses issue #374.
232
- # https://github.com/activerecord-hackery/ransack/issues/374
233
- #
234
- it 'evaluates conditions for multiple `belongs_to` associations to the
235
- same table contextually',
236
- if: ::ActiveRecord::VERSION::STRING.first(3) == '4.0' do
357
+ it 'use appropriate table alias' do
358
+ s = Search.new(Person, {
359
+ name_eq: "person_name_query",
360
+ articles_title_eq: "person_article_title_query",
361
+ parent_name_eq: "parent_name_query",
362
+ parent_articles_title_eq: 'parents_article_title_query'
363
+ }).result
364
+
365
+ real_query = remove_quotes_and_backticks(s.to_sql)
366
+
367
+ expect(real_query)
368
+ .to match(%r{LEFT OUTER JOIN articles ON (\('default_scope' = 'default_scope'\) AND )?articles.person_id = people.id})
369
+ expect(real_query)
370
+ .to match(%r{LEFT OUTER JOIN articles articles_people ON (\('default_scope' = 'default_scope'\) AND )?articles_people.person_id = parents_people.id})
371
+
372
+ expect(real_query)
373
+ .to include "people.name = 'person_name_query'"
374
+ expect(real_query)
375
+ .to include "articles.title = 'person_article_title_query'"
376
+ expect(real_query)
377
+ .to include "parents_people.name = 'parent_name_query'"
378
+ expect(real_query)
379
+ .to include "articles_people.title = 'parents_article_title_query'"
380
+ end
381
+
382
+ it 'evaluates conditions for multiple `belongs_to` associations to the same table contextually' do
237
383
  s = Search.new(
238
384
  Recommendation,
239
385
  person_name_eq: 'Ernie',
@@ -248,7 +394,7 @@ module Ransack
248
394
  ON target_people_recommendations.id = recommendations.target_person_id
249
395
  LEFT OUTER JOIN people parents_people
250
396
  ON parents_people.id = target_people_recommendations.parent_id
251
- WHERE ((people.name = 'Ernie' AND parents_people.name = 'Test'))
397
+ WHERE (people.name = 'Ernie' AND parents_people.name = 'Test')
252
398
  SQL
253
399
  .squish
254
400
  expect(real_query).to eq expected_query
@@ -265,6 +411,7 @@ module Ransack
265
411
  s = Search.new(Note, notable_of_Person_type_name_eq: 'Ernie').result
266
412
  expect(s).to be_an ActiveRecord::Relation
267
413
  expect(s.to_sql).to match /#{people_name_field} = 'Ernie'/
414
+ expect(s.to_sql).to match /#{notable_type_field} = 'Person'/
268
415
  end
269
416
 
270
417
  it 'evaluates nested conditions' do
@@ -303,11 +450,8 @@ module Ransack
303
450
  { m: 'or', comments_body_cont: 'e', articles_comments_body_cont: 'e' }
304
451
  ]
305
452
  )
306
- if ActiveRecord::VERSION::MAJOR == 3
307
- all_or_load, uniq_or_distinct = :all, :uniq
308
- else
309
- all_or_load, uniq_or_distinct = :load, :distinct
310
- end
453
+
454
+ all_or_load, uniq_or_distinct = :load, :distinct
311
455
  expect(s.result.send(all_or_load).size)
312
456
  .to eq(9000)
313
457
  expect(s.result(distinct: true).size)
@@ -316,6 +460,11 @@ module Ransack
316
460
  .to eq s.result(distinct: true).send(all_or_load)
317
461
  end
318
462
 
463
+ it 'evaluates joins with belongs_to join' do
464
+ s = Person.joins(:parent).ransack(parent_name_eq: 'Ernie').result(distinct: true)
465
+ expect(s).to be_an ActiveRecord::Relation
466
+ end
467
+
319
468
  private
320
469
 
321
470
  def remove_quotes_and_backticks(str)
@@ -355,88 +504,169 @@ module Ransack
355
504
  expect(sort.dir).to eq 'asc'
356
505
  end
357
506
 
358
- it 'creates sorts based on multiple attributes/directions in array format' do
359
- @s.sorts = ['id desc', { name: 'name', dir: 'asc' }]
507
+ it 'creates sorts based on a single alias/direction' do
508
+ @s.sorts = 'daddy desc'
509
+ expect(@s.sorts.size).to eq(1)
510
+ sort = @s.sorts.first
511
+ expect(sort).to be_a Nodes::Sort
512
+ expect(sort.name).to eq 'parent_name'
513
+ expect(sort.dir).to eq 'desc'
514
+ end
515
+
516
+ it 'creates sorts based on a single alias and uppercase direction' do
517
+ @s.sorts = 'daddy DESC'
518
+ expect(@s.sorts.size).to eq(1)
519
+ sort = @s.sorts.first
520
+ expect(sort).to be_a Nodes::Sort
521
+ expect(sort.name).to eq 'parent_name'
522
+ expect(sort.dir).to eq 'desc'
523
+ end
524
+
525
+ it 'creates sorts based on a single alias and without direction' do
526
+ @s.sorts = 'daddy'
527
+ expect(@s.sorts.size).to eq(1)
528
+ sort = @s.sorts.first
529
+ expect(sort).to be_a Nodes::Sort
530
+ expect(sort.name).to eq 'parent_name'
531
+ expect(sort.dir).to eq 'asc'
532
+ end
533
+
534
+ it 'creates sorts based on attributes, alias and directions in array format' do
535
+ @s.sorts = ['id desc', { name: 'daddy', dir: 'asc' }]
360
536
  expect(@s.sorts.size).to eq(2)
361
537
  sort1, sort2 = @s.sorts
362
538
  expect(sort1).to be_a Nodes::Sort
363
539
  expect(sort1.name).to eq 'id'
364
540
  expect(sort1.dir).to eq 'desc'
365
541
  expect(sort2).to be_a Nodes::Sort
366
- expect(sort2.name).to eq 'name'
542
+ expect(sort2.name).to eq 'parent_name'
367
543
  expect(sort2.dir).to eq 'asc'
368
544
  end
369
545
 
370
- it 'creates sorts based on multiple attributes and uppercase directions in array format' do
371
- @s.sorts = ['id DESC', { name: 'name', dir: 'ASC' }]
546
+ it 'creates sorts based on attributes, alias and uppercase directions in array format' do
547
+ @s.sorts = ['id DESC', { name: 'daddy', dir: 'ASC' }]
372
548
  expect(@s.sorts.size).to eq(2)
373
549
  sort1, sort2 = @s.sorts
374
550
  expect(sort1).to be_a Nodes::Sort
375
551
  expect(sort1.name).to eq 'id'
376
552
  expect(sort1.dir).to eq 'desc'
377
553
  expect(sort2).to be_a Nodes::Sort
378
- expect(sort2.name).to eq 'name'
554
+ expect(sort2.name).to eq 'parent_name'
379
555
  expect(sort2.dir).to eq 'asc'
380
556
  end
381
557
 
382
- it 'creates sorts based on multiple attributes and different directions
558
+ it 'creates sorts based on attributes, alias and different directions
383
559
  in array format' do
384
- @s.sorts = ['id DESC', { name: 'name', dir: nil }]
560
+ @s.sorts = ['id DESC', { name: 'daddy', dir: nil }]
385
561
  expect(@s.sorts.size).to eq(2)
386
562
  sort1, sort2 = @s.sorts
387
563
  expect(sort1).to be_a Nodes::Sort
388
564
  expect(sort1.name).to eq 'id'
389
565
  expect(sort1.dir).to eq 'desc'
390
566
  expect(sort2).to be_a Nodes::Sort
391
- expect(sort2.name).to eq 'name'
567
+ expect(sort2.name).to eq 'parent_name'
392
568
  expect(sort2.dir).to eq 'asc'
393
569
  end
394
570
 
395
- it 'creates sorts based on multiple attributes/directions in hash format' do
571
+ it 'creates sorts based on attributes, alias and directions in hash format' do
396
572
  @s.sorts = {
397
573
  '0' => { name: 'id', dir: 'desc' },
398
- '1' => { name: 'name', dir: 'asc' }
574
+ '1' => { name: 'daddy', dir: 'asc' }
399
575
  }
400
576
  expect(@s.sorts.size).to eq(2)
401
577
  expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
402
578
  id_sort = @s.sorts.detect { |s| s.name == 'id' }
403
- name_sort = @s.sorts.detect { |s| s.name == 'name' }
579
+ daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
404
580
  expect(id_sort.dir).to eq 'desc'
405
- expect(name_sort.dir).to eq 'asc'
581
+ expect(daddy_sort.dir).to eq 'asc'
406
582
  end
407
583
 
408
- it 'creates sorts based on multiple attributes and uppercase directions
584
+ it 'creates sorts based on attributes, alias and uppercase directions
409
585
  in hash format' do
410
586
  @s.sorts = {
411
587
  '0' => { name: 'id', dir: 'DESC' },
412
- '1' => { name: 'name', dir: 'ASC' }
588
+ '1' => { name: 'daddy', dir: 'ASC' }
413
589
  }
414
590
  expect(@s.sorts.size).to eq(2)
415
591
  expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
416
592
  id_sort = @s.sorts.detect { |s| s.name == 'id' }
417
- name_sort = @s.sorts.detect { |s| s.name == 'name' }
593
+ daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
418
594
  expect(id_sort.dir).to eq 'desc'
419
- expect(name_sort.dir).to eq 'asc'
595
+ expect(daddy_sort.dir).to eq 'asc'
420
596
  end
421
597
 
422
- it 'creates sorts based on multiple attributes and different directions
598
+ it 'creates sorts based on attributes, alias and different directions
423
599
  in hash format' do
424
600
  @s.sorts = {
425
601
  '0' => { name: 'id', dir: 'DESC' },
426
- '1' => { name: 'name', dir: nil }
602
+ '1' => { name: 'daddy', dir: nil }
427
603
  }
428
604
  expect(@s.sorts.size).to eq(2)
429
605
  expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
430
606
  id_sort = @s.sorts.detect { |s| s.name == 'id' }
431
- name_sort = @s.sorts.detect { |s| s.name == 'name' }
607
+ daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
432
608
  expect(id_sort.dir).to eq 'desc'
433
- expect(name_sort.dir).to eq 'asc'
609
+ expect(daddy_sort.dir).to eq 'asc'
434
610
  end
435
611
 
436
612
  it 'overrides existing sort' do
437
613
  @s.sorts = 'id asc'
438
614
  expect(@s.result.first.id).to eq 1
439
615
  end
616
+
617
+ it "PG's sort option", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
618
+ default = Ransack.options.clone
619
+
620
+ s = Search.new(Person, s: 'name asc')
621
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC"
622
+
623
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
624
+ s = Search.new(Person, s: 'name asc')
625
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS FIRST"
626
+ s = Search.new(Person, s: 'name desc')
627
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS LAST"
628
+
629
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
630
+ s = Search.new(Person, s: 'name asc')
631
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" ASC NULLS LAST"
632
+ s = Search.new(Person, s: 'name desc')
633
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" DESC NULLS FIRST"
634
+
635
+ Ransack.options = default
636
+ end
637
+
638
+ it "PG's sort option with double name", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
639
+ default = Ransack.options.clone
640
+
641
+ s = Search.new(Person, s: 'doubled_name asc')
642
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC"
643
+
644
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
645
+ s = Search.new(Person, s: 'doubled_name asc')
646
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
647
+ s = Search.new(Person, s: 'doubled_name desc')
648
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
649
+
650
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
651
+ s = Search.new(Person, s: 'doubled_name asc')
652
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
653
+ s = Search.new(Person, s: 'doubled_name desc')
654
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
655
+
656
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_first }
657
+ s = Search.new(Person, s: 'doubled_name asc')
658
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
659
+ s = Search.new(Person, s: 'doubled_name desc')
660
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
661
+
662
+ Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_last }
663
+ s = Search.new(Person, s: 'doubled_name asc')
664
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
665
+ s = Search.new(Person, s: 'doubled_name desc')
666
+ expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
667
+
668
+ Ransack.options = default
669
+ end
440
670
  end
441
671
 
442
672
  describe '#method_missing' do
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,15 @@
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'
9
+ require 'simplecov'
10
+ require 'byebug'
6
11
 
12
+ SimpleCov.start
7
13
  I18n.enforce_available_locales = false
8
14
  Time.zone = 'Eastern Time (US & Canada)'
9
15
  I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'support', '*.yml')]
@@ -11,16 +17,17 @@ I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'support', '*.yml')]
11
17
  Dir[File.expand_path('../{helpers,support,blueprints}/*.rb', __FILE__)]
12
18
  .each { |f| require f }
13
19
 
20
+ Faker::Config.random = Random.new(0)
14
21
  Sham.define do
15
22
  name { Faker::Name.name }
16
23
  title { Faker::Lorem.sentence }
17
24
  body { Faker::Lorem.paragraph }
18
25
  salary { |index| 30000 + (index * 1000) }
19
- tag_name { Faker::Lorem.words(3).join(' ') }
20
- note { Faker::Lorem.words(7).join(' ') }
21
- only_admin { Faker::Lorem.words(3).join(' ') }
22
- only_search { Faker::Lorem.words(3).join(' ') }
23
- 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(' ') }
24
31
  notable_id { |id| id }
25
32
  end
26
33
 
@@ -35,12 +42,14 @@ RSpec.configure do |config|
35
42
  line = '=' * message.length
36
43
  puts line, message, line
37
44
  Schema.create
45
+ SubDB::Schema.create
38
46
  end
39
47
 
40
48
  config.before(:all) { Sham.reset(:before_all) }
41
49
  config.before(:each) { Sham.reset(:before_each) }
42
50
 
43
51
  config.include RansackHelper
52
+ config.include PolyamorousHelper
44
53
  end
45
54
 
46
55
  RSpec::Matchers.define :be_like do |expected|