ransack 2.4.2 → 3.2.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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/cronjob.yml +6 -9
  3. data/.github/workflows/deploy.yml +35 -0
  4. data/.github/workflows/rubocop.yml +1 -1
  5. data/.github/workflows/test-deploy.yml +29 -0
  6. data/.github/workflows/test.yml +16 -40
  7. data/.nojekyll +0 -0
  8. data/CHANGELOG.md +137 -11
  9. data/CONTRIBUTING.md +4 -3
  10. data/Gemfile +2 -2
  11. data/README.md +45 -973
  12. data/docs/.gitignore +19 -0
  13. data/docs/.nojekyll +0 -0
  14. data/docs/babel.config.js +3 -0
  15. data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
  16. data/docs/docs/getting-started/_category_.json +4 -0
  17. data/docs/docs/getting-started/advanced-mode.md +46 -0
  18. data/docs/docs/getting-started/configuration.md +47 -0
  19. data/docs/docs/getting-started/search-matches.md +67 -0
  20. data/docs/docs/getting-started/simple-mode.md +284 -0
  21. data/docs/docs/getting-started/sorting.md +79 -0
  22. data/docs/docs/getting-started/using-predicates.md +282 -0
  23. data/docs/docs/going-further/_category_.json +4 -0
  24. data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
  25. data/docs/docs/going-further/associations.md +70 -0
  26. data/docs/docs/going-further/custom-predicates.md +52 -0
  27. data/docs/docs/going-further/documentation.md +43 -0
  28. data/docs/docs/going-further/exporting-to-csv.md +49 -0
  29. data/docs/docs/going-further/external-guides.md +57 -0
  30. data/docs/docs/going-further/form-customisation.md +63 -0
  31. data/docs/docs/going-further/i18n.md +53 -0
  32. data/docs/docs/going-further/merging-searches.md +41 -0
  33. data/docs/docs/going-further/other-notes.md +428 -0
  34. data/docs/docs/going-further/polymorphic-search.md +40 -0
  35. data/docs/docs/going-further/ransackers.md +331 -0
  36. data/docs/docs/going-further/release_process.md +36 -0
  37. data/docs/docs/going-further/saving-queries.md +82 -0
  38. data/docs/docs/going-further/searching-postgres.md +57 -0
  39. data/docs/docs/going-further/wiki-contributors.md +82 -0
  40. data/docs/docs/intro.md +99 -0
  41. data/docs/docusaurus.config.js +107 -0
  42. data/docs/package.json +37 -0
  43. data/docs/sidebars.js +31 -0
  44. data/docs/src/components/HomepageFeatures/index.js +64 -0
  45. data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  46. data/docs/src/css/custom.css +39 -0
  47. data/docs/src/pages/index.module.css +23 -0
  48. data/docs/src/pages/markdown-page.md +7 -0
  49. data/docs/static/.nojekyll +0 -0
  50. data/docs/static/img/docusaurus.png +0 -0
  51. data/docs/static/img/favicon.ico +0 -0
  52. data/docs/static/img/logo.svg +1 -0
  53. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  54. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  55. data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  56. data/docs/static/img/undraw_docusaurus_react.svg +170 -0
  57. data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  58. data/docs/yarn.lock +7671 -0
  59. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -4
  60. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -1
  61. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -1
  62. data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
  63. data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
  64. data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
  65. data/lib/polyamorous.rb +1 -0
  66. data/lib/ransack/adapters/active_record/base.rb +1 -3
  67. data/lib/ransack/adapters/active_record/context.rb +24 -51
  68. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +2 -9
  69. data/lib/ransack/configuration.rb +16 -2
  70. data/lib/ransack/constants.rb +0 -3
  71. data/lib/ransack/helpers/form_helper.rb +11 -3
  72. data/lib/ransack/locale/sv.yml +70 -0
  73. data/lib/ransack/nodes/sort.rb +2 -2
  74. data/lib/ransack/search.rb +4 -3
  75. data/lib/ransack/translate.rb +1 -1
  76. data/lib/ransack/version.rb +1 -1
  77. data/ransack.gemspec +5 -5
  78. data/spec/helpers/polyamorous_helper.rb +2 -8
  79. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  80. data/spec/polyamorous/join_association_spec.rb +1 -6
  81. data/spec/polyamorous/join_dependency_spec.rb +0 -16
  82. data/spec/ransack/adapters/active_record/base_spec.rb +28 -11
  83. data/spec/ransack/configuration_spec.rb +14 -0
  84. data/spec/ransack/helpers/form_helper_spec.rb +57 -2
  85. data/spec/ransack/nodes/condition_spec.rb +13 -0
  86. data/spec/ransack/search_spec.rb +140 -27
  87. data/spec/support/schema.rb +49 -0
  88. metadata +80 -29
  89. data/docs/release_process.md +0 -20
  90. data/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +0 -24
  91. data/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -79
  92. data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -11
  93. data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -1
  94. data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +0 -80
  95. data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -1
  96. /data/docs/{img → docs/going-further/img}/create_release.png +0 -0
  97. /data/{logo → docs/static/logo}/ransack-h.png +0 -0
  98. /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
  99. /data/{logo → docs/static/logo}/ransack-v.png +0 -0
  100. /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
  101. /data/{logo → docs/static/logo}/ransack.png +0 -0
  102. /data/{logo → docs/static/logo}/ransack.svg +0 -0
  103. /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_association.rb +0 -0
  104. /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_dependency.rb +0 -0
  105. /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/reflection.rb +0 -0
