ransack 1.7.0 → 2.4.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 (127) 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/test.yml +120 -0
  5. data/.gitignore +3 -0
  6. data/CHANGELOG.md +463 -27
  7. data/CONTRIBUTING.md +52 -22
  8. data/Gemfile +24 -24
  9. data/README.md +453 -126
  10. data/Rakefile +6 -25
  11. data/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +24 -0
  12. data/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +79 -0
  13. data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +11 -0
  14. data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +1 -0
  15. data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +80 -0
  16. data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +1 -0
  17. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +74 -0
  18. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +93 -0
  19. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +1 -0
  20. data/lib/polyamorous/join.rb +70 -0
  21. data/lib/polyamorous/polyamorous.rb +24 -0
  22. data/lib/polyamorous/swapping_reflection_class.rb +11 -0
  23. data/lib/polyamorous/tree_node.rb +7 -0
  24. data/lib/ransack/adapters/active_record/base.rb +27 -2
  25. data/lib/ransack/adapters/active_record/context.rb +213 -139
  26. data/lib/ransack/adapters/active_record/ransack/constants.rb +70 -55
  27. data/lib/ransack/adapters/active_record/ransack/context.rb +10 -18
  28. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +42 -32
  29. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
  30. data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
  31. data/lib/ransack/adapters/active_record.rb +11 -10
  32. data/lib/ransack/adapters.rb +45 -23
  33. data/lib/ransack/configuration.rb +107 -4
  34. data/lib/ransack/constants.rb +13 -26
  35. data/lib/ransack/context.rb +45 -33
  36. data/lib/ransack/helpers/form_builder.rb +21 -12
  37. data/lib/ransack/helpers/form_helper.rb +75 -70
  38. data/lib/ransack/locale/ar.yml +70 -0
  39. data/lib/ransack/locale/az.yml +70 -0
  40. data/lib/ransack/locale/bg.yml +70 -0
  41. data/lib/ransack/locale/ca.yml +70 -0
  42. data/lib/ransack/locale/da.yml +70 -0
  43. data/lib/ransack/locale/el.yml +70 -0
  44. data/lib/ransack/locale/es.yml +22 -22
  45. data/lib/ransack/locale/fa.yml +70 -0
  46. data/lib/ransack/locale/fi.yml +71 -0
  47. data/lib/ransack/locale/id.yml +70 -0
  48. data/lib/ransack/locale/it.yml +70 -0
  49. data/lib/ransack/locale/ja.yml +70 -0
  50. data/lib/ransack/locale/nl.yml +4 -4
  51. data/lib/ransack/locale/pt-BR.yml +70 -0
  52. data/lib/ransack/locale/ru.yml +70 -0
  53. data/lib/ransack/locale/sk.yml +70 -0
  54. data/lib/ransack/locale/tr.yml +70 -0
  55. data/lib/ransack/locale/{zh.yml → zh-CN.yml} +13 -13
  56. data/lib/ransack/locale/zh-TW.yml +70 -0
  57. data/lib/ransack/nodes/attribute.rb +5 -2
  58. data/lib/ransack/nodes/bindable.rb +18 -6
  59. data/lib/ransack/nodes/condition.rb +85 -28
  60. data/lib/ransack/nodes/grouping.rb +17 -11
  61. data/lib/ransack/nodes/sort.rb +9 -5
  62. data/lib/ransack/nodes/value.rb +74 -68
  63. data/lib/ransack/nodes.rb +1 -1
  64. data/lib/ransack/predicate.rb +17 -20
  65. data/lib/ransack/search.rb +17 -8
  66. data/lib/ransack/translate.rb +115 -115
  67. data/lib/ransack/version.rb +1 -1
  68. data/lib/ransack/visitor.rb +1 -12
  69. data/lib/ransack.rb +9 -9
  70. data/logo/ransack-h.png +0 -0
  71. data/logo/ransack-h.svg +34 -0
  72. data/logo/ransack-v.png +0 -0
  73. data/logo/ransack-v.svg +34 -0
  74. data/logo/ransack.png +0 -0
  75. data/logo/ransack.svg +21 -0
  76. data/ransack.gemspec +7 -24
  77. data/spec/console.rb +4 -0
  78. data/spec/helpers/polyamorous_helper.rb +19 -0
  79. data/spec/polyamorous/join_association_spec.rb +35 -0
  80. data/spec/polyamorous/join_dependency_spec.rb +97 -0
  81. data/spec/polyamorous/join_spec.rb +19 -0
  82. data/spec/ransack/adapters/active_record/base_spec.rb +370 -75
  83. data/spec/ransack/adapters/active_record/context_spec.rb +72 -34
  84. data/spec/ransack/configuration_spec.rb +97 -14
  85. data/spec/ransack/helpers/form_builder_spec.rb +2 -11
  86. data/spec/ransack/helpers/form_helper_spec.rb +481 -113
  87. data/spec/ransack/nodes/condition_spec.rb +24 -0
  88. data/spec/ransack/nodes/grouping_spec.rb +56 -0
  89. data/spec/ransack/predicate_spec.rb +79 -5
  90. data/spec/ransack/search_spec.rb +207 -81
  91. data/spec/spec_helper.rb +8 -0
  92. data/spec/support/schema.rb +100 -42
  93. metadata +57 -184
  94. data/.travis.yml +0 -69
  95. data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
  96. data/lib/ransack/adapters/active_record/3.0/context.rb +0 -201
  97. data/lib/ransack/adapters/active_record/3.1/context.rb +0 -215
  98. data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
  99. data/lib/ransack/adapters/active_record/compat.rb +0 -14
  100. data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
  101. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
  102. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
  103. data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
  104. data/lib/ransack/adapters/mongoid/base.rb +0 -130
  105. data/lib/ransack/adapters/mongoid/context.rb +0 -208
  106. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
  107. data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
  108. data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
  109. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
  110. data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
  111. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
  112. data/lib/ransack/adapters/mongoid/table.rb +0 -35
  113. data/lib/ransack/adapters/mongoid.rb +0 -13
  114. data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -276
  115. data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
  116. data/spec/mongoid/configuration_spec.rb +0 -102
  117. data/spec/mongoid/dependencies_spec.rb +0 -8
  118. data/spec/mongoid/helpers/ransack_helper.rb +0 -11
  119. data/spec/mongoid/nodes/condition_spec.rb +0 -34
  120. data/spec/mongoid/nodes/grouping_spec.rb +0 -13
  121. data/spec/mongoid/predicate_spec.rb +0 -155
  122. data/spec/mongoid/search_spec.rb +0 -446
  123. data/spec/mongoid/support/mongoid.yml +0 -6
  124. data/spec/mongoid/support/schema.rb +0 -128
  125. data/spec/mongoid/translate_spec.rb +0 -14
  126. data/spec/mongoid_spec_helper.rb +0 -59
  127. data/spec/ransack/dependencies_spec.rb +0 -12
