ransack 2.1.1 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) 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/cronjob.yml +102 -0
  5. data/.github/workflows/rubocop.yml +20 -0
  6. data/.github/workflows/test.yml +163 -0
  7. data/.gitignore +1 -0
  8. data/.rubocop.yml +44 -0
  9. data/CHANGELOG.md +64 -1
  10. data/CONTRIBUTING.md +16 -11
  11. data/Gemfile +23 -17
  12. data/README.md +190 -57
  13. data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
  14. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +71 -0
  15. data/docs/img/create_release.png +0 -0
  16. data/docs/release_process.md +17 -0
  17. data/lib/polyamorous/{activerecord_5.2.1_ruby_2 → activerecord_5.2_ruby_2}/join_association.rb +2 -9
  18. data/lib/polyamorous/{activerecord_5.2.1_ruby_2 → activerecord_5.2_ruby_2}/join_dependency.rb +25 -3
  19. data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +11 -0
  20. data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +1 -0
  21. data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +80 -0
  22. data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +1 -0
  23. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +74 -0
  24. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +93 -0
  25. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +1 -0
  26. data/lib/polyamorous/activerecord_7.0_ruby_2/join_association.rb +1 -0
  27. data/lib/polyamorous/activerecord_7.0_ruby_2/join_dependency.rb +1 -0
  28. data/lib/polyamorous/activerecord_7.0_ruby_2/reflection.rb +1 -0
  29. data/lib/polyamorous/polyamorous.rb +24 -0
  30. data/lib/polyamorous.rb +1 -25
  31. data/lib/ransack/adapters/active_record/base.rb +5 -1
  32. data/lib/ransack/adapters/active_record/context.rb +71 -68
  33. data/lib/ransack/adapters/active_record/ransack/constants.rb +18 -3
  34. data/lib/ransack/adapters/active_record/ransack/context.rb +2 -6
  35. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +13 -5
  36. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -1
  37. data/lib/ransack/configuration.rb +31 -1
  38. data/lib/ransack/constants.rb +3 -5
  39. data/lib/ransack/context.rb +19 -18
  40. data/lib/ransack/helpers/form_builder.rb +8 -14
  41. data/lib/ransack/helpers/form_helper.rb +1 -1
  42. data/lib/ransack/helpers.rb +1 -1
  43. data/lib/ransack/locale/az.yml +1 -1
  44. data/lib/ransack/locale/ca.yml +70 -0
  45. data/lib/ransack/locale/es.yml +22 -22
  46. data/lib/ransack/locale/fa.yml +70 -0
  47. data/lib/ransack/locale/fi.yml +71 -0
  48. data/lib/ransack/locale/sk.yml +70 -0
  49. data/lib/ransack/locale/sv.yml +70 -0
  50. data/lib/ransack/nodes/attribute.rb +1 -1
  51. data/lib/ransack/nodes/condition.rb +7 -1
  52. data/lib/ransack/nodes/grouping.rb +1 -1
  53. data/lib/ransack/nodes/sort.rb +3 -3
  54. data/lib/ransack/nodes/value.rb +1 -1
  55. data/lib/ransack/predicate.rb +2 -1
  56. data/lib/ransack/search.rb +4 -1
  57. data/lib/ransack/translate.rb +115 -115
  58. data/lib/ransack/version.rb +1 -1
  59. data/lib/ransack.rb +3 -3
  60. data/ransack.gemspec +8 -23
  61. data/spec/blueprints/articles.rb +1 -1
  62. data/spec/blueprints/comments.rb +1 -1
  63. data/spec/blueprints/notes.rb +1 -1
  64. data/spec/blueprints/tags.rb +1 -1
  65. data/spec/console.rb +5 -5
  66. data/spec/helpers/polyamorous_helper.rb +3 -8
  67. data/spec/helpers/ransack_helper.rb +1 -1
  68. data/spec/{ransack → polyamorous}/join_association_spec.rb +8 -1
  69. data/spec/{ransack → polyamorous}/join_dependency_spec.rb +18 -7
  70. data/spec/{ransack → polyamorous}/join_spec.rb +0 -0
  71. data/spec/ransack/adapters/active_record/base_spec.rb +26 -15
  72. data/spec/ransack/adapters/active_record/context_spec.rb +60 -18
  73. data/spec/ransack/configuration_spec.rb +24 -0
  74. data/spec/ransack/helpers/form_helper_spec.rb +16 -16
  75. data/spec/ransack/nodes/condition_spec.rb +13 -0
  76. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  77. data/spec/ransack/predicate_spec.rb +54 -2
  78. data/spec/ransack/search_spec.rb +238 -36
  79. data/spec/spec_helper.rb +10 -5
  80. data/spec/support/schema.rb +37 -3
  81. metadata +45 -139
  82. data/.travis.yml +0 -37
  83. data/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +0 -2
  84. data/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +0 -2
  85. data/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +0 -32
  86. data/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +0 -112
  87. data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +0 -32
  88. data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +0 -113