@@ -66,9 +66,5 @@ module Polyamorous
66
66
 
67
67
  joins
68
68
  end
69
-
70
- def ==(other)
71
- base_klass == other.base_klass
72
- end
73
69
  end
74
70
  end
@@ -1,4 +1,3 @@
1
- # active_record_6.1_ruby_2/join_dependency.rb
2
1
  module Polyamorous
3
2
  module JoinDependencyExtensions
4
3
  # Replaces ActiveRecord::Associations::JoinDependency#build
@@ -1 +1,11 @@
1
- require 'polyamorous/activerecord_6.0_ruby_2/reflection'
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_6.1_ruby_2/join_association'
@@ -0,0 +1 @@
1
+ require 'polyamorous/activerecord_6.1_ruby_2/join_dependency'
@@ -0,0 +1 @@
1
+ require 'polyamorous/activerecord_6.1_ruby_2/reflection'
@@ -0,0 +1 @@
1
+ require 'polyamorous/polyamorous'
@@ -4,7 +4,6 @@ module Ransack
4
4
  module Base
5
5
 
6
6
  def self.extended(base)
7
- alias :search :ransack unless base.respond_to? :search
8
7
  base.class_eval do
9
8
  class_attribute :_ransackers
10
9
  class_attribute :_ransack_aliases
@@ -14,7 +13,6 @@ module Ransack
14
13
  end
15
14
 
16
15
  def ransack(params = {}, options = {})
17
- ActiveSupport::Deprecation.warn("#search is deprecated and will be removed in 2.3, please use #ransack instead") if __callee__ == :search
18
16
  Search.new(self, params, options)
19
17
  end
20
18
 
@@ -70,7 +68,7 @@ module Ransack
70
68
  end
71
69
 
72
70
  # ransack_scope_skip_sanitize_args, by default, returns an empty array.
73
- # i.e. use the sanitize_scope_args setting to determin if args should be converted.
71
+ # i.e. use the sanitize_scope_args setting to determine if args should be converted.
74
72
  # For overriding with a list of scopes which should be passed the args as-is.
75
73
  #
76
74
  def ransackable_scopes_skip_sanitize_args
@@ -44,9 +44,13 @@ module Ransack
44
44
  else
45
45
  case Ransack.options[:postgres_fields_sort_option]
46
46
  when :nulls_first
47
- scope_or_sort = scope_or_sort.direction == :asc ? "#{scope_or_sort.to_sql} NULLS FIRST" : "#{scope_or_sort.to_sql} NULLS LAST"
47
+ scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") : Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
48
48
  when :nulls_last
