ransack 1.8.3 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.travis.yml +26 -63
  4. data/CHANGELOG.md +187 -24
  5. data/CONTRIBUTING.md +9 -0
  6. data/Gemfile +5 -20
  7. data/README.md +163 -40
  8. data/Rakefile +1 -22
  9. data/lib/ransack/adapters/active_record/base.rb +11 -2
  10. data/lib/ransack/adapters/active_record/context.rb +178 -168
  11. data/lib/ransack/adapters/active_record/ransack/constants.rb +6 -3
  12. data/lib/ransack/adapters/active_record/ransack/context.rb +10 -16
  13. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +3 -3
  14. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
  15. data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
  16. data/lib/ransack/adapters/active_record.rb +0 -9
  17. data/lib/ransack/adapters.rb +2 -0
  18. data/lib/ransack/configuration.rb +30 -4
  19. data/lib/ransack/constants.rb +4 -1
  20. data/lib/ransack/context.rb +29 -24
  21. data/lib/ransack/helpers/form_builder.rb +15 -3
  22. data/lib/ransack/helpers/form_helper.rb +8 -3
  23. data/lib/ransack/locale/ar.yml +70 -0
  24. data/lib/ransack/locale/az.yml +70 -0
  25. data/lib/ransack/locale/bg.yml +70 -0
  26. data/lib/ransack/locale/ca.yml +70 -0
  27. data/lib/ransack/locale/el.yml +70 -0
  28. data/lib/ransack/locale/es.yml +22 -22
  29. data/lib/ransack/locale/fa.yml +70 -0
  30. data/lib/ransack/locale/fi.yml +71 -0
  31. data/lib/ransack/locale/it.yml +70 -0
  32. data/lib/ransack/locale/nl.yml +4 -4
  33. data/lib/ransack/locale/ru.yml +70 -0
  34. data/lib/ransack/locale/tr.yml +70 -0
  35. data/lib/ransack/locale/zh-CN.yml +12 -12
  36. data/lib/ransack/nodes/attribute.rb +1 -1
  37. data/lib/ransack/nodes/grouping.rb +2 -7
  38. data/lib/ransack/nodes/value.rb +74 -68
  39. data/lib/ransack/predicate.rb +11 -19
  40. data/lib/ransack/search.rb +1 -1
  41. data/lib/ransack/translate.rb +115 -115
  42. data/lib/ransack/version.rb +1 -1
  43. data/lib/ransack/visitor.rb +1 -12
  44. data/lib/ransack.rb +5 -2
  45. data/logo/ransack-h.png +0 -0
  46. data/logo/ransack-h.svg +34 -0
  47. data/logo/ransack-v.png +0 -0
  48. data/logo/ransack-v.svg +34 -0
  49. data/logo/ransack.png +0 -0
  50. data/logo/ransack.svg +21 -0
  51. data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +2 -0
  52. data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +2 -0
  53. data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +31 -0
  54. data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +112 -0
  55. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +31 -0
  56. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +112 -0
  57. data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/reflection.rb +12 -0
  58. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_association.rb +22 -0
  59. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_dependency.rb +81 -0
  60. data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/reflection.rb +2 -0
  61. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +2 -0
  62. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +81 -0
  63. data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +2 -0
  64. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +2 -0
  65. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +2 -0
  66. data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +2 -0
  67. data/polyamorous/lib/polyamorous/join.rb +70 -0
  68. data/polyamorous/lib/polyamorous/swapping_reflection_class.rb +11 -0
  69. data/polyamorous/lib/polyamorous/tree_node.rb +7 -0
  70. data/polyamorous/lib/polyamorous/version.rb +3 -0
  71. data/polyamorous/lib/polyamorous.rb +29 -0
  72. data/polyamorous/polyamorous.gemspec +35 -0
  73. data/ransack.gemspec +9 -10
  74. data/spec/helpers/polyamorous_helper.rb +28 -0
  75. data/spec/ransack/adapters/active_record/base_spec.rb +74 -0
  76. data/spec/ransack/adapters/active_record/context_spec.rb +44 -6
  77. data/spec/ransack/configuration_spec.rb +17 -2
  78. data/spec/ransack/helpers/form_builder_spec.rb +3 -15
  79. data/spec/ransack/helpers/form_helper_spec.rb +88 -151
  80. data/spec/ransack/join_association_spec.rb +28 -0
  81. data/spec/ransack/join_dependency_spec.rb +97 -0
  82. data/spec/ransack/join_spec.rb +19 -0
  83. data/spec/ransack/predicate_spec.rb +16 -2
  84. data/spec/ransack/search_spec.rb +32 -3
  85. data/spec/spec_helper.rb +5 -0
  86. data/spec/support/schema.rb +45 -21
  87. metadata +81 -67
  88. data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
  89. data/lib/ransack/adapters/active_record/3.0/context.rb +0 -203
  90. data/lib/ransack/adapters/active_record/3.1/context.rb +0 -212
  91. data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
  92. data/lib/ransack/adapters/active_record/compat.rb +0 -14
  93. data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
  94. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
  95. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
  96. data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
  97. data/lib/ransack/adapters/mongoid/base.rb +0 -134
  98. data/lib/ransack/adapters/mongoid/context.rb +0 -212
  99. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
  100. data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
  101. data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
  102. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
  103. data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
  104. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
  105. data/lib/ransack/adapters/mongoid/table.rb +0 -35
  106. data/lib/ransack/adapters/mongoid.rb +0 -15
  107. data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -314
  108. data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
  109. data/spec/mongoid/configuration_spec.rb +0 -162
  110. data/spec/mongoid/dependencies_spec.rb +0 -8
  111. data/spec/mongoid/helpers/ransack_helper.rb +0 -11
  112. data/spec/mongoid/nodes/condition_spec.rb +0 -49
  113. data/spec/mongoid/nodes/grouping_spec.rb +0 -13
  114. data/spec/mongoid/predicate_spec.rb +0 -155
  115. data/spec/mongoid/search_spec.rb +0 -445
  116. data/spec/mongoid/support/mongoid.yml +0 -11
  117. data/spec/mongoid/support/schema.rb +0 -135
  118. data/spec/mongoid/translate_spec.rb +0 -14
  119. data/spec/mongoid_spec_helper.rb +0 -63
  120. data/spec/ransack/dependencies_spec.rb +0 -12