data/README.md CHANGED
@@ -1,19 +1,9 @@
1
1
  # ![Ransack](./logo/ransack-h.png "Ransack")
2
2
 
3
- [![Build Status](https://travis-ci.org/activerecord-hackery/ransack.svg)](https://travis-ci.org/activerecord-hackery/ransack)
3
+ [![Build Status](https://github.com/activerecord-hackery/ransack/workflows/test/badge.svg)](https://github.com/activerecord-hackery/ransack/actions)
4
4
  [![Gem Version](https://badge.fury.io/rb/ransack.svg)](http://badge.fury.io/rb/ransack)
5
5
  [![Code Climate](https://codeclimate.com/github/activerecord-hackery/ransack/badges/gpa.svg)](https://codeclimate.com/github/activerecord-hackery/ransack)
6
- [![Backers on Open Collective](https://opencollective.com/ransack/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/ransack/sponsors/badge.svg)](#sponsors)
7
-
8
- Ransack is a rewrite of [MetaSearch](https://github.com/activerecord-hackery/meta_search)
9
- created by [Ernie Miller](http://twitter.com/erniemiller)
10
- and developed/maintained for years by
11
- [Jon Atack](http://twitter.com/jonatack) and
12
- [Ryan Bigg](http://twitter.com/ryanbigg) with the help of a great group of
13
- [contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors). Ransack's logo is designed by [Anıl Kılıç](https://github.com/anilkilic).
14
- While it supports many of the same features as MetaSearch, its underlying
15
- implementation differs greatly from MetaSearch,
16
- and backwards compatibility is not a design goal.
6
+ [![Backers on Open Collective](https://opencollective.com/ransack/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/ransack/sponsors/badge.svg)](#sponsors)
17
7
 
18
8
  Ransack enables the creation of both
19
9
  [simple](http://ransack-demo.herokuapp.com) and
@@ -21,25 +11,11 @@ Ransack enables the creation of both
21
11
  for your Ruby on Rails application
22
12
  ([demo source code here](https://github.com/activerecord-hackery/ransack_demo)).
23
13
  If you're looking for something that simplifies query generation at the model
24
- or controller layer, you're probably not looking for Ransack (or MetaSearch,
25
- for that matter). Try [Squeel](https://github.com/activerecord-hackery/squeel)
26
- instead.
27
-
28
- If you're viewing this at
29
- [github.com/activerecord-hackery/ransack](https://github.com/activerecord-hackery/ransack),
30
- you're reading the documentation for the master branch with the latest features.
31
- [View documentation for the last release (2.0.0).](https://github.com/activerecord-hackery/ransack/tree/v2.0.0)
14
+ or controller layer, you're probably not looking for Ransack.
32
15
 
33
16
  ## Getting started
34
17
 
35
- Ransack is compatible with Rails 5.0, 5.1 and 5.2 on Ruby 2.2 and later.
36
- If you are using Rails <5.0 use the 1.8 line of Ransack.
37
- If you are using Ruby 1.8 or an earlier JRuby and run into compatibility
38
- issues, you can use an earlier version of Ransack, say, up to 1.3.0.
39
-
40
- Ransack works out-of-the-box with Active Record and also features limited
41
- support for Mongoid 4 and 5 (without associations, further details
42
- [below](https://github.com/activerecord-hackery/ransack#mongoid)).
18
+ Ransack is supported for Rails 6.1, 6.0, 5.2 on Ruby 2.6.6 and later.
43
19
 
44
20
  In your Gemfile, for the last officially released gem:
45
21
 
@@ -63,27 +39,14 @@ gem 'ransack', github: 'activerecord-hackery/ransack'
63
39
 
64
40
  ## Usage
65
41
 
66
- Ransack can be used in one of two modes, simple or advanced.
67
-
68
- ### Simple Mode
69
-
70
- This mode works much like MetaSearch, for those of you who are familiar with
71
- it, and requires very little setup effort.
72
-
73
- If you're coming from MetaSearch, things to note:
42
+ Ransack can be used in one of two modes, simple or advanced. For
43
+ searching/filtering not requiring complex boolean logic, Ransack's simple
44
+ mode should meet your needs.
74
45
 
75
- 1. The default param key for search params is now `:q`, instead of `:search`.
76
- This is primarily to shorten query strings, though advanced queries (below)
77
- will still run afoul of URL length limits in most browsers and require a
78
- switch to HTTP POST requests. This key is [configurable](https://github.com/activerecord-hackery/ransack/wiki/Configuration).
46
+ If you're coming from MetaSearch (Ransack's predecessor), refer to the
47
+ [Updating From MetaSearch](#updating-from-metasearch) section
79
48
 
80
- 2. `form_for` is now `search_form_for`, and validates that a Ransack::Search
81
- object is passed to it.
82
-
83
- 3. Common ActiveRecord::Relation methods are no longer delegated by the
84
- search object. Instead, you will get your search results (an
85
- ActiveRecord::Relation in the case of the ActiveRecord adapter) via a call to
86
- `Ransack#result`.
49
+ ### Simple Mode
87
50
 
88
51
  #### In your controller
89
52
 
@@ -100,9 +63,35 @@ this example, with preloading each Person's Articles and pagination):
100
63
  def index
101
64
  @q = Person.ransack(params[:q])
102
65
  @people = @q.result.includes(:articles).page(params[:page])
66
+ end
67
+ ```
103
68
 
104
- # or use `to_a.uniq` to remove duplicates (can also be done in the view):
105
- @people = @q.result.includes(:articles).page(params[:page]).to_a.uniq
69
+ ##### Default search options
70
+
71
+ **Search parameter**
72
+
73
+ Ransack uses a default `:q` param key for search params. This may be changed by
74
+ setting the `search_key` option in a Ransack initializer file (typically
75
+ `config/initializers/ransack.rb`):
76
+
77
+ ```ruby
78
+ Ransack.configure do |c|
79
+ # Change default search parameter key name.
80
+ # Default key name is :q
81
+ c.search_key = :query
82
+ end
83
+ ```
84
+
85
+ **String search**
86
+
87
+ After version 2.4.0 when searching a string query Ransack by default strips all whitespace around the query string.
88
+ This may be disabled by setting the `strip_whitespace` option in a Ransack initializer file:
89
+
90
+ ```ruby
91
+ Ransack.configure do |c|
92
+ # Change whitespace stripping behaviour.
93
+ # Default is true
94
+ c.strip_whitespace = false
106
95
  end
107
96
  ```
108
97
 
@@ -272,6 +261,57 @@ the order indicator arrow by passing `hide_indicator: true` in the sort link:
272
261
  default_order: { last_name: 'asc', first_name: 'desc' }) %>
273
262
  ```
274
263
 
264
+ #### PostgreSQL's sort option
265
+
266
+ The `NULLS FIRST` and `NULLS LAST` options can be used to determine whether nulls appear before or after non-null values in the sort ordering.
267
+
268
+ You may want to configure it like this:
269
+
270
+ ```rb
271
+ Ransack.configure do |c|
272
+ c.postgres_fields_sort_option = :nulls_first # or :nulls_last
273
+ end
274
+ ```
275
+
276
+ To treat nulls as having the lowest or highest value respectively. To force nulls to always be first or last, use
277
+
278
+ ```rb
279
+ Ransack.configure do |c|
280
+ c.postgres_fields_sort_option = :nulls_always_first # or :nulls_always_last
281
+ end
282
+ ```
283
+
284
+ See this feature: https://www.postgresql.org/docs/13/queries-order.html
285
+
286
+ #### Case Insensitive Sorting in PostgreSQL
287
+
288
+ In order to request PostgreSQL to do a case insensitive sort for all string columns of a model at once, Ransack can be extended by using this approach:
289
+
290
+ ```ruby
291
+ module RansackObject
292
+
293
+ def self.included(base)
294
+ base.columns.each do |column|
295
+ if column.type == :string
296
+ base.ransacker column.name.to_sym, type: :string do
297
+ Arel.sql("lower(#{base.table_name}.#{column.name})")
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+ ```
304
+
305
+ ```ruby
306
+ class UserWithManyAttributes < ActiveRecord::Base
307
+ include RansackObject
308
+ end
309
+ ```
310
+
311
+ If this approach is taken, it is advisable to [add a functional index](https://www.postgresql.org/docs/13/citext.html).
312
+
313
+ This was originally asked in [a Ransack issue](https://github.com/activerecord-hackery/ransack/issues/1201) and a solution was found on [Stack Overflow](https://stackoverflow.com/a/34677378).
314
+
275
315
  ### Advanced Mode
276
316
 
277
317
  "Advanced" searches (ab)use Rails' nested attributes functionality in order to
@@ -430,6 +470,25 @@ query parameters in your URLs.
430
470
  <% end %>
431
471
  ```
432
472
 
473
+ You can also use `ransack_alias` for sorting.
474
+
475
+ ```ruby
476
+ class Post < ActiveRecord::Base
477
+ belongs_to :author
478
+
479
+ # Abbreviate :author_first_name to :author
480
+ ransack_alias :author, :author_first_name
481
+ end
482
+ ```
483
+
484
+ Now, you can use `:author` instead of `:author_first_name` in a `sort_link`.
485
+
486
+ ```erb
487
+ <%= sort_link(@q, :author) %>
488
+ ```
489
+
490
+ Note that using `:author_first_name_or_author_last_name_cont` would produce an invalid sql query. In those cases, Ransack ignores the sorting clause.
491
+
433
492
  ### Search Matchers
434
493
 
435
494
  List of all possible predicates
@@ -459,13 +518,10 @@ List of all possible predicates
459
518
  | `*_lteq_any` | Less than or equal to any | |
460
519
  | `*_gt_any` | Greater than any | |
461
520
  | `*_gteq_any` | Greater than or equal to any | |
462
- | `*_matches_any` | `*_does_not_match_any` | same as above but with `LIKE` |
463
521
  | `*_lt_all` | Less than all | SQL: `col < value1 AND col < value2` |
464
522
  | `*_lteq_all` | Less than or equal to all | |
465
523
  | `*_gt_all` | Greater than all | |
466
524
  | `*_gteq_all` | Greater than or equal to all | |
467
- | `*_matches_all` | Matches all | same as above but with `LIKE` |
468
- | `*_does_not_match_all` | Does not match all | |
469
525
  | `*_not_eq_all` | none of values in a set | |
470
526
  | `*_start` | Starts with | SQL: `col LIKE 'value%'` |
471
527
  | `*_not_start` | Does not start with | |
@@ -485,6 +541,12 @@ List of all possible predicates
485
541
  | `*_not_cont` | Does not contain |
486
542
  | `*_not_cont_any` | Does not contain any of | |
487
543
  | `*_not_cont_all` | Does not contain all of | |
544
+ | `*_i_cont` | Contains value with case insensitive | uses `ILIKE` |
545
+ | `*_i_cont_any` | Contains any of values with case insensitive | |
546
+ | `*_i_cont_all` | Contains all of values with case insensitive | |
547
+ | `*_not_i_cont` | Does not contain with case insensitive |
548
+ | `*_not_i_cont_any` | Does not contain any of values with case insensitive | |
549
+ | `*_not_i_cont_all` | Does not contain all of values with case insensitive | |
488
550
  | `*_true` | is true | |
489
551
  | `*_false` | is false | |
490
552
 
@@ -689,6 +751,43 @@ Trying it out in `rails console`:
689
751
 
690
752
  That's it! Now you know how to whitelist/blacklist various elements in Ransack.
691
753
 
754
+ ### Handling unknown predicates or attributes
755
+
756
+ By default, Ransack will ignore any unknown predicates or attributes:
757
+
758
+ ```ruby
759
+ Article.ransack(unknown_attr_eq: 'Ernie').result.to_sql
760
+ => SELECT "articles".* FROM "articles"
761
+ ```
762
+
763
+ Ransack may be configured to raise an error if passed an unknown predicate or
764
+ attributes, by setting the `ignore_unknown_conditions` option to `false` in your
765
+ Ransack initializer file at `config/initializers/ransack.rb`:
766
+
767
+ ```ruby
768
+ Ransack.configure do |c|
769
+ # Raise errors if a query contains an unknown predicate or attribute.
770
+ # Default is true (do not raise error on unknown conditions).
771
+ c.ignore_unknown_conditions = false
772
+ end
773
+ ```
774
+
775
+ ```ruby
776
+ Article.ransack(unknown_attr_eq: 'Ernie')
777
+ # ArgumentError (Invalid search term unknown_attr_eq)
778
+ ```
779
+
780
+ As an alternative to setting a global configuration option, the `.ransack!`
781
+ class method also raises an error if passed an unknown condition:
782
+
783
+ ```ruby
784
+ Article.ransack!(unknown_attr_eq: 'Ernie')
785
+ # ArgumentError: Invalid search term unknown_attr_eq
786
+ ```
787
+
788
+ This is equivalent to the `ignore_unknown_conditions` configuration option,
789
+ except it may be applied on a case-by-case basis.
790
+
692
791
  ### Using Scopes/Class Methods
693
792
 
694
793
  Continuing on from the preceding section, searching by scopes requires defining
@@ -717,8 +816,6 @@ class Employee < ActiveRecord::Base
717
816
  %i(activated hired_since)
718
817
  end
719
818
  end
720
-
721
- private_class_method :ransackable_scopes
722
819
  end
723
820
 
724
821
  Employee.ransack({ activated: true, hired_since: '2013-01-01' })
@@ -753,7 +850,7 @@ To turn this off on a per-scope basis Ransack adds the following method to
753
850
  Add the scope you wish to bypass this behavior to ransackable_scopes_skip_sanitize_args:
754
851
 
755
852
  ```ruby
756
- def ransackable_scopes_skip_sanitize_args
853
+ def self.ransackable_scopes_skip_sanitize_args
757
854
  [:scope_to_skip_sanitize_args]
758
855
  end
759
856
  ```
@@ -887,6 +984,28 @@ en:
887
984
  title: Old Ransack Namespaced Title
888
985
  ```
889
986
 
987
+ ### Updating From MetaSearch
988
+
989
+ Ransack works much like MetaSearch, for those of you who are familiar with
990
+ it, and requires very little setup effort.
991
+
992
+ If you're coming from MetaSearch, things to note:
993
+
994
+ 1. The default param key for search params is now `:q`, instead of `:search`.
995
+ This is primarily to shorten query strings, though advanced queries (below)
996
+ will still run afoul of URL length limits in most browsers and require a
997
+ switch to HTTP POST requests. This key is
998
+ [configurable](default-search-parameter) via setting the `search_key` option
999
+ in your Ransack intitializer file.
1000
+
1001
+ 2. `form_for` is now `search_form_for`, and validates that a Ransack::Search
1002
+ object is passed to it.
1003
+
1004
+ 3. Common ActiveRecord::Relation methods are no longer delegated by the
1005
+ search object. Instead, you will get your search results (an
1006
+ ActiveRecord::Relation in the case of the ActiveRecord adapter) via a call to
1007
+ `Ransack#result`.
1008
+
890
1009
  ## Mongoid
891
1010
 
892
1011
  Mongoid support has been moved to its own gem at [ransack-mongoid](https://github.com/activerecord-hackery/ransack-mongoid).
@@ -924,6 +1043,7 @@ In other words: `Major.Minor.Patch`.
924
1043
 
925
1044
  To support the project:
926
1045
 
1046
+ * Consider supporting via [Open Collective](https://opencollective.com/ransack/backers/badge.svg)
927
1047
  * Use Ransack in your apps, and let us know if you encounter anything that's
928
1048
  broken or missing. A failing spec to demonstrate the issue is awesome. A pull
929
1049
  request with passing tests is even better!
@@ -939,6 +1059,21 @@ fix bugs!
939
1059
 
940
1060
  This project exists thanks to all the people who contribute. <img src="https://opencollective.com/ransack/contributors.svg?width=890&button=false" />
941
1061
 
1062
+ Ransack is a rewrite of [MetaSearch](https://github.com/activerecord-hackery/meta_search)
1063
+ created by [Ernie Miller](http://twitter.com/erniemiller)
1064
+ and developed/maintained by:
1065
+
1066
+ - [Greg Molnar](https://github.com/gregmolnar)
1067
+ - [Deivid Rodriguez](https://github.com/deivid-rodriguez)
1068
+ - [Sean Carroll](https://github.com/seanfcarroll)
1069
+ - [Jon Atack](http://twitter.com/jonatack)
1070
+ - [Ryan Bigg](http://twitter.com/ryanbigg)
1071
+ - a great group of [contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors).
1072
+ - Ransack's logo is designed by [Anıl Kılıç](https://github.com/anilkilic).
1073
+
1074
+ While it supports many of the same features as MetaSearch, its underlying implementation differs greatly from MetaSearch, and backwards compatibility is not a design goal.
1075
+
1076
+
942
1077
 
943
1078
  ## Backers
944
1079
 
@@ -961,5 +1096,3 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
961
1096
  <a href="https://opencollective.com/ransack/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/7/avatar.svg"></a>
962
1097
  <a href="https://opencollective.com/ransack/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/8/avatar.svg"></a>
963
1098
  <a href="https://opencollective.com/ransack/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ransack/sponsor/9/avatar.svg"></a>
964
-
965
-
@@ -0,0 +1,78 @@
1
+ # test-ransack-scope-and-column-same-name.rb
2
+
3
+ # This is a stand-alone test case.
4
+
5
+ # Run it in your console with: `ruby test-ransack-scope-and-column-same-name.rb`
6
+
7
+ # If you change the gem dependencies, run it with:
8
+ # `rm gemfile* && ruby test-ransack-scope-and-column-same-name.rb`
9
+
10
+ unless File.exist?('Gemfile')
11
+ File.write('Gemfile', <<-GEMFILE)
12
+ source 'https://rubygems.org'
13
+
14
+ # Rails master
15
+ gem 'rails', github: 'rails/rails', branch: '6-1-stable'
16
+
17
+ # Rails last release
18
+ # gem 'rails'
19
+
20
+ gem 'sqlite3'
21
+ gem 'ransack', github: 'activerecord-hackery/ransack'
22
+ GEMFILE
23
+
24
+ system 'bundle install'
25
+ end
26
+
27
+ require 'bundler'
28
+ Bundler.setup(:default)
29
+
30
+ require 'active_record'
31
+ require 'minitest/autorun'
32
+ require 'logger'
33
+ require 'ransack'
34
+
35
+ # This connection will do for database-independent bug reports.
36
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
37
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
38
+
39
+ # Display versions.
40
+ message = "Running test case with Ruby #{RUBY_VERSION}, Active Record #{
41
+ ::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION} and #{
42
+ ::ActiveRecord::Base.connection.adapter_name}"
43
+ line = '=' * message.length
44
+ puts line, message, line
45
+
46
+ ActiveRecord::Schema.define do
47
+ create_table :users, force: true do |t|
48
+ t.boolean :active, null: false, default: true
49
+ end
50
+ end
51
+
52
+ class User < ActiveRecord::Base
53
+ scope :activated, -> (boolean = true) { where(active: boolean) }
54
+
55
+ private
56
+
57
+ def self.ransackable_scopes(auth_object = nil)
58
+ %i(activated)
59
+ end
60
+ end
61
+
62
+ class BugTest < Minitest::Test
63
+ def test_activated_scope_equals_true
64
+ sql = User.ransack({ activated: true }).result.to_sql
65
+ puts sql
66
+ assert_equal(
67
+ "SELECT \"users\".* FROM \"users\" WHERE \"users\".\"active\" = 1", sql
68
+ )
69
+ end
70
+
71
+ def test_activated_scope_equals_false
72
+ sql = User.ransack({ activated: false }).result.to_sql
73
+ puts sql
74
+ assert_equal(
75
+ "SELECT \"users\".* FROM \"users\"", sql
76
+ )
77
+ end
78
+ end
@@ -0,0 +1,71 @@
1
+ # test-ransacker-arel-present-predicate.rb
2
+
3
+ # Run it in your console with: `ruby test-ransacker-arel-present-predicate.rb`
4
+
5
+ # If you change the gem dependencies, run it with:
6
+ # `rm gemfile* && ruby test-ransacker-arel-present-predicate.rb`
7
+
8
+ unless File.exist?('Gemfile')
9
+ File.write('Gemfile', <<-GEMFILE)
10
+ source 'https://rubygems.org'
11
+
12
+ # Rails master
13
+ gem 'rails', github: 'rails/rails', branch: '6-1-stable'
14
+
15
+ # Rails last release
16
+ # gem 'rails'
17
+
18
+ gem 'sqlite3'
19
+ gem 'ransack', github: 'activerecord-hackery/ransack'
20
+ GEMFILE
21
+
22
+ system 'bundle install'
23
+ end
24
+
25
+ require 'bundler'
26
+ Bundler.setup(:default)
27
+
28
+ require 'active_record'
29
+ require 'minitest/autorun'
30
+ require 'logger'
31
+ require 'ransack'
32
+
33
+ # This connection will do for database-independent bug reports.
34
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
35
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
36
+
37
+ # Display versions.
38
+ message = "Running test case with Ruby #{RUBY_VERSION}, Active Record #{
39
+ ::ActiveRecord::VERSION::STRING}, Arel #{Arel::VERSION} and #{
40
+ ::ActiveRecord::Base.connection.adapter_name}"
41
+ line = '=' * message.length
42
+ puts line, message, line
43
+
44
+ ActiveRecord::Schema.define do
45
+ create_table :projects, force: true do |t|
46
+ t.string :name
47
+ t.string :number
48
+ end
49
+ end
50
+
51
+ class Project < ActiveRecord::Base
52
+ ransacker :name do
53
+ Arel.sql('projects.name')
54
+ end
55
+
56
+ ransacker :number do |parent|
57
+ parent.table[:number]
58
+ end
59
+ end
60
+
61
+ class BugTest < Minitest::Test
62
+ def test_ransackers
63
+ sql = Project.ransack({ number_present: 1 }).result.to_sql
64
+ puts sql
65
+ assert_equal "SELECT \"projects\".* FROM \"projects\" WHERE (\"projects\".\"number\" IS NOT NULL AND \"projects\".\"number\" != '')", sql
66
+
67
+ sql = Project.ransack({ name_present: 1 }).result.to_sql
68
+ puts sql
69
+ assert_equal "SELECT \"projects\".* FROM \"projects\" WHERE (projects.name IS NOT NULL AND projects.name != '')", sql
70
+ end
71
+ end
Binary file
@@ -0,0 +1,17 @@
1
+ ## Release Process
2
+
3
+ *For maintainers of Ransack.*
4
+
5
+ To release a new version of Ransack and publish it to RubyGems, take the following steps:
6
+
7
+ - Create a new release, marked `Prerelease`.
8
+ - Update the versions file to the new release, commit and push to `master`.
9
+ - Update the [`version.rb`](../lib/ransack/version.rb) file to the new release, commit and push to `master`.
10
+ - From the terminal, run the following commands:
11
+
12
+ ```bash
13
+ rake build
14
+ rake release
15
+ ```
16
+
17
+ ![Create a Release](img/create_release.png)
@@ -1,5 +1,3 @@
1
- # active_record_5.2.1_ruby_2/join_association.rb
2
-
3
1
  module Polyamorous
4
2
  module JoinAssociationExtensions
5
3
  include SwappingReflectionClass
@@ -19,13 +17,8 @@ module Polyamorous
19
17
  end
20
18
  end
21
19
 
22
- def build_constraint(klass, table, key, foreign_table, foreign_key)
23
- if reflection.polymorphic?
24
- super(klass, table, key, foreign_table, foreign_key)
25
- .and(foreign_table[reflection.foreign_type].eq(reflection.klass.name))
26
- else
27
- super(klass, table, key, foreign_table, foreign_key)
28
- end
20
+ def ==(other)
21
+ base_klass == other.base_klass
29
22
  end
30
23
  end
31
24
  end
@@ -1,9 +1,6 @@
1
- # active_record_5.2.1_ruby_2/join_dependency.rb
2
-
3
1
  module Polyamorous
4
2
  module JoinDependencyExtensions
5
3
  # Replaces ActiveRecord::Associations::JoinDependency#build
6
- #
7
4
  def build(associations, base_klass)
8
5
  associations.map do |name, right|
9
6
  if name.is_a? Join
@@ -30,6 +27,31 @@ module Polyamorous
30
27
  end
31
28
  end
32
29
 
30
+ def join_constraints(joins_to_add, join_type, alias_tracker)
31
+ @alias_tracker = alias_tracker
32
+
33
+ construct_tables!(join_root)
34
+ joins = make_join_constraints(join_root, join_type)
35
+
36
+ joins.concat joins_to_add.flat_map { |oj|
37
+ construct_tables!(oj.join_root)
38
+ if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
39
+ walk join_root, oj.join_root
40
+ else
41
+ make_join_constraints(oj.join_root, join_type)
42
+ end
43
+ }
44
+ end
45
+
46
+ private
47
+ def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
48
+ foreign_table = parent.table
49
+ foreign_klass = parent.base_klass
50
+ join_type = child.join_type || join_type if join_type == Arel::Nodes::InnerJoin
51
+ joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
52
+ joins.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
53
+ end
54
+
33
55
  module ClassMethods
34
56
  # Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
35
57
  #
@@ -0,0 +1,11 @@
1
+ module Polyamorous
2
+ module ReflectionExtensions
3
+ def join_scope(table, foreign_table, foreign_klass)
4
+ if respond_to?(:polymorphic?) && polymorphic?
5
+ super.where!(foreign_table[foreign_type].eq(klass.name))
6
+ else
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1 @@
1
+ require 'polyamorous/activerecord_5.2_ruby_2/join_association'
@@ -0,0 +1,80 @@
1
+ # active_record_6.0_ruby_2/join_dependency.rb
2
+ module Polyamorous
3
+ module JoinDependencyExtensions
4
+ # Replaces ActiveRecord::Associations::JoinDependency#build
5
+ def build(associations, base_klass)
6
+ associations.map do |name, right|
7
+ if name.is_a? Join
8
+ reflection = find_reflection base_klass, name.name
9
+ reflection.check_validity!
10
+ reflection.check_eager_loadable!
11
+
12
+ klass = if reflection.polymorphic?
13
+ name.klass || base_klass
14
+ else
15
+ reflection.klass
16
+ end
17
+ JoinAssociation.new(reflection, build(right, klass), name.klass, name.type)
18
+ else
19
+ reflection = find_reflection base_klass, name
20
+ reflection.check_validity!
21
+ reflection.check_eager_loadable!
22
+
23
+ if reflection.polymorphic?
24
+ raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
25
+ end
26
+ JoinAssociation.new(reflection, build(right, reflection.klass))
27
+ end
28
+ end
29
+ end
30
+
31
+ def join_constraints(joins_to_add, alias_tracker)
32
+ @alias_tracker = alias_tracker
33
+
34
+ construct_tables!(join_root)
35
+ joins = make_join_constraints(join_root, join_type)
36
+
37
+ joins.concat joins_to_add.flat_map { |oj|
38
+ construct_tables!(oj.join_root)
39
+ if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
40
+ walk join_root, oj.join_root, oj.join_type
41
+ else
42
+ make_join_constraints(oj.join_root, oj.join_type)
43
+ end
44
+ }
45
+ end
46
+
47
+ private
48
+ def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
49
+ foreign_table = parent.table
50
+ foreign_klass = parent.base_klass
51
+ join_type = child.join_type || join_type if join_type == Arel::Nodes::InnerJoin
52
+ joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
53
+ joins.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
54
+ end
55
+
56
+ module ClassMethods
57
+ # Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
58
+ #
59
+ def walk_tree(associations, hash)
60
+ case associations
61
+ when TreeNode
62
+ associations.add_to_tree(hash)
63
+ when Hash
64
+ associations.each do |k, v|
65
+ cache =
66
+ if TreeNode === k
67
+ k.add_to_tree(hash)
68
+ else
69
+ hash[k] ||= {}
70
+ end
71
+ walk_tree(v, cache)
72
+ end
73
+ else
74
+ super(associations, hash)
75
+ end
76
+ end
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1 @@
1
+ require 'polyamorous/activerecord_5.2_ruby_2/reflection'