@@ -3,53 +3,63 @@ module Ransack
3
3
  class Condition
4
4
 
5
5
  def arel_predicate
6
- arel_predicate_for(attributes_array)
6
+ attributes.map { |attribute|
7
+ association = attribute.parent
8
+ if negative? && attribute.associated_collection?
9
+ query = context.build_correlated_subquery(association)
10
+ context.remove_association(association)
11
+ if self.predicate_name == 'not_null' && self.value
12
+ query.where(format_predicate(attribute))
13
+ Arel::Nodes::In.new(context.primary_key, Arel.sql(query.to_sql))
14
+ else
15
+ query.where(format_predicate(attribute).not)
16
+ Arel::Nodes::NotIn.new(context.primary_key, Arel.sql(query.to_sql))
17
+ end
18
+ else
19
+ format_predicate(attribute)
20
+ end
21
+ }.reduce(combinator_method)
7
22
  end
8
23
 
9
24
  private
10
25
 
11
- def attributes_array
12
- attributes.map do |a|
13
- a.attr.send(
14
- arel_predicate_for_attribute(a), formatted_values_for_attribute(a)
15
- )
16
- end
17
- end
18
-
19
- def arel_predicate_for(predicates)
20
- if predicates.size > 1
21
- combinator_for(predicates)
22
- else
23
- format_predicate(predicates.first)
24
- end
26
+ def combinator_method
27
+ combinator === Constants::OR ? :or : :and
25
28
  end