49
- scope_or_sort = scope_or_sort.direction == :asc ? "#{scope_or_sort.to_sql} NULLS LAST" : "#{scope_or_sort.to_sql} NULLS FIRST"
49
+ scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") : Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
50
+ when :nulls_always_first
51
+ scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
52
+ when :nulls_always_last
53
+ scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
50
54
  end
51
55
 
52
56
  relation = relation.order(scope_or_sort)
@@ -106,13 +110,7 @@ module Ransack
106
110
  def join_sources
107
111
  base, joins = begin
108
112
  alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, @object.table.name, [])
109
- constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1)
110
- @join_dependency.join_constraints(@object.joins_values, alias_tracker, @object.references_values)
111
- elsif ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
112
- @join_dependency.join_constraints(@object.joins_values, alias_tracker)
113
- else
114
- @join_dependency.join_constraints(@object.joins_values, @join_type, alias_tracker)
115
- end
113
+ constraints = @join_dependency.join_constraints(@object.joins_values, alias_tracker, @object.references_values)
116
114
 
117
115
  [
118
116
  Arel::SelectManager.new(@object.table),
@@ -139,6 +137,7 @@ module Ransack
139
137
  stashed.eql?(association)
140
138
  }
141
139
  @object.joins_values.delete_if { |jd|
140
+ jd.instance_variables.include?(:@join_root) &&
142
141
  jd.instance_variable_get(:@join_root).children.map(&:object_id) == [association.object_id]
143
142
  }
144
143
  end
@@ -279,11 +278,7 @@ module Ransack
279
278
  join_list = join_nodes + convert_join_strings_to_ast(relation.table, string_joins)
280
279
 
281
280
  alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
282
- join_dependency = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
283
- Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin)
284
- else
285
- Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins)
286
- end
281
+ join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin)
287
282
  join_dependency.instance_variable_set(:@alias_tracker, alias_tracker)
288
283
  join_nodes.each do |join|
289
284
  join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
@@ -310,22 +305,13 @@ module Ransack
310
305
  end
311
306
 
312
307
  def build_association(name, parent = @base, klass = nil)
313
- if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
314
- jd = Polyamorous::JoinDependency.new(
315
- parent.base_klass,
316
- parent.table,
317
- Polyamorous::Join.new(name, @join_type, klass),
318
- @join_type
319
- )
320
- found_association = jd.instance_variable_get(:@join_root).children.last
321
- else
322
- jd = Polyamorous::JoinDependency.new(
323
- parent.base_klass,
324
- parent.table,
325
- Polyamorous::Join.new(name, @join_type, klass)
326
- )
327
- found_association = jd.instance_variable_get(:@join_root).children.last
328
- end
308
+ jd = Polyamorous::JoinDependency.new(
309
+ parent.base_klass,
310
+ parent.table,
311
+ Polyamorous::Join.new(name, @join_type, klass),
312
+ @join_type
313
+ )
314
+ found_association = jd.instance_variable_get(:@join_root).children.last
329
315
 
330
316
  @associations_pot[found_association] = parent
331
317
 
@@ -334,11 +320,7 @@ module Ransack
334
320
  @join_dependency.instance_variable_get(:@join_root).children.push found_association
335
321
 
336
322
  # Builds the arel nodes properly for this association
337
- if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1)
338
- @tables_pot[found_association] = @join_dependency.construct_tables_for_association!(jd.instance_variable_get(:@join_root), found_association)
339
- else
340
- @join_dependency.send(:construct_tables!, jd.instance_variable_get(:@join_root))
341
- end
323
+ @tables_pot[found_association] = @join_dependency.construct_tables_for_association!(jd.instance_variable_get(:@join_root), found_association)
342
324
 
343
325
  # Leverage the stashed association functionality in AR
344
326
  @object = @object.joins(jd)
@@ -348,22 +330,13 @@ module Ransack
348
330
  def extract_joins(association)
349
331
  parent = @join_dependency.instance_variable_get(:@join_root)
350
332
  reflection = association.reflection