@@ -1,5 +1,4 @@
1
1
  require 'ransack/context'
2
- require 'ransack/adapters/active_record/compat'
3
2
  require 'polyamorous'
4
3
 
5
4
  module Ransack
@@ -7,14 +6,11 @@ module Ransack
7
6
  module ActiveRecord
8
7
  class Context < ::Ransack::Context
9
8
 
10
- # Because the AR::Associations namespace is insane
11
- if defined? ::ActiveRecord::Associations::JoinDependency
12
- JoinDependency = ::ActiveRecord::Associations::JoinDependency
13
- end
14
-
15
9
  def initialize(object, options = {})
16
10
  super
17
- @arel_visitor = @engine.connection.visitor
11
+ if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2
12
+ @arel_visitor = @engine.connection.visitor
13
+ end
18
14
  end
19
15
 
20
16
  def relation_for(object)
@@ -25,19 +21,39 @@ module Ransack
25
21
  return nil unless attr && attr.valid?
26
22
  name = attr.arel_attribute.name.to_s
27
23
  table = attr.arel_attribute.relation.table_name
28
- schema_cache = @engine.connection.schema_cache
29
- unless schema_cache.send(database_table_exists?, table)
24
+ schema_cache = self.klass.connection.schema_cache
25
+ unless schema_cache.send(:data_source_exists?, table)
30
26
  raise "No table named #{table} exists."
31
27
  end
32
- schema_cache.columns_hash(table)[name].type
28
+ attr.klass.columns.find { |column| column.name == name }.type
33
29
  end
34
30
 
35
31
  def evaluate(search, opts = {})
36
32
  viz = Visitor.new
37
33
  relation = @object.where(viz.accept(search.base))
34
+
38
35
  if search.sorts.any?