26
29
 
27
- def combinator_for(predicates)
28
- if combinator === Constants::AND
29
- Arel::Nodes::Grouping.new(Arel::Nodes::And.new(predicates))
30
- elsif combinator === Constants::OR
31
- predicates.inject(&:or)
32
- end
33
- end
30
+ def format_predicate(attribute)
31
+ arel_pred = arel_predicate_for_attribute(attribute)
32
+ arel_values = formatted_values_for_attribute(attribute)
33
+ predicate = attr_value_for_attribute(attribute).public_send(arel_pred, arel_values)
34
34
 
35
- def format_predicate(predicate)
36
- predicate.tap do
37
- if casted_array_with_in_predicate?(predicate)
38
- predicate.right[0] = format_values_for(predicate.right[0])
35
+ if in_predicate?(predicate)
36
+ predicate.right = predicate.right.map do |pr|
37
+ casted_array?(pr) ? format_values_for(pr) : pr
39
38
  end
40
39
  end
40
+
41
+ predicate
41
42
  end
42
43
 
43
- def casted_array_with_in_predicate?(predicate)
44
+ def in_predicate?(predicate)
44
45
  return unless defined?(Arel::Nodes::Casted)
45
- predicate.class == Arel::Nodes::In &&
46
- predicate.right[0].respond_to?(:val) &&
47
- predicate.right[0].val.is_a?(Array)
46
+ predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
47
+ end
48
+
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
48
52
  end
49
53
 
50
54
  def format_values_for(predicate)
51
- predicate.val.map do |value|
52
- value.is_a?(String) ? Arel::Nodes.build_quoted(value) : value
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|
62
+ val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
53
63
  end
54
64
  end
55
65
 
@@ -2,11 +2,7 @@ module Ransack
2
2
  module Translate
3
3
 
4
4
  def self.i18n_key(klass)
5
- if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
6
- klass.model_name.i18n_key.to_s.tr('.'.freeze, '/'.freeze)
7
- else
8
- klass.model_name.i18n_key.to_s.freeze
9
- end
5
+ klass.model_name.i18n_key
10
6
  end
11
7
  end
12
8
  end
@@ -20,5 +20,28 @@ module Ransack
20
20
  end
21
21
  end
22
22
 
23
+ def visit_Ransack_Nodes_Sort(object)
24
+ if object.valid?
25
+ if object.attr.is_a?(Arel::Attributes::Attribute)
26
+ object.attr.send(object.dir)
27
+ else
28
+ ordered(object)
29
+ end
30
+ else
31
+ scope_name = :"sort_by_#{object.name}_#{object.dir}"
32
+ scope_name if object.context.object.respond_to?(scope_name)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def ordered(object)
39
+ case object.dir
40
+ when 'asc'.freeze
41
+ Arel::Nodes::Ascending.new(object.attr)
42
+ when 'desc'.freeze
43
+ Arel::Nodes::Descending.new(object.attr)
44
+ end
45
+ end
23
46
  end
24
47
  end
@@ -1,13 +1,14 @@
1
1
  require 'ransack/adapters/active_record/base'
2
- ActiveRecord::Base.extend Ransack::Adapters::ActiveRecord::Base
3
2
 
4
- case ActiveRecord::VERSION::STRING
5
- when /^3\.0\./
6
- require 'ransack/adapters/active_record/3.0/context'
7
- when /^3\.1\./
8
- require 'ransack/adapters/active_record/3.1/context'
9
- when /^3\.2\./
10
- require 'ransack/adapters/active_record/3.2/context'
11
- else
12
- require 'ransack/adapters/active_record/context'
3
+ ActiveSupport.on_load(:active_record) do
4
+ extend Ransack::Adapters::ActiveRecord::Base
5
+
6
+ Ransack::SUPPORTS_ATTRIBUTE_ALIAS =
7
+ begin
8
+ ActiveRecord::Base.respond_to?(:attribute_aliases)
9
+ rescue NameError
10
+ false
11
+ end
13
12
  end
13
+
14
+ require 'ransack/adapters/active_record/context'
@@ -1,42 +1,64 @@
1
1
  module Ransack
2
2
  module Adapters
3
3
 
4
- def self.current_adapters
5
- @current_adapters ||= {
6
- :active_record => defined?(::ActiveRecord::Base),
7
- :mongoid => defined?(::Mongoid) && !defined?(::ActiveRecord::Base)
8
- }
4
+ def self.object_mapper
5
+ @object_mapper ||= instantiate_object_mapper
9
6
  end
10
- def self.require_constants
11
- require 'ransack/adapters/mongoid/ransack/constants' if current_adapters[:mongoid]
12
- require 'ransack/adapters/active_record/ransack/constants' if current_adapters[:active_record]
7
+
8
+ def self.instantiate_object_mapper
9
+ if defined?(::ActiveRecord::Base)
10
+ ActiveRecordAdapter.new
11
+ elsif defined?(::Mongoid)
12
+ MongoidAdapter.new
13
+ else
14
+ raise "Unsupported adapter"
15
+ end
13
16
  end
14
17
 
15
- def self.require_adapter
16
- if current_adapters[:active_record]
18
+ class ActiveRecordAdapter
19
+ def require_constants
20
+ require 'ransack/adapters/active_record/ransack/constants'
21
+ end
22
+
23
+ def require_adapter
17
24
  require 'ransack/adapters/active_record/ransack/translate'
18
25
  require 'ransack/adapters/active_record'
19
26
  end
20
27
 
21
- if current_adapters[:mongoid]
28
+ def require_context
29
+ require 'ransack/adapters/active_record/ransack/visitor'
30
+ end
31
+
32
+ def require_nodes
33
+ require 'ransack/adapters/active_record/ransack/nodes/condition'
34
+ end
35
+
36
+ def require_search
37
+ require 'ransack/adapters/active_record/ransack/context'
38
+ end
39
+ end
40
+
41
+ class MongoidAdapter
42
+ def require_constants
43
+ require 'ransack/adapters/mongoid/ransack/constants'
44
+ end
45
+
46
+ def require_adapter
22
47
  require 'ransack/adapters/mongoid/ransack/translate'
23
48
  require 'ransack/adapters/mongoid'
24
49
  end
25
- end
26
50
 
27
- def self.require_context
28
- require 'ransack/adapters/active_record/ransack/visitor' if current_adapters[:active_record]
29
- require 'ransack/adapters/mongoid/ransack/visitor' if current_adapters[:mongoid]
30
- end
51
+ def require_context
52
+ require 'ransack/adapters/mongoid/ransack/visitor'
53
+ end
31
54
 
32
- def self.require_nodes
33
- require 'ransack/adapters/active_record/ransack/nodes/condition' if current_adapters[:active_record]
34
- require 'ransack/adapters/mongoid/ransack/nodes/condition' if current_adapters[:mongoid]
35
- end
55
+ def require_nodes
56
+ require 'ransack/adapters/mongoid/ransack/nodes/condition'
57
+ end
36
58
 
37
- def self.require_search
38
- require 'ransack/adapters/active_record/ransack/context' if current_adapters[:active_record]
39
- require 'ransack/adapters/mongoid/ransack/context' if current_adapters[:mongoid]
59
+ def require_search
60
+ require 'ransack/adapters/mongoid/ransack/context'
61
+ end
40
62
  end