351
- join_constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1)
352
- association.join_constraints_with_tables(
353
- parent.table,
354
- parent.base_klass,
355
- Arel::Nodes::OuterJoin,
356
- @join_dependency.instance_variable_get(:@alias_tracker),
357
- @tables_pot[association]
358
- )
359
- else
360
- association.join_constraints(
361
- parent.table,
362
- parent.base_klass,
363
- Arel::Nodes::OuterJoin,
364
- @join_dependency.instance_variable_get(:@alias_tracker)
365
- )
366
- end
333
+ join_constraints = association.join_constraints_with_tables(
334
+ parent.table,
335
+ parent.base_klass,
336
+ Arel::Nodes::OuterJoin,
337
+ @join_dependency.instance_variable_get(:@alias_tracker),
338
+ @tables_pot[association]
339
+ )
367
340
  join_constraints.to_a.flatten
368
341
  end
369
342
  end
@@ -47,18 +47,11 @@ module Ransack
47
47
  end
48
48
 
49
49
  def casted_array?(predicate)
50
- (predicate.respond_to?(:value) && predicate.value.is_a?(Array)) || # Rails 6.1
51
- (predicate.respond_to?(:val) && predicate.val.is_a?(Array)) # Rails 5.2, 6.0
50
+ predicate.value.is_a?(Array) && predicate.is_a?(Arel::Nodes::Casted)
52
51
  end
53
52
 
54
53
  def format_values_for(predicate)
55
- value = if predicate.respond_to?(:value)
56
- predicate.value # Rails 6.1
57
- else
58
- predicate.val # Rails 5.2, 6.0
59
- end
60
-
61
- value.map do |val|
54
+ predicate.value.map do |val|
62
55
  val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
63
56
  end
64
57
  end
@@ -34,7 +34,8 @@ module Ransack
34
34
  :down_arrow => '▲'.freeze,
35
35
  :default_arrow => nil,
36
36
  :sanitize_scope_args => true,
37
- :postgres_fields_sort_option => nil
37
+ :postgres_fields_sort_option => nil,
38
+ :strip_whitespace => true
38
39
  }
39
40
 
40
41
  def configure
@@ -148,7 +149,7 @@ module Ransack
148
149
  # User may want to configure it like this:
149
150
  #
150
151
  # Ransack.configure do |c|
151
- # c.postgres_fields_sort_option = :nulls_first # or :nulls_last
152
+ # c.postgres_fields_sort_option = :nulls_first # or e.g. :nulls_always_last
152
153
  # end
153
154
  #
154
155
  # See this feature: https://www.postgresql.org/docs/13/queries-order.html
@@ -170,6 +171,19 @@ module Ransack
170
171
  self.options[:hide_sort_order_indicators] = boolean
171
172
  end
172
173
 
174
+ # By default, Ransack displays strips all whitespace when searching for a string.
175
+ # The default may be globally changed in an initializer file like
176
+ # `config/initializers/ransack.rb` as follows:
177
+ #
178
+ # Ransack.configure do |config|
179
+ # # Enable whitespace stripping for string searches
180
+ # config.strip_whitespace = true
181
+ # end
182
+ #
183
+ def strip_whitespace=(boolean)
184
+ self.options[:strip_whitespace] = boolean
185
+ end
186
+
173
187
  def arel_predicate_with_suffix(arel_predicate, suffix)
174
188
  if arel_predicate === Proc
175
189
  proc { |v| "#{arel_predicate.call(v)}#{suffix}" }
@@ -45,9 +45,6 @@ module Ransack
45
45
  NOT_EQ_ALL = 'not_eq_all'.freeze
46
46
  CONT = 'cont'.freeze
47
47
 
48
- RAILS_6_0 = '6.0.0'.freeze
49
- RAILS_6_1 = '6.1.0'.freeze
50
-
51
48
  RANSACK_SLASH_SEARCHES = 'ransack/searches'.freeze
52
49
  RANSACK_SLASH_SEARCHES_SLASH_SEARCH = 'ransack/searches/search'.freeze
53
50
  end
@@ -130,12 +130,20 @@ module Ransack
130
130
 
131
131
  def url_options
132
132
  @params.merge(
133
- @options.merge(
133
+ @options.except(:class, :data, :host).merge(
134
134
  @search.context.search_key => search_and_sort_params))
135
135
  end
136
136
 
137
137
  def html_options(args)
138
- html_options = extract_options_and_mutate_args!(args)
138
+ if args.empty?
139
+ html_options = @options
140
+ else
141
+ deprecation_message = "Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one."
142
+ caller_location = caller_locations(2, 2).first
143
+ warn "#{deprecation_message} (called at #{caller_location.path}:#{caller_location.lineno})"
144
+ html_options = extract_options_and_mutate_args!(args)
145
+ end
146
+
139
147
  html_options.merge(
140
148
  class: [['sort_link'.freeze, @current_dir], html_options[:class]]
141
149
  .compact.join(' '.freeze)
@@ -145,7 +153,7 @@ module Ransack
145
153
  private
146
154
 
147
155
  def parameters_hash(params)
148
- if ::ActiveRecord::VERSION::MAJOR >= 5 && params.respond_to?(:to_unsafe_h)
156
+ if params.respond_to?(:to_unsafe_h)
149
157
  params.to_unsafe_h
150
158
  else
151
159
  params
@@ -0,0 +1,70 @@
1
+ sv:
2
+ ransack:
3
+ search: "sök"
4
+ predicate: "predikat"
5
+ and: "och"
6
+ or: "eller"
7
+ any: "vilken som"
8
+ all: "alla"
9
+ combinator: "kombinator"
10
+ attribute: "attribut"
11
+ value: "värde"
12
+ condition: "villkor"
13
+ sort: "sortera"
14
+ asc: "stigande"
15
+ desc: "fallande"
16
+ predicates:
17
+ eq: "lika med"
18
+ eq_any: "lika med vilket som"
19
+ eq_all: "lika med alla"
20
+ not_eq: "inte lika med"
21
+ not_eq_any: "inte lika med någon"
22
+ not_eq_all: "inte lika med alla"
23
+ matches: "matchar"
24
+ matches_any: "matchar någon"
25
+ matches_all: "matchar alla"
26
+ does_not_match: "matchar inte"
27
+ does_not_match_any: "matchar inte någon"
28
+ does_not_match_all: "matchar inte alla"
29
+ lt: "mindre än"
30
+ lt_any: "mindre än någon"
31
+ lt_all: "mindre än alla"
32
+ lteq: "mindre än eller lika med"
33
+ lteq_any: "mindre än eller lika med någon"
34
+ lteq_all: "mindre än eller lika med alla"
35
+ gt: "större än"
36
+ gt_any: "större än någon"
37
+ gt_all: "större än alla"
38
+ gteq: "större än eller lika med"
39
+ gteq_any: "större än eller lika med någon"
40
+ gteq_all: "större än eller lika med alla"
41
+ in: "i"
42
+ in_any: "i någon"
43
+ in_all: "i alla"
44
+ not_in: "inte i"
45
+ not_in_any: "inte i någon"
46
+ not_in_all: "inte i alla"
47
+ cont: "innehåller"
48
+ cont_any: "innehåller någon"
49
+ cont_all: "innehåller alla"
50
+ not_cont: "innehåller inte"
51
+ not_cont_any: "innehåller inte någon"
52
+ not_cont_all: "innehåller inte alla"
53
+ start: "börjar med"
54
+ start_any: "börjar med någon"
55
+ start_all: "börjar med alla"
56
+ not_start: "börjar inte med"
57
+ not_start_any: "börjar inte med någon"
58
+ not_start_all: "börjar inte med alla"
59
+ end: "slutar med"
60
+ end_any: "slutar med någon"
61
+ end_all: "slutar med alla"
62
+ not_end: "slutar inte med"
63
+ not_end_any: "slutar inte med någon"
64
+ not_end_all: "slutar inte med alla"
65
+ 'true': "är sant"
66
+ 'false': "är falskt"
67
+ present: "existerar"
68
+ blank: "är tom"
69
+ 'null': "är null"
70
+ not_null: "är inte null"
@@ -31,8 +31,8 @@ module Ransack
31
31
  end
32
32
 
33
33
  def name=(name)
34
- @name = name
35
- context.bind(self, name)
34
+ @name = context.ransackable_alias(name) || name
35
+ context.bind(self, @name)
36
36
  end
37
37
 
38
38
  def dir=(dir)
@@ -15,10 +15,11 @@ module Ransack
15
15
  :translate, :to => :base
16
16
 
17
17
  def initialize(object, params = {}, options = {})
18
+ strip_whitespace = options.fetch(:strip_whitespace, Ransack.options[:strip_whitespace])
18
19
  params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h)
19
20
  if params.is_a? Hash
20
21
  params = params.dup
21
- params = params.transform_values { |v| v.is_a?(String) ? v.strip : v }
22
+ params = params.transform_values { |v| v.is_a?(String) && strip_whitespace ? v.strip : v }
22
23
  params.delete_if { |k, v| [*v].all?{ |i| i.blank? && i != false } }
23
24
  else
24
25
  params = {}
@@ -42,10 +43,10 @@ module Ransack
42
43
  collapse_multiparameter_attributes!(params).each do |key, value|
43
44
  if ['s'.freeze, 'sorts'.freeze].freeze.include?(key)
44
45
  send("#{key}=", value)
45
- elsif base.attribute_method?(key)
46
- base.send("#{key}=", value)
47
46
  elsif @context.ransackable_scope?(key, @context.object)
48
47
  add_scope(key, value)
48
+ elsif base.attribute_method?(key)
49
+ base.send("#{key}=", value)
49
50
  elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions
50
51
  raise ArgumentError, "Invalid search term #{key}"
51
52
  end
@@ -32,6 +32,7 @@ module Ransack
32
32
  defaults = base_ancestors.map do |klass|
33
33
  "ransack.attributes.#{i18n_key(klass)}.#{original_name}".to_sym
34
34
  end
35
+ defaults << options.delete(:default) if options[:default]
35
36
 
36
37
  translated_names = attribute_names.map do |name|
37
38
  attribute_name(context, name, options[:include_associations])
@@ -48,7 +49,6 @@ module Ransack
48
49
  defaults << "%{attributes}".freeze
49
50
  end
50
51
 
51
- defaults << options.delete(:default) if options[:default]
52
52
  options.reverse_merge! count: 1, default: defaults
53
53
  I18n.translate(defaults.shift, **options.merge(interpolations))
54
54
  end
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = '2.4.2'
2
+ VERSION = '3.2.0'
3
3
  end
data/ransack.gemspec CHANGED
@@ -7,16 +7,16 @@ Gem::Specification.new do |s|
7
7
  s.name = "ransack"
8
8
  s.version = Ransack::VERSION
9
9
  s.platform = Gem::Platform::RUBY
10
- s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Sean Carroll"]
10
+ s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Sean Carroll", "David Rodríguez"]
11
11
  s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com", "sfcarroll@gmail.com"]
12
12
  s.homepage = "https://github.com/activerecord-hackery/ransack"
13
- s.summary = %q{Object-based searching for Active Record and Mongoid (currently).}
13
+ s.summary = %q{Object-based searching for Active Record.}
14
14
  s.description = %q{Ransack is the successor to the MetaSearch gem. It improves and expands upon MetaSearch's functionality, but does not have a 100%-compatible API.}
15
- s.required_ruby_version = '>= 2.6'
15
+ s.required_ruby_version = '>= 2.7'
16
16
  s.license = 'MIT'
17
17
 
18
- s.add_dependency 'activerecord', '>= 5.2.4'
19
- s.add_dependency 'activesupport', '>= 5.2.4'
18
+ s.add_dependency 'activerecord', '>= 6.1.5'
19
+ s.add_dependency 'activesupport', '>= 6.1.5'
20
20
  s.add_dependency 'i18n'
21
21
 
22
22
  s.files = `git ls-files`.split("\n")
@@ -3,14 +3,8 @@ module PolyamorousHelper
3
3
  Polyamorous::JoinAssociation.new reflection, children, klass
4
4
  end
5
5
 
6
- if ActiveRecord.version >= ::Gem::Version.new("6.0.0.rc1")
7
- def new_join_dependency(klass, associations = {})
8
- Polyamorous::JoinDependency.new klass, klass.arel_table, associations, Polyamorous::InnerJoin
9
- end
10
- else
11
- def new_join_dependency(klass, associations = {})
12
- Polyamorous::JoinDependency.new klass, klass.arel_table, associations
13
- end
6
+ def new_join_dependency(klass, associations = {})
7
+ Polyamorous::JoinDependency.new klass, klass.arel_table, associations, Polyamorous::InnerJoin
14
8
  end
15
9
 
16
10
  def new_join(name, type = Polyamorous::InnerJoin, klass = nil)
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ module Polyamorous
4
+ describe "ActiveRecord Compatibility" do
5
+ it 'works with self joins and includes' do
6
+ trade_account = Account.create!
7
+ Account.create!(trade_account: trade_account)
8
+
9
+ accounts = Account.joins(:trade_account).includes(:trade_account, :agent_account)
10
+ account = accounts.first
11
+
12
+ expect(account.agent_account).to be_nil
13
+ end
14
+ end
15
+ end
@@ -12,12 +12,7 @@ module Polyamorous
12
12
 
13
13
  subject { new_join_association(reflection, parent.children, Person) }
14
14
 
15
- it 'respects polymorphism on equality test' do
16
- expect(subject).to eq new_join_association(reflection, parent.children, Person)
17
- expect(subject).not_to eq new_join_association(reflection, parent.children, Article)
18
- end
19
-
20
- it 'leaves the orginal reflection intact for thread safety' do
15
+ it 'leaves the original reflection intact for thread safety' do
21
16
  reflection.instance_variable_set(:@klass, Article)
22
17
  join_association
23
18
  .swapping_reflection_klass(reflection, Person) do |new_reflection|
@@ -77,21 +77,5 @@ module Polyamorous
77
77
  specify { expect(subject.send(:join_root).drop(1)[1].table_name)
78
78
  .to eq 'comments' }
79
79
  end
80
-
81
- context '#left_outer_join in Rails 5 overrides join type specified',
82
- if: ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MAJOR < 6 && ActiveRecord::VERSION::MINOR < 2 do
83
-
84
- let(:join_type_class) do
85
- new_join_dependency(
86
- Person,
87
- new_join(:articles)
88
- ).join_constraints(
89
- [],
90
- Arel::Nodes::OuterJoin
91
- ).first.joins.map(&:class)
92
- end
93
-
94
- specify { expect(join_type_class).to eq [Arel::Nodes::OuterJoin] }
95
- end
96
80
  end
97
81
  end
@@ -8,7 +8,6 @@ module Ransack
8
8
  subject { ::ActiveRecord::Base }
9
9
 
10
10
  it { should respond_to :ransack }
11
- it { should respond_to :search }
12
11
 
13
12
  describe '#search' do
14
13
  subject { Person.ransack }
@@ -44,12 +43,12 @@ module Ransack
44
43
 
45
44
  it 'applies stringy boolean scopes with true value in an array' do
46
45
  s = Person.ransack('of_age' => ['true'])
47
- expect(s.result.to_sql).to (include 'age >= 18')
46
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{(age >= '18')} : 'age >= 18')
48
47
  end
49
48
 
50
49
  it 'applies stringy boolean scopes with false value in an array' do
51
50
  s = Person.ransack('of_age' => ['false'])
52
- expect(s.result.to_sql).to (include 'age < 18')
51
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age < '18'} : 'age < 18')
53
52
  end
54
53
 
55
54
  it 'ignores unlisted scopes' do
@@ -69,15 +68,25 @@ module Ransack
69
68
 
70
69
  it 'passes values to scopes' do
71
70
  s = Person.ransack('over_age' => 18)
72
- expect(s.result.to_sql).to (include 'age > 18')
71
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
73
72
  end
74
73
 
75
74
  it 'chains scopes' do
76
75
  s = Person.ransack('over_age' => 18, 'active' => true)
77
- expect(s.result.to_sql).to (include 'age > 18')
76
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
78
77
  expect(s.result.to_sql).to (include 'active = 1')
79
78
  end
80
79
 
80
+ it 'applies scopes that define string SQL joins' do
81
+ allow(Article)
82
+ .to receive(:ransackable_scopes)
83
+ .and_return([:latest_comment_cont])
84
+
85
+ # Including a negative condition to test removing the scope
86
+ s = Search.new(Article, notes_note_not_eq: 'Test', latest_comment_cont: 'Test')
87
+ expect(s.result.to_sql).to include 'latest_comment'
88
+ end
89
+
81
90
  context "with sanitize_custom_scope_booleans set to false" do
82
91
  before(:all) do
83
92
  Ransack.configure { |c| c.sanitize_custom_scope_booleans = false }
@@ -89,12 +98,12 @@ module Ransack
89
98
 
90
99
  it 'passes true values to scopes' do
91
100
  s = Person.ransack('over_age' => 1)
92
- expect(s.result.to_sql).to (include 'age > 1')
101
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
93
102
  end
94
103
 
95
104
  it 'passes false values to scopes' do
96
105
  s = Person.ransack('over_age' => 0)
97
- expect(s.result.to_sql).to (include 'age > 0')
106
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
98
107
  end
99
108
  end
100
109
 
@@ -107,12 +116,12 @@ module Ransack
107
116
 
108
117
  it 'passes true values to scopes' do
109
118
  s = Person.ransack('over_age' => 1)
110
- expect(s.result.to_sql).to (include 'age > 1')
119
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
111
120
  end
112
121
 
113
122
  it 'passes false values to scopes' do
114
123
  s = Person.ransack('over_age' => 0)
115
- expect(s.result.to_sql).to (include 'age > 0')
124
+ expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
116
125
  end
117
126
  end
118
127
 
@@ -123,7 +132,7 @@ module Ransack
123
132
  end
124
133
 
125
134
  it 'raises exception if ransack! called with unknown condition' do
126
- expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error
135
+ expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error(ArgumentError)
127
136
  end
128
137
 
129
138
  it 'does not modify the parameters' do
@@ -314,7 +323,11 @@ module Ransack
314
323
  end
315
324
 
316
325
  it 'should function correctly with a multi-parameter attribute' do
317
- ::ActiveRecord::Base.default_timezone = :utc
326
+ if ::ActiveRecord::VERSION::MAJOR >= 7
327
+ ::ActiveRecord.default_timezone = :utc
328
+ else
329
+ ::ActiveRecord::Base.default_timezone = :utc
330
+ end
318
331
  Time.zone = 'UTC'
319
332
 
320
333
  date = Date.current
@@ -690,6 +703,10 @@ module Ransack
690
703
  it { should eq [] }
691
704
  end
692
705
 
706
+ private
707
+ def rails7_and_mysql
708
+ ::ActiveRecord::VERSION::MAJOR >= 7 && ENV['DB'] == 'mysql'
709
+ end
693
710
  end
694
711
  end
695
712
  end
@@ -45,6 +45,20 @@ module Ransack
45
45
  Ransack.options = default
46
46
  end
47
47
 
48
+ it 'should have default value for strip_whitespace' do
49
+ expect(Ransack.options[:strip_whitespace]).to eq true
50
+ end
51
+
52
+ it 'changes default search key parameter' do
53
+ default = Ransack.options.clone
54
+
55
+ Ransack.configure { |c| c.strip_whitespace = false }
56
+
57
+ expect(Ransack.options[:strip_whitespace]).to eq false
58
+
59
+ Ransack.options = default
60
+ end
61
+
48
62
  it 'should have default values for arrows' do
49
63
  expect(Ransack.options[:up_arrow]).to eq '&#9660;'
50
64
  expect(Ransack.options[:down_arrow]).to eq '&#9650;'