ransack 1.7.0 → 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
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