39
- relation = relation.except(:order).reorder(viz.accept(search.sorts))
36
+ relation = relation.except(:order)
37
+ # Rather than applying all of the search's sorts in one fell swoop,
38
+ # as the original implementation does, we apply one at a time.
39
+ #
40
+ # If the sort (returned by the Visitor above) is a symbol, we know
41
+ # that it represents a scope on the model and we can apply that
42
+ # scope.
43
+ #
44
+ # Otherwise, we fall back to the applying the sort with the "order"
45
+ # method as the original implementation did. Actually the original
46
+ # implementation used "reorder," which was overkill since we already
47
+ # have a clean slate after "relation.except(:order)" above.
48
+ viz.accept(search.sorts).each do |scope_or_sort|
49
+ if scope_or_sort.is_a?(Symbol)
50
+ relation = relation.send(scope_or_sort)
51
+ else
52
+ relation = relation.order(scope_or_sort)
53
+ end
54
+ end
40
55
  end
56
+
41
57
  opts[:distinct] ? relation.distinct : relation
42
58
  end
43
59
 
@@ -45,7 +61,7 @@ module Ransack
45
61
  exists = false
46
62
  if ransackable_attribute?(str, klass)
47
63
  exists = true
48
- elsif (segments = str.split(/_/)).size > 1
64
+ elsif (segments = str.split(Constants::UNDERSCORE)).size > 1
49
65
  remainder = []
50
66
  found_assoc = nil
51
67
  while !found_assoc && remainder.unshift(segments.pop) &&
@@ -80,84 +96,56 @@ module Ransack
80
96
  end
81
97
  end
82
98
 
83
- if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
84
-
85
- def join_associations
86
- raise NotImplementedError,
87
- "ActiveRecord 4.1 and later does not use join_associations. Use join_sources."
88
- end
89
-
90
- # All dependent Arel::Join nodes used in the search query.
91
- #
92
- # This could otherwise be done as `@object.arel.join_sources`, except
93
- # that ActiveRecord's build_joins sets up its own JoinDependency.
94
- # This extracts what we need to access the joins using our existing
95
- # JoinDependency to track table aliases.
96
- #
97
- def join_sources
98
- base, joins =
99
- if ::ActiveRecord::VERSION::MAJOR >= 5
100
- [
101
- Arel::SelectManager.new(@object.table),
102
- @join_dependency.join_constraints(@object.joins_values, @join_type)
103
- ]
99
+ # All dependent Arel::Join nodes used in the search query.
100
+ #
101
+ # This could otherwise be done as `@object.arel.join_sources`, except
102
+ # that ActiveRecord's build_joins sets up its own JoinDependency.
103
+ # This extracts what we need to access the joins using our existing
104
+ # JoinDependency to track table aliases.
105
+ #
106
+ def join_sources
107
+ base, joins =
108
+ if ::ActiveRecord::VERSION::STRING > Constants::RAILS_5_2_0
109
+ alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, @object.table.name, [])
110
+ constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
111
+ @join_dependency.join_constraints(@object.joins_values, alias_tracker)
104
112
  else
105
- [
106
- Arel::SelectManager.new(@object.engine, @object.table),
107
- @join_dependency.join_constraints(@object.joins_values)
108
- ]
109
- end
110
- joins.each do |aliased_join|
111
- base.from(aliased_join)
113
+ @join_dependency.join_constraints(@object.joins_values, @join_type, alias_tracker)
112
114
  end
113
- base.join_sources
114
- end
115
-
116
- else
117
115
 
118
- # All dependent JoinAssociation items used in the search query.
119
- #
120
- # Deprecated: this goes away in ActiveRecord 4.1. Use join_sources.
121
- #
122
- def join_associations
123
- @join_dependency.join_associations
116
+ [
117
+ Arel::SelectManager.new(@object.table),
118
+ constraints
119
+ ]
120
+ else
121
+ [
122
+ Arel::SelectManager.new(@object.table),
123
+ @join_dependency.join_constraints(@object.joins_values, @join_type)
124
+ ]
124
125
  end