41
63
  end
42
64
  end
@@ -5,10 +5,36 @@ module Ransack
5
5
  module Configuration
6
6
 
7
7
  mattr_accessor :predicates, :options
8
- self.predicates = {}
8
+
9
+ class PredicateCollection
10
+ attr_reader :sorted_names_with_underscores
11
+
12
+ def initialize
13
+ @collection = {}
14
+ @sorted_names_with_underscores = []
15
+ end
16
+
17
+ delegate :[], :keys, :has_key?, to: :@collection
18
+
19
+ def []=(key, value)
20
+ @sorted_names_with_underscores << [key, '_' + key]
21
+ @sorted_names_with_underscores.sort! { |(a, _), (b, _)| b.length <=> a.length }
22
+
23
+ @collection[key] = value
24
+ end
25
+ end
26
+
27
+ self.predicates = PredicateCollection.new
28
+
9
29
  self.options = {
10
30
  :search_key => :q,
11
- :ignore_unknown_conditions => true
31
+ :ignore_unknown_conditions => true,
32
+ :hide_sort_order_indicators => false,
33
+ :up_arrow => '&#9660;'.freeze,
34
+ :down_arrow => '&#9650;'.freeze,
35
+ :default_arrow => nil,
36
+ :sanitize_scope_args => true,
37
+ :postgres_fields_sort_option => nil
12
38
  }
13
39
 
14
40
  def configure
@@ -61,12 +87,89 @@ module Ransack
61
87
  self.options[:search_key] = name
62
88
  end
63
89
 
64
- # Raise an error if an unknown predicate, condition or attribute is passed
65
- # into a search.
90
+ # By default Ransack ignores errors if an unknown predicate, condition or
91
+ # attribute is passed into a search. The default may be overridden in an
92
+ # initializer file like `config/initializers/ransack.rb` as follows:
93
+ #
94
+ # Ransack.configure do |config|
95
+ # # Raise if an unknown predicate, condition or attribute is passed
96
+ # config.ignore_unknown_conditions = false
97
+ # end
98
+ #
66
99
  def ignore_unknown_conditions=(boolean)
67
100
  self.options[:ignore_unknown_conditions] = boolean
68
101
  end
69
102
 
103
+ # By default, Ransack displays sort order indicator arrows with HTML codes:
104
+ #
105
+ # up_arrow: '&#9660;'
106
+ # down_arrow: '&#9650;'
107
+ #
108
+ # There is also a default arrow which is displayed if a column is not sorted.
109
+ # By default this is nil so nothing will be displayed.
110
+ #
111
+ # Any of the defaults may be globally overridden in an initializer file
112
+ # like `config/initializers/ransack.rb` as follows:
113
+ #
114
+ # Ransack.configure do |config|
115
+ # # Globally set the up arrow to an icon, and the down and default arrows to unicode.
116
+ # config.custom_arrows = {
117
+ # up_arrow: '<i class="fa fa-long-arrow-up"></i>',
118
+ # down_arrow: 'U+02193',
119
+ # default_arrow: 'U+11047'
120
+ # }
121
+ # end
122
+ #
123
+ def custom_arrows=(opts = {})
124
+ self.options[:up_arrow] = opts[:up_arrow].freeze if opts[:up_arrow]
125
+ self.options[:down_arrow] = opts[:down_arrow].freeze if opts[:down_arrow]
126
+ self.options[:default_arrow] = opts[:default_arrow].freeze if opts[:default_arrow]
127
+ end
128
+
129
+ # Ransack sanitizes many values in your custom scopes into booleans.
130
+ # [1, '1', 't', 'T', 'true', 'TRUE'] all evaluate to true.
131
+ # [0, '0', 'f', 'F', 'false', 'FALSE'] all evaluate to false.
132
+ #
133
+ # This default may be globally overridden in an initializer file like
134
+ # `config/initializers/ransack.rb` as follows:
135
+ #
136
+ # Ransack.configure do |config|
137
+ # # Accept my custom scope values as what they are.
138
+ # config.sanitize_custom_scope_booleans = false
139
+ # end
140
+ #
141
+ def sanitize_custom_scope_booleans=(boolean)
142
+ self.options[:sanitize_scope_args] = boolean
143
+ end
144
+
145
+ # The `NULLS FIRST` and `NULLS LAST` options can be used to determine
146
+ # whether nulls appear before or after non-null values in the sort ordering.
147
+ #
148
+ # User may want to configure it like this:
149
+ #
150
+ # Ransack.configure do |c|
151
+ # c.postgres_fields_sort_option = :nulls_first # or :nulls_last
152
+ # end
153
+ #
154
+ # See this feature: https://www.postgresql.org/docs/13/queries-order.html
155
+ #
156
+ def postgres_fields_sort_option=(setting)
157
+ self.options[:postgres_fields_sort_option] = setting
158
+ end
159
+
160
+ # By default, Ransack displays sort order indicator arrows in sort links.
161
+ # The default may be globally overridden in an initializer file like
162
+ # `config/initializers/ransack.rb` as follows:
163
+ #
164
+ # Ransack.configure do |config|
165
+ # # Hide sort link order indicators globally across the application
166
+ # config.hide_sort_order_indicators = true
167
+ # end
168
+ #
169
+ def hide_sort_order_indicators=(boolean)
170
+ self.options[:hide_sort_order_indicators] = boolean
171
+ end
172
+
70
173
  def arel_predicate_with_suffix(arel_predicate, suffix)
71
174
  if arel_predicate === Proc
72
175
  proc { |v| "#{arel_predicate.call(v)}#{suffix}" }
@@ -1,19 +1,7 @@
1
1
  module Ransack
2
2
  module Constants
3
- ASC = 'asc'.freeze
4
- DESC = 'desc'.freeze
5
- ASC_DESC = [ASC, DESC].freeze
6
-
7
- ASC_ARROW = '&#9650;'.freeze
8
- DESC_ARROW = '&#9660;'.freeze
9
-
10
3
  OR = 'or'.freeze
11
4
  AND = 'and'.freeze
12
- SPACED_AND = ' AND '.freeze
13
-
14
- SORT = 'sort'.freeze
15
- SORT_LINK = 'sort_link'.freeze
16
- SORT_DIRECTION = 'sort_direction'.freeze
17
5
 
18
6
  CAP_SEARCH = 'Search'.freeze
19
7
  SEARCH = 'search'.freeze
@@ -23,17 +11,12 @@ module Ransack
23
11
  ATTRIBUTES = 'attributes'.freeze
24
12
  COMBINATOR = 'combinator'.freeze
25
13
 
26
- SPACE = ' '.freeze
27
- COMMA_SPACE = ', '.freeze
28
- COLON_SPACE = ': '.freeze
29
14
  TWO_COLONS = '::'.freeze
30
15
  UNDERSCORE = '_'.freeze
31
16
  LEFT_PARENTHESIS = '('.freeze
32
17
  Q = 'q'.freeze
33
18
  I = 'i'.freeze
34
- NON_BREAKING_SPACE = '&nbsp;'.freeze
35
19
  DOT_ASTERIX = '.*'.freeze
36
- EMPTY = ''.freeze
37
20
 
38
21
  STRING_JOIN = 'string_join'.freeze
39
22
  ASSOCIATION_JOIN = 'association_join'.freeze
@@ -44,14 +27,17 @@ module Ransack
44
27
  FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
45
28
  BOOLEAN_VALUES = (TRUE_VALUES + FALSE_VALUES).freeze
46
29
 
47
- S_SORTS = %w(s sorts).freeze
48
- AND_OR = %w(and or).freeze
49
- IN_NOT_IN = %w(in not_in).freeze
50
- SUFFIXES = %w(_any _all).freeze
51
- AREL_PREDICATES = %w(
52
- eq not_eq matches does_not_match lt lteq gt gteq in not_in
53
- ).freeze
54
- A_S_I = %w(a s i).freeze
30
+ AND_OR = ['and'.freeze, 'or'.freeze].freeze
31
+ IN_NOT_IN = ['in'.freeze, 'not_in'.freeze].freeze
32
+ SUFFIXES = ['_any'.freeze, '_all'.freeze].freeze
33
+ AREL_PREDICATES = [
34
+ 'eq'.freeze, 'not_eq'.freeze,
35
+ 'matches'.freeze, 'does_not_match'.freeze,
36
+ 'lt'.freeze, 'lteq'.freeze,
37
+ 'gt'.freeze, 'gteq'.freeze,
38
+ 'in'.freeze, 'not_in'.freeze
39
+ ].freeze
40
+ A_S_I = ['a'.freeze, 's'.freeze, 'i'.freeze].freeze
55
41
 
56
42
  EQ = 'eq'.freeze
57
43
  NOT_EQ = 'not_eq'.freeze
@@ -59,7 +45,8 @@ module Ransack
59
45
  NOT_EQ_ALL = 'not_eq_all'.freeze
60
46
  CONT = 'cont'.freeze
61
47
 
62
- RAILS_4_1 = '4.1'.freeze
48
+ RAILS_6_0 = '6.0.0'.freeze
49
+ RAILS_6_1_ALPHA = '6.1.0.alpha'.freeze
63
50
 
64
51
  RANSACK_SLASH_SEARCHES = 'ransack/searches'.freeze
65
52
  RANSACK_SLASH_SEARCHES_SLASH_SEARCH = 'ransack/searches/search'.freeze
@@ -1,5 +1,5 @@
1
1
  require 'ransack/visitor'
2
- Ransack::Adapters.require_context
2
+ Ransack::Adapters.object_mapper.require_context
3
3
 
4
4
  module Ransack
5
5
  class Context
@@ -17,9 +17,12 @@ module Ransack
17
17
  end
18
18
 
19
19
  def for(object, options = {})
20
- context = Class === object ?
21
- for_class(object, options) :
22
- for_object(object, options)
20
+ context =
21
+ if Class === object
22
+ for_class(object, options)
23
+ else
24
+ for_object(object, options)
25
+ end
23
26
  context or raise ArgumentError,
24
27
  "Don't know what context to use for #{object}"
25
28
  end
@@ -37,7 +40,7 @@ module Ransack
37
40
  # Convert a string representing a chain of associations and an attribute
38
41
  # into the attribute itself
39
42
  def contextualize(str)
40
- parent, attr_name = @bind_pairs[str]
43
+ parent, attr_name = bind_pair_for(str)
41
44
  table_for(parent)[attr_name]
42
45
  end
43
46
 
@@ -55,32 +58,34 @@ module Ransack
55
58
  end
56
59
 
57
60
  def bind(object, str)
58
- object.parent, object.attr_name = @bind_pairs[str]
61
+ return nil unless str
62
+ object.parent, object.attr_name = bind_pair_for(str)
59
63
  end
60
64
 
61
65
  def traverse(str, base = @base)
62
- str ||= Constants::EMPTY
63
-
64
- if (segments = str.split(/_/)).size > 0
66
+ str ||= ''.freeze
67
+ segments = str.split(Constants::UNDERSCORE)
68
+ unless segments.empty?
65
69
  remainder = []
66
70
  found_assoc = nil
67
- while !found_assoc && segments.size > 0 do
71
+ until found_assoc || segments.empty?
68
72
  # Strip the _of_Model_type text from the association name, but hold
69
73
  # onto it in klass, for use as the next base
70
74
  assoc, klass = unpolymorphize_association(
71
75
  segments.join(Constants::UNDERSCORE)
72
- )
76
+ )
73
77
  if found_assoc = get_association(assoc, base)
74
78
  base = traverse(
75
- remainder.join(
76
- Constants::UNDERSCORE), klass || found_assoc.klass
77
- )
79
+ remainder.join(Constants::UNDERSCORE), klass || found_assoc.klass
80
+ )
78
81
  end
79
82
 
80
83
  remainder.unshift segments.pop
81
84
  end
82
- raise UntraversableAssociationError,
83
- "No association matches #{str}" unless found_assoc
85
+ unless found_assoc
86
+ raise(UntraversableAssociationError,
87
+ "No association matches #{str}")
88
+ end
84
89
  end
85
90
 
86
91
  klassify(base)
@@ -88,22 +93,21 @@ module Ransack
88
93
 
89
94
  def association_path(str, base = @base)
90
95
  base = klassify(base)
91
- str ||= Constants::EMPTY
96
+ str ||= ''.freeze
92
97
  path = []
93
- segments = str.split(/_/)
98
+ segments = str.split(Constants::UNDERSCORE)
94
99
  association_parts = []
95
- if (segments = str.split(/_/)).size > 0
96
- while segments.size > 0 &&
97
- !base.columns_hash[segments.join(Constants::UNDERSCORE)] &&
98
- association_parts << segments.shift do
100
+ unless segments.empty?
101
+ while !segments.empty? &&
102
+ !base.columns_hash[segments.join(Constants::UNDERSCORE)] &&
103
+ association_parts << segments.shift
99
104
  assoc, klass = unpolymorphize_association(
100
105
  association_parts.join(Constants::UNDERSCORE)
101
- )
102
- if found_assoc = get_association(assoc, base)
103
- path += association_parts
104
- association_parts = []
105
- base = klassify(klass || found_assoc)
106
- end
106
+ )
107
+ next unless found_assoc = get_association(assoc, base)
108
+ path += association_parts
109
+ association_parts = []
110
+ base = klassify(klass || found_assoc)
107
111
  end
108
112
  end
109
113
 
@@ -118,9 +122,13 @@ module Ransack
118
122
  end
119
123
  end
120
124
 
125
+ def ransackable_alias(str)
126
+ klass._ransack_aliases.fetch(str, str)
127
+ end
128
+
121
129
  def ransackable_attribute?(str, klass)
122
130
  klass.ransackable_attributes(auth_object).include?(str) ||
123
- klass.ransortable_attributes(auth_object).include?(str)
131
+ klass.ransortable_attributes(auth_object).include?(str)
124
132
  end
125
133
 
126
134
  def ransackable_association?(str, klass)
@@ -128,18 +136,22 @@ module Ransack
128
136
  end
129
137
 
130
138
  def ransackable_scope?(str, klass)
131
- klass.ransackable_scopes(auth_object).any? { |s| s.to_s == str }
139
+ klass.ransackable_scopes(auth_object).any? { |s| s.to_sym == str.to_sym }
140
+ end
141
+
142
+ def ransackable_scope_skip_sanitize_args?(str, klass)
143
+ klass.ransackable_scopes_skip_sanitize_args.any? { |s| s.to_sym == str.to_sym }
132
144
  end
133
145
 
134
- def searchable_attributes(str = Constants::EMPTY)
146
+ def searchable_attributes(str = ''.freeze)
135
147
  traverse(str).ransackable_attributes(auth_object)
136
148
  end
137
149
 
138
- def sortable_attributes(str = Constants::EMPTY)
150
+ def sortable_attributes(str = ''.freeze)
139
151
  traverse(str).ransortable_attributes(auth_object)
140
152
  end
141
153
 
142
- def searchable_associations(str = Constants::EMPTY)
154
+ def searchable_associations(str = ''.freeze)
143
155
  traverse(str).ransackable_associations(auth_object)
144
156
  end
145
157
  end