ransack 1.8.4 → 3.2.1

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 (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
@@ -26,7 +26,7 @@ module Ransack
26
26
  case type
27
27
  when :date
28
28
  cast_to_date(value)
29
- when :datetime, :timestamp, :time
29
+ when :datetime, :timestamp, :time, :timestamptz
30
30
  cast_to_time(value)
31
31
  when :boolean
32
32
  cast_to_boolean(value)
@@ -50,7 +50,7 @@ module Ransack
50
50
  y, m, d = *[val].flatten
51
51
  m ||= 1
52
52
  d ||= 1
53
- Date.new(y,m,d) rescue nil
53
+ Date.new(y, m, d) rescue nil
54
54
  end
55
55
  end
56
56
 
@@ -98,7 +98,7 @@ module Ransack
98
98
  val.to_s.to_d
99
99
  end
100
100
  end
101
-
101
+
102
102
  def cast_to_money(val)
103
103
  val.blank? ? nil : val.to_f.to_s
104
104
  end
@@ -1,7 +1,7 @@
1
1
  module Ransack
2
2
  class Predicate
3
3
  attr_reader :name, :arel_predicate, :type, :formatter, :validator,
4
- :compound, :wants_array
4
+ :compound, :wants_array, :case_insensitive
5
5
 
6
6
  class << self
7
7
 
@@ -9,34 +9,26 @@ module Ransack
9
9
  Ransack.predicates.keys
10
10
  end
11
11
 
12
- def names_by_decreasing_length
13
- names.sort { |a, b| b.length <=> a.length }
14
- end
15
-
16
12
  def named(name)
17
13
  Ransack.predicates[name.to_s]
18
14
  end
19
15
 
20
16
  def detect_and_strip_from_string!(str)
21
- if p = detect_from_string(str)
22
- str.sub! /_#{p}$/, ''.freeze
23
- p
24
- end
17
+ detect_from_string str, chomp: true
25
18
  end
26
19
 
27
- def detect_from_string(str)
28
- names_by_decreasing_length.detect { |p| str.end_with?("_#{p}") }
29
- end
20
+ def detect_from_string(str, chomp: false)
21
+ return unless str
30
22
 
31
- # def name_from_attribute_name(attribute_name)
32
- # names_by_decreasing_length.detect {
33
- # |p| attribute_name.to_s.match(/_#{p}$/)
34
- # }
35
- # end
23
+ Ransack.predicates.sorted_names_with_underscores.each do |predicate, underscored|
24
+ if str.end_with? underscored
25
+ str.chomp! underscored if chomp
26
+ return predicate
27
+ end
28
+ end
36
29
 
37
- # def for_attribute_name(attribute_name)
38
- # self.named(detect_from_string(attribute_name.to_s))
39
- # end
30
+ nil
31
+ end
40
32
 
41
33
  end
42
34
 
@@ -50,6 +42,7 @@ module Ransack
50
42
  @compound = opts[:compound]
51
43
  @wants_array = opts.fetch(:wants_array,
52
44
  @compound || Constants::IN_NOT_IN.include?(@arel_predicate))
45
+ @case_insensitive = opts[:case_insensitive]
53
46
  end
54
47
 
55
48
  def eql?(other)
@@ -15,9 +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
22
+ params = params.transform_values { |v| v.is_a?(String) && strip_whitespace ? v.strip : v }
21
23
  params.delete_if { |k, v| [*v].all?{ |i| i.blank? && i != false } }
22
24
  else
23
25
  params = {}
@@ -29,6 +31,7 @@ module Ransack
29
31
  )
30
32
  @scope_args = {}
31
33
  @sorts ||= []
34
+ @ignore_unknown_conditions = options[:ignore_unknown_conditions] == false ? false : true
32
35
  build(params.with_indifferent_access)
33
36
  end
34
37
 
@@ -40,11 +43,11 @@ module Ransack
40
43
  collapse_multiparameter_attributes!(params).each do |key, value|
41
44
  if ['s'.freeze, 'sorts'.freeze].freeze.include?(key)
42
45
  send("#{key}=", value)
43
- elsif base.attribute_method?(key)
44
- base.send("#{key}=", value)
45
46
  elsif @context.ransackable_scope?(key, @context.object)
46
47
  add_scope(key, value)
47
- elsif !Ransack.options[:ignore_unknown_conditions]
48
+ elsif base.attribute_method?(key)
49
+ base.send("#{key}=", value)
50
+ elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions
48
51
  raise ArgumentError, "Invalid search term #{key}"
49
52
  end
50
53
  end
@@ -123,7 +126,7 @@ module Ransack
123
126
  private
124
127
 
125
128
  def add_scope(key, args)
126
- sanitized_args = if Ransack.options[:sanitize_scope_args]
129
+ sanitized_args = if Ransack.options[:sanitize_scope_args] && !@context.ransackable_scope_skip_sanitize_args?(key, @context.object)
127
130
  sanitized_scope_args(args)
128
131
  else
129
132
  args
@@ -6,151 +6,151 @@ I18n.load_path += Dir[
6
6
 
7
7
  module Ransack
8
8
  module Translate
9
- def self.word(key, options = {})
10
- I18n.translate(:"ransack.#{key}", :default => key.to_s)
11
- end
12
-
13
- def self.predicate(key, options = {})
14
- I18n.translate(:"ransack.predicates.#{key}", :default => key.to_s)
15
- end
16
-
17
- def self.attribute(key, options = {})
18
- unless context = options.delete(:context)
19
- raise ArgumentError, "A context is required to translate attributes"
9
+ class << self
10
+ def word(key, options = {})
11
+ I18n.translate(:"ransack.#{key}", default: key.to_s)
20
12
  end
21
13
 
22
- original_name = key.to_s
23
- base_class = context.klass
24
- base_ancestors = base_class.ancestors.select {
25
- |x| x.respond_to?(:model_name)
26
- }
27
- predicate = Predicate.detect_from_string(original_name)
28
- attributes_str = original_name.sub(/_#{predicate}$/, ''.freeze)
29
- attribute_names = attributes_str.split(/_and_|_or_/)
30
- combinator = attributes_str.match(/_and_/) ? :and : :or
31
- defaults = base_ancestors.map do |klass|
32
- "ransack.attributes.#{i18n_key(klass)}.#{original_name}".to_sym
14
+ def predicate(key, options = {})
15
+ I18n.translate(:"ransack.predicates.#{key}", default: key.to_s)
33
16
  end
34
17
 
35
- translated_names = attribute_names.map do |name|
36
- attribute_name(context, name, options[:include_associations])
37
- end
18
+ def attribute(key, options = {})
19
+ unless context = options.delete(:context)
20
+ raise ArgumentError, "A context is required to translate attributes"
21
+ end
38
22
 
39
- interpolations = {
40
- :attributes => translated_names.join(" #{Translate.word(combinator)} ")
41
- }
23
+ original_name = key.to_s
24
+ base_class = context.klass
25
+ base_ancestors = base_class.ancestors.select {
26
+ |x| x.respond_to?(:model_name)
27
+ }
28
+ attributes_str = original_name.dup # will be modified by ⬇
29
+ predicate = Predicate.detect_and_strip_from_string!(attributes_str)
30
+ attribute_names = attributes_str.split(/_and_|_or_/)
31
+ combinator = attributes_str =~ /_and_/ ? :and : :or
32
+ defaults = base_ancestors.map do |klass|
33
+ "ransack.attributes.#{i18n_key(klass)}.#{original_name}".to_sym
34
+ end
35
+ defaults << options.delete(:default) if options[:default]
42
36
 
43
- if predicate
44
- defaults << "%{attributes} %{predicate}".freeze
45
- interpolations[:predicate] = Translate.predicate(predicate)
46
- else
47
- defaults << "%{attributes}".freeze
48
- end
37
+ translated_names = attribute_names.map do |name|
38
+ attribute_name(context, name, options[:include_associations])
39
+ end
49
40
 
50
- defaults << options.delete(:default) if options[:default]
51
- options.reverse_merge! :count => 1, :default => defaults
52
- I18n.translate(defaults.shift, options.merge(interpolations))
53
- end
41
+ interpolations = {
42
+ attributes: translated_names.join(" #{Translate.word(combinator)} ")
43
+ }
54
44
 
55
- def self.association(key, options = {})
56
- unless context = options.delete(:context)
57
- raise ArgumentError, "A context is required to translate associations"
45
+ if predicate
46
+ defaults << "%{attributes} %{predicate}".freeze
47
+ interpolations[:predicate] = Translate.predicate(predicate)
48
+ else
49
+ defaults << "%{attributes}".freeze
50
+ end
51
+
52
+ options.reverse_merge! count: 1, default: defaults
53
+ I18n.translate(defaults.shift, **options.merge(interpolations))
58
54
  end
59
55
 
60
- defaults =
61
- if key.blank?
62
- [:"ransack.models.#{i18n_key(context.klass)}",
63
- :"#{context.klass.i18n_scope}.models.#{i18n_key(context.klass)}"]
64
- else
65
- [:"ransack.associations.#{i18n_key(context.klass)}.#{key}"]
56
+ def association(key, options = {})
57
+ unless context = options.delete(:context)
58
+ raise ArgumentError, "A context is required to translate associations"
66
59
  end
67
- defaults << context.traverse(key).model_name.human
68
- options = { :count => 1, :default => defaults }
69
- I18n.translate(defaults.shift, options)
70
- end
71
60
 
72
- private
61
+ defaults =
62
+ if key.blank?
63
+ [:"ransack.models.#{i18n_key(context.klass)}",
64
+ :"#{context.klass.i18n_scope}.models.#{i18n_key(context.klass)}"]
65
+ else
66
+ [:"ransack.associations.#{i18n_key(context.klass)}.#{key}"]
67
+ end
68
+ defaults << context.traverse(key).model_name.human
69
+ options = { :count => 1, :default => defaults }
70
+ I18n.translate(defaults.shift, **options)
71
+ end
73
72
 
74
- def self.attribute_name(context, name, include_associations = nil)
75
- @context, @name = context, name
76
- @assoc_path = context.association_path(name)
77
- @attr_name = @name.sub(/^#{@assoc_path}_/, ''.freeze)
78
- associated_class = @context.traverse(@assoc_path) if @assoc_path.present?
79
- @include_associated = include_associations && associated_class
73
+ private
80
74
 
81
- defaults = default_attribute_name << fallback_args
82
- options = { :count => 1, :default => defaults }
83
- interpolations = build_interpolations(associated_class)
75
+ def attribute_name(context, name, include_associations = nil)
76
+ @context, @name = context, name
77
+ @assoc_path = context.association_path(name)
78
+ @attr_name = @name.sub(/^#{@assoc_path}_/, ''.freeze)
79
+ associated_class = @context.traverse(@assoc_path) if @assoc_path.present?
80
+ @include_associated = include_associations && associated_class
84
81
 
85
- I18n.translate(defaults.shift, options.merge(interpolations))
86
- end
82
+ defaults = default_attribute_name << fallback_args
83
+ options = { count: 1, default: defaults }
84
+ interpolations = build_interpolations(associated_class)
87
85
 
88
- def self.default_attribute_name
89
- ["ransack.attributes.#{i18n_key(@context.klass)}.#{@name}".to_sym]
90
- end
86
+ I18n.translate(defaults.shift, **options.merge(interpolations))
87
+ end
91
88
 
92
- def self.fallback_args
93
- if @include_associated
94
- '%{association_name} %{attr_fallback_name}'.freeze
95
- else
96
- '%{attr_fallback_name}'.freeze
89
+ def default_attribute_name
90
+ ["ransack.attributes.#{i18n_key(@context.klass)}.#{@name}".to_sym]
97
91
  end
98
- end
99
92
 
100
- def self.build_interpolations(associated_class)
101
- {
102
- :attr_fallback_name => attr_fallback_name(associated_class),
103
- :association_name => association_name
104
- }
105
- .reject { |_, value| value.nil? }
106
- end
93
+ def fallback_args
94
+ if @include_associated
95
+ '%{association_name} %{attr_fallback_name}'.freeze
96
+ else
97
+ '%{attr_fallback_name}'.freeze
98
+ end
99
+ end
107
100
 
108
- def self.attr_fallback_name(associated_class)
109
- I18n.t(
110
- :"ransack.attributes.#{fallback_class(associated_class)}.#{@attr_name}",
111
- :default => default_interpolation(associated_class)
101
+ def build_interpolations(associated_class)
102
+ {
103
+ attr_fallback_name: attr_fallback_name(associated_class),
104
+ association_name: association_name
105
+ }.reject { |_, value| value.nil? }
106
+ end
107
+
108
+ def attr_fallback_name(associated_class)
109
+ I18n.t(
110
+ :"ransack.attributes.#{fallback_class(associated_class)}.#{@attr_name}",
111
+ default: default_interpolation(associated_class)
112
112
  )
113
- end
113
+ end
114
114
 
115
- def self.fallback_class(associated_class)
116
- i18n_key(associated_class || @context.klass)
117
- end
115
+ def fallback_class(associated_class)
116
+ i18n_key(associated_class || @context.klass)
117
+ end
118
118
 
119
- def self.association_name
120
- association(@assoc_path, :context => @context) if @include_associated
121
- end
119
+ def association_name
120
+ association(@assoc_path, context: @context) if @include_associated
121
+ end
122
122
 
123
- def self.default_interpolation(associated_class)
124
- [
125
- associated_attribute(associated_class),
126
- ".attributes.#{@attr_name}".to_sym,
127
- @attr_name.humanize
128
- ]
129
- .flatten
130
- end
123
+ def default_interpolation(associated_class)
124
+ [
125
+ associated_attribute(associated_class),
126
+ ".attributes.#{@attr_name}".to_sym,
127
+ @attr_name.humanize
128
+ ].flatten
129
+ end
131
130
 
132
- def self.associated_attribute(associated_class)
133
- if associated_class
134
- translated_attribute(associated_class)
135
- else
136
- translated_ancestor_attributes
131
+ def associated_attribute(associated_class)
132
+ if associated_class
133
+ translated_attribute(associated_class)
134
+ else
135
+ translated_ancestor_attributes
136
+ end
137
137
  end
138
- end
139
138
 
140
- def self.translated_attribute(associated_class)
141
- key = "#{associated_class.i18n_scope}.attributes.#{
139
+ def translated_attribute(associated_class)
140
+ key = "#{associated_class.i18n_scope}.attributes.#{
142
141
  i18n_key(associated_class)}.#{@attr_name}"
143
- ["#{key}.one".to_sym, key.to_sym]
144
- end
142
+ ["#{key}.one".to_sym, key.to_sym]
143
+ end
145
144
 
146
- def self.translated_ancestor_attributes
147
- @context.klass.ancestors
148
- .select { |ancestor| ancestor.respond_to?(:model_name) }
149
- .map { |ancestor| translated_attribute(ancestor) }
150
- end
145
+ def translated_ancestor_attributes
146
+ @context.klass.ancestors
147
+ .select { |ancestor| ancestor.respond_to?(:model_name) }
148
+ .map { |ancestor| translated_attribute(ancestor) }
149
+ end
151
150
 
152
- def self.i18n_key(klass)
153
- raise "not implemented"
151
+ def i18n_key(klass)
152
+ raise "not implemented"
153
+ end
154
154
  end
155
155
  end
156
156
  end
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = '1.8.4'
2
+ VERSION = '3.2.1'
3
3
  end
@@ -31,17 +31,7 @@ module Ransack
31
31
 
32
32
  def visit_or(object)
33
33
  nodes = object.values.map { |o| accept(o) }.compact
34
- return nil unless nodes.size > 0
35
-
36
- if nodes.size > 1
37
- nodes.inject(&:or)
38
- else
39
- nodes.first
40
- end
41
- end
42
-
43
- def visit_Ransack_Nodes_Sort(object)
44
- object.attr.send(object.dir) if object.valid?
34
+ nodes.inject(&:or)
45
35
  end
46
36
 
47
37
  def quoted?(object)
@@ -57,6 +47,5 @@ module Ransack
57
47
  klass.name.gsub(Constants::TWO_COLONS, Constants::UNDERSCORE)
58
48
  }"
59
49
  end
60
-
61
50
  end
62
51
  end
data/lib/ransack.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  require 'active_support/core_ext'
2
2
  require 'ransack/configuration'
3
3
  require 'ransack/adapters'
4
+ require 'polyamorous/polyamorous'
4
5
 
5
6
  Ransack::Adapters.object_mapper.require_constants
6
7
 
7
8
  module Ransack
8
9
  extend Configuration
9
- class UntraversableAssociationError < StandardError; end;
10
+ class UntraversableAssociationError < StandardError; end
10
11
  end
11
12
 
12
13
  Ransack.configure do |config|
@@ -14,16 +15,17 @@ Ransack.configure do |config|
14
15
  config.add_predicate name, :arel_predicate => name
15
16
  end
16
17
  Ransack::Constants::DERIVED_PREDICATES.each do |args|
17
- config.add_predicate *args
18
+ config.add_predicate(*args)
18
19
  end
19
20
  end
20
21
 
21
22
  require 'ransack/search'
22
23
  require 'ransack/ransacker'
23
- require 'ransack/helpers'
24
- require 'action_controller'
25
24
  require 'ransack/translate'
26
25
 
27
26
  Ransack::Adapters.object_mapper.require_adapter
28
27
 
29
- ActionController::Base.helper Ransack::Helpers::FormHelper
28
+ ActiveSupport.on_load(:action_controller) do
29
+ require 'ransack/helpers'
30
+ ActionController::Base.helper Ransack::Helpers::FormHelper
31
+ end
data/ransack.gemspec CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  $:.push File.expand_path("../lib", __FILE__)
3
4
  require "ransack/version"
4
5
 
@@ -6,37 +7,20 @@ Gem::Specification.new do |s|
6
7
  s.name = "ransack"
7
8
  s.version = Ransack::VERSION
8
9
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack"]
10
- s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com"]
10
+ s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Sean Carroll", "David Rodríguez"]
11
+ s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com", "sfcarroll@gmail.com"]
11
12
  s.homepage = "https://github.com/activerecord-hackery/ransack"
12
- s.summary = %q{Object-based searching for Active Record and Mongoid (currently).}
13
+ s.summary = %q{Object-based searching for Active Record.}
13
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.}
14
- s.required_ruby_version = '>= 1.9'
15
+ s.required_ruby_version = '>= 2.7'
15
16
  s.license = 'MIT'
16
17
 
17
- s.rubyforge_project = "ransack"
18
-
19
- s.add_dependency 'actionpack', '>= 3.0'
20
- s.add_dependency 'activerecord', '>= 3.0'
21
- s.add_dependency 'activesupport', '>= 3.0'
18
+ s.add_dependency 'activerecord', '>= 6.1.5'
19
+ s.add_dependency 'activesupport', '>= 6.1.5'
22
20
  s.add_dependency 'i18n'
23
- s.add_dependency 'polyamorous', '~> 1.3'
24
- s.add_development_dependency 'rspec', '~> 3'
25
- s.add_development_dependency 'machinist', '~> 1.0.6'
26
- s.add_development_dependency 'faker', '~> 0.9.5'
27
- s.add_development_dependency 'sqlite3', '~> 1.3.3'
28
- s.add_development_dependency 'pg'
29
- s.add_development_dependency 'mysql2', '0.3.20'
30
- s.add_development_dependency 'pry', '0.10'
31
21
 
32
22
  s.files = `git ls-files`.split("\n")
33
-
34
- s.test_files = `git ls-files -- {test,spec,features}/*`
35
- .split("\n")
36
-
37
- s.executables = `git ls-files -- bin/*`
38
- .split("\n")
39
- .map { |f| File.basename(f) }
40
-
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
41
25
  s.require_paths = ["lib"]
42
26
  end
@@ -2,4 +2,4 @@ Article.blueprint do
2
2
  person
3
3
  title
4
4
  body
5
- end
5
+ end
@@ -2,4 +2,4 @@ Comment.blueprint do
2
2
  article
3
3
  person
4
4
  body
5
- end
5
+ end
@@ -2,4 +2,4 @@ Note.blueprint do
2
2
  note
3
3
  notable_type { "Article" }
4
4
  notable_id
5
- end
5
+ end
@@ -1,3 +1,3 @@
1
1
  Tag.blueprint do
2
2
  name { Sham.tag_name }
3
- end
3
+ end
data/spec/console.rb CHANGED
@@ -14,11 +14,11 @@ Sham.define do
14
14
  title { Faker::Lorem.sentence }
15
15
  body { Faker::Lorem.paragraph }
16
16
  salary { |index| 30000 + (index * 1000) }
17
- tag_name { Faker::Lorem.words(3).join(' ') }
18
- note { Faker::Lorem.words(7).join(' ') }
19
- only_admin { Faker::Lorem.words(3).join(' ') }
20
- only_search { Faker::Lorem.words(3).join(' ') }
21
- only_sort { Faker::Lorem.words(3).join(' ') }
17
+ tag_name { Faker::Lorem.words(number: 3).join(' ') }
18
+ note { Faker::Lorem.words(number: 7).join(' ') }
19
+ only_admin { Faker::Lorem.words(number: 3).join(' ') }
20
+ only_search { Faker::Lorem.words(number: 3).join(' ') }
21
+ only_sort { Faker::Lorem.words(number: 3).join(' ') }
22
22
  notable_id { |id| id }
23
23
  end
24
24
 
@@ -0,0 +1,13 @@
1
+ module PolyamorousHelper
2
+ def new_join_association(reflection, children, klass)
3
+ Polyamorous::JoinAssociation.new reflection, children, klass
4
+ end
5
+
6
+ def new_join_dependency(klass, associations = {})
7
+ Polyamorous::JoinDependency.new klass, klass.arel_table, associations, Polyamorous::InnerJoin
8
+ end
9
+
10
+ def new_join(name, type = Polyamorous::InnerJoin, klass = nil)
11
+ Polyamorous::Join.new name, type, klass
12
+ end
13
+ end
@@ -6,4 +6,4 @@ module RansackHelper
6
6
  def quote_column_name(column)
7
7
  ActiveRecord::Base.connection.quote_column_name(column)
8
8
  end
9
- end
9
+ end
@@ -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
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module Polyamorous
4
+ describe JoinAssociation do
5
+
6
+ let(:join_dependency) { new_join_dependency Note, {} }
7
+ let(:reflection) { Note.reflect_on_association(:notable) }
8
+ let(:parent) { join_dependency.send(:join_root) }
9
+ let(:join_association) {
10
+ new_join_association(reflection, parent.children, Article)
11
+ }
12
+
13
+ subject { new_join_association(reflection, parent.children, Person) }
14
+
15
+ it 'leaves the original reflection intact for thread safety' do
16
+ reflection.instance_variable_set(:@klass, Article)
17
+ join_association
18
+ .swapping_reflection_klass(reflection, Person) do |new_reflection|
19
+ expect(new_reflection.options).not_to equal reflection.options
20
+ expect(new_reflection.options).not_to have_key(:polymorphic)
21
+ expect(new_reflection.klass).to eq(Person)
22
+ expect(reflection.klass).to eq(Article)
23
+ end
24
+ end
25
+
26
+ it 'sets the polymorphic option to true after initializing' do
27
+ expect(join_association.reflection.options[:polymorphic]).to be true
28
+ end
29
+ end
30
+ end