125
-
126
- def join_sources
127
- base = Arel::SelectManager.new(@object.engine, @object.table)
128
- joins = @object.joins_values
129
- joins.each do |assoc|
130
- assoc.join_to(base)
131
- end
132
- base.join_sources
126
+ joins = joins.collect(&:joins).flatten if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2
127
+ joins.each do |aliased_join|
128
+ base.from(aliased_join)
133
129
  end
134
-
130
+ base.join_sources
135
131
  end
136
132
 
137
133
  def alias_tracker
138
- @join_dependency.alias_tracker
134
+ @join_dependency.send(:alias_tracker)
139
135
  end
140
136
 
141
137
  def lock_association(association)
142
138
  @lock_associations << association
143
139
  end
144
140
 
145
- if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
146
- def remove_association(association)
147
- return if @lock_associations.include?(association)
148
- @join_dependency.join_root.children.delete_if { |stashed|
149
- stashed.eql?(association)
150
- }
151
- @object.joins_values.delete_if { |jd|
152
- jd.join_root.children.map(&:object_id) == [association.object_id]
153
- }
154
- end
155
- else
156
- def remove_association(association)
157
- return if @lock_associations.include?(association)
158
- @join_dependency.join_parts.delete(association)
159
- @object.joins_values.delete(association)
160
- end
141
+ def remove_association(association)
142
+ return if @lock_associations.include?(association)
143
+ @join_dependency.instance_variable_get(:@join_root).children.delete_if { |stashed|
144
+ stashed.eql?(association)
145
+ }
146
+ @object.joins_values.delete_if { |jd|
147
+ jd.instance_variable_get(:@join_root).children.map(&:object_id) == [association.object_id]
148
+ }
161
149
  end
162
150
 
163
151
  # Build an Arel subquery that selects keys for the top query,
@@ -181,8 +169,7 @@ module Ransack
181
169
  def build_correlated_subquery(association)
182
170
  join_constraints = extract_joins(association)
183
171
  join_root = join_constraints.shift
184
- join_table = join_root.left
185
- correlated_key = join_root.right.expr.left
172
+ correlated_key = extract_correlated_key(join_root)
186
173
  subquery = Arel::SelectManager.new(association.base_klass)
187
174
  subquery.from(join_root.left)
188
175
  subquery.project(correlated_key)
@@ -198,11 +185,17 @@ module Ransack
198
185
 
199
186
  private
200
187
 
201
- def database_table_exists?
202
- if ::ActiveRecord::VERSION::MAJOR >= 5
203
- :data_source_exists?
188
+ def extract_correlated_key(join_root)
189
+ correlated_key = join_root.right.expr.left
190
+
191
+ if correlated_key.is_a? Arel::Nodes::And
192
+ correlated_key = correlated_key.left.left
193
+ elsif correlated_key.is_a? Arel::Nodes::Equality
194
+ correlated_key = correlated_key.left
195
+ elsif correlated_key.is_a? Arel::Nodes::Grouping
196
+ correlated_key = join_root.right.expr.right.left
204
197
  else
205
- :table_exists?
198
+ correlated_key
206
199
  end
207
200
  end
208
201
 
@@ -250,7 +243,9 @@ module Ransack
250
243
  # Checkout active_record/relation/query_methods.rb +build_joins+ for
251
244
  # reference. Lots of duplicated code maybe we can avoid it
252
245
  def build_joins(relation)
253
- buckets = relation.joins_values.group_by do |join|
246
+ buckets = relation.joins_values + relation.left_outer_joins_values
247
+
248
+ buckets = buckets.group_by do |join|
254
249
  case join
255
250
  when String
256
251
  :string_join
@@ -268,123 +263,138 @@ module Ransack
268
263
  association_joins = buckets[:association_join]
269
264
  stashed_association_joins = buckets[:stashed_join]
270
265
  join_nodes = buckets[:join_node].uniq
271
- string_joins = buckets[:string_join].map(&:strip).uniq
266
+ string_joins = buckets[:string_join].map(&:strip)
267
+ string_joins.uniq!
272
268
 
273
- join_list =
274
- if ::ActiveRecord::VERSION::MAJOR >= 5
275
- join_nodes +
276
- convert_join_strings_to_ast(relation.table, string_joins)
277
- else
278
- relation.send :custom_join_ast,
279
- relation.table.from(relation.table), string_joins
280
- end
281
-
282
- join_dependency = JoinDependency.new(
283
- relation.klass, association_joins, join_list
284
- )
285
-
286
- join_nodes.each do |join|
287
- join_dependency.alias_tracker.aliases[join.left.name.downcase] = 1
288
- end
269
+ join_list = join_nodes + convert_join_strings_to_ast(relation.table, string_joins)
289
270
 
290
- if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
291
- join_dependency
271
+ if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2_0
272
+ join_dependency = Polyamorous::JoinDependency.new(relation.klass, association_joins, join_list)
273
+ join_nodes.each do |join|
274
+ join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
275
+ end
276
+ elsif ::ActiveRecord::VERSION::STRING == Constants::RAILS_5_2_0
277
+ alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
278
+ join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, alias_tracker)
279
+ join_nodes.each do |join|
280
+ join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
281
+ end
292
282
  else
293
- join_dependency.graft(*stashed_association_joins)
283
+ alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
284
+ join_dependency = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
285
+ Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin)
286
+ else
287
+ Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins)
288
+ end
289
+ join_dependency.instance_variable_set(:@alias_tracker, alias_tracker)
290
+ join_nodes.each do |join|
291
+ join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
292
+ end
294
293
  end
294
+ join_dependency
295
295
  end
296
296
 
297
297
  def convert_join_strings_to_ast(table, joins)
298
+ joins.map! { |join| table.create_string_join(Arel.sql(join)) unless join.blank? }
299
+ joins.compact!
298
300
  joins
299
- .flatten
300
- .reject(&:blank?)
301
- .map { |join| table.create_string_join(Arel.sql(join)) }
302
301
  end
303
302
 
304
303
  def build_or_find_association(name, parent = @base, klass = nil)
305
304
  find_association(name, parent, klass) or build_association(name, parent, klass)
306
305
  end
307
306
 
308
- if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
309
-
310
- def find_association(name, parent = @base, klass = nil)
311
- @join_dependency.join_root.children.detect do |assoc|
312
- assoc.reflection.name == name &&
313
- (@associations_pot.empty? || @associations_pot[assoc] == parent) &&
314
- (!klass || assoc.reflection.klass == klass)
315
- end
307
+ def find_association(name, parent = @base, klass = nil)
308
+ @join_dependency.instance_variable_get(:@join_root).children.detect do |assoc|
309
+ assoc.reflection.name == name && assoc.table &&
310
+ (@associations_pot.empty? || @associations_pot[assoc] == parent || !@associations_pot.key?(assoc)) &&
311
+ (!klass || assoc.reflection.klass == klass)
316
312
  end
313
+ end
317
314
 
318
- def build_association(name, parent = @base, klass = nil)
319
- jd = JoinDependency.new(
315
+ def build_association(name, parent = @base, klass = nil)
316
+ if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
317
+ jd = Polyamorous::JoinDependency.new(
318
+ parent.base_klass,
319
+ parent.table,
320
+ Polyamorous::Join.new(name, @join_type, klass),
321
+ @join_type
322
+ )
323
+ found_association = jd.instance_variable_get(:@join_root).children.last
324
+ elsif ::Gem::Version.new(::ActiveRecord::VERSION::STRING) < ::Gem::Version.new(Constants::RAILS_5_2_0)
325
+ jd = Polyamorous::JoinDependency.new(
320
326
  parent.base_klass,
321
327
  Polyamorous::Join.new(name, @join_type, klass),
322
328
  []
323
329
  )
324
330
  found_association = jd.join_root.children.last
325
- @associations_pot[found_association] = parent
326
-
327
- # TODO maybe we dont need to push associations here, we could loop
328
- # through the @associations_pot instead
329
- @join_dependency.join_root.children.push found_association
330
-
331
- # Builds the arel nodes properly for this association
332
- @join_dependency.send(
333
- :construct_tables!, jd.join_root, found_association
334
- )
335
-
336
- # Leverage the stashed association functionality in AR
337
- @object = @object.joins(jd)
338
-
339
- found_association
340
- end
341
-
342
- def extract_joins(association)
343
- parent = @join_dependency.join_root
344
- reflection = association.reflection
345
- join_constraints = association.join_constraints(
331
+ elsif ::ActiveRecord::VERSION::STRING == Constants::RAILS_5_2_0
332
+ alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, parent.table.name, [])
333
+ jd = Polyamorous::JoinDependency.new(
334
+ parent.base_klass,
346
335
  parent.table,
336
+ Polyamorous::Join.new(name, @join_type, klass),
337
+ alias_tracker
338
+ )
339
+ found_association = jd.instance_variable_get(:@join_root).children.last
340
+ else
341
+ jd = Polyamorous::JoinDependency.new(
347
342
  parent.base_klass,
348
- association,
349
- Arel::Nodes::OuterJoin,
350
- association.tables,
351
- reflection.scope_chain,
352
- reflection.chain
343
+ parent.table,
344
+ Polyamorous::Join.new(name, @join_type, klass)
353
345
  )
354
- join_constraints.to_a.flatten
346
+ found_association = jd.instance_variable_get(:@join_root).children.last
355
347
  end
356
348
 
357
- else
349
+ @associations_pot[found_association] = parent
358
350
 
359
- def build_association(name, parent = @base, klass = nil)
360
- @join_dependency.send(
361
- :build,
362
- Polyamorous::Join.new(name, @join_type, klass),
363
- parent
364
- )
365
- found_association = @join_dependency.join_associations.last
366
- # Leverage the stashed association functionality in AR
367
- @object = @object.joins(found_association)
351
+ # TODO maybe we dont need to push associations here, we could loop
352
+ # through the @associations_pot instead
353
+ @join_dependency.instance_variable_get(:@join_root).children.push found_association
368
354
 
369
- found_association
370
- end
371
-
372
- def extract_joins(association)
373
- query = Arel::SelectManager.new(association.base_klass, association.table)
374
- association.join_to(query).join_sources
375
- end
376
-
377
- def find_association(name, parent = @base, klass = nil)
378
- found_association = @join_dependency.join_associations
379
- .detect do |assoc|
380
- assoc.reflection.name == name &&
381
- assoc.parent == parent &&
382
- (!klass || assoc.reflection.klass == klass)
383
- end
355
+ # Builds the arel nodes properly for this association
356
+ if ::ActiveRecord::VERSION::STRING > Constants::RAILS_5_2_0
357
+ @join_dependency.send(:construct_tables!, jd.instance_variable_get(:@join_root))
358
+ else
359
+ @join_dependency.send(:construct_tables!, jd.instance_variable_get(:@join_root), found_association)
384
360
  end
385
361
 
362
+ # Leverage the stashed association functionality in AR
363
+ @object = @object.joins(jd)
364
+ found_association
386
365
  end
387
366
 
367
+ def extract_joins(association)
368
+ parent = @join_dependency.instance_variable_get(:@join_root)
369
+ reflection = association.reflection
370
+ join_constraints = if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_1
371
+ association.join_constraints(
372
+ parent.table,
373
+ parent.base_klass,
374
+ association,
375
+ Arel::Nodes::OuterJoin,
376
+ association.tables,
377
+ reflection.scope_chain,
378
+ reflection.chain
379
+ )
380
+ elsif ::ActiveRecord::VERSION::STRING <= Constants::RAILS_5_2_0
381
+ association.join_constraints(
382
+ parent.table,
383
+ parent.base_klass,
384
+ Arel::Nodes::OuterJoin,
385
+ association.tables,
386
+ reflection.chain
387
+ )
388
+ else
389
+ association.join_constraints(
390
+ parent.table,
391
+ parent.base_klass,
392
+ Arel::Nodes::OuterJoin,
393
+ @join_dependency.instance_variable_get(:@alias_tracker)
394
+ )
395
+ end
396
+ join_constraints.to_a.flatten
397
+ end
388
398
  end
389
399
  end
390
400
  end
@@ -102,9 +102,12 @@ module Ransack
102
102
  # replace % \ to \% \\
103
103
  def escape_wildcards(unescaped)
104
104
  case ActiveRecord::Base.connection.adapter_name
105
- when "Mysql2".freeze, "PostgreSQL".freeze
106
- # Necessary for PostgreSQL and MySQL
107
- unescaped.to_s.gsub(/([\\|\%|_|.])/, '\\\\\\1')
105
+ when "Mysql2".freeze
106
+ # Necessary for MySQL
107
+ unescaped.to_s.gsub(/([\\%_])/, '\\\\\\1')
108
+ when "PostgreSQL".freeze
109
+ # Necessary for PostgreSQL
110
+ unescaped.to_s.gsub(/([\\%_.])/, '\\\\\\1')
108
111
  else
109
112
  unescaped
110
113
  end
@@ -30,22 +30,20 @@ module Ransack
30
30
  @associations_pot = {}
31
31
  @lock_associations = []
32
32
 
33
- if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
33
+ if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_5_2
34
+ @base = @join_dependency.instance_variable_get(:@join_root)
35
+ else
34
36
  @base = @join_dependency.join_root
35
37
  @engine = @base.base_klass.arel_engine
36
- else
37
- @base = @join_dependency.join_base
38
- @engine = @base.arel_engine
39
38
  end
39
+ end
40
40
 
41
- @default_table = Arel::Table.new(
42
- @base.table_name, as: @base.aliased_table_name, type_caster: self
43
- )
44
- @bind_pairs = Hash.new do |hash, key|
45
- parent, attr_name = get_parent_and_attribute_name(key)
46
- if parent && attr_name
47
- hash[key] = [parent, attr_name]
48
- end
41
+ def bind_pair_for(key)
42
+ @bind_pairs ||= {}
43
+
44
+ @bind_pairs[key] ||= begin
45
+ parent, attr_name = get_parent_and_attribute_name(key.to_s)
46
+ [parent, attr_name] if parent && attr_name
49
47
  end
50
48
  end
51
49
 
@@ -54,10 +52,6 @@ module Ransack
54
52
  obj
55
53
  elsif obj.respond_to? :klass
56
54
  obj.klass
57
- elsif obj.respond_to? :active_record # Rails 3
58
- obj.active_record
59
- elsif obj.respond_to? :base_klass # Rails 4
60
- obj.base_klass
61
55
  else
62
56
  raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
63
57
  end
@@ -33,8 +33,8 @@ module Ransack
33
33
  predicate = attribute.attr.public_send(arel_pred, arel_values)
34
34
 
35
35
  if in_predicate?(predicate)
36
- predicate.right = predicate.right.map do |predicate|
37
- casted_array?(predicate) ? format_values_for(predicate) : predicate
36
+ predicate.right = predicate.right.map do |pr|
37
+ casted_array?(pr) ? format_values_for(pr) : pr
38
38
  end
39
39
  end
40
40
 
@@ -43,7 +43,7 @@ module Ransack
43
43
 
44
44
  def in_predicate?(predicate)
45
45
  return unless defined?(Arel::Nodes::Casted)
46
- predicate.class == Arel::Nodes::In
46
+ predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
47
47
  end
48
48
 
49
49
  def casted_array?(predicate)
@@ -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
@@ -12,12 +12,3 @@ ActiveSupport.on_load(:active_record) do
12
12
  end
13
13
 
14
14
  require 'ransack/adapters/active_record/context'
15
-
16
- case ActiveRecord::VERSION::STRING
17
- when /^3\.0\./
18
- require 'ransack/adapters/active_record/3.0/context'
19
- when /^3\.1\./
20
- require 'ransack/adapters/active_record/3.1/context'
21
- when /^3\.2\./
22
- require 'ransack/adapters/active_record/3.2/context'
23
- end
@@ -10,6 +10,8 @@ module Ransack
10
10
  ActiveRecordAdapter.new
11
11
  elsif defined?(::Mongoid)
12
12
  MongoidAdapter.new
13
+ else
14
+ raise "Unsupported adapter"
13
15
  end
14
16
  end
15
17