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
data/.travis.yml DELETED
@@ -1,69 +0,0 @@
1
- services: mongodb
2
-
3
- language: ruby
4
-
5
- sudo: false
6
-
7
- rvm:
8
- - 2.2.3
9
- - 2.1
10
- - 2.0
11
- - 1.9
12
-
13
- env:
14
- - RAILS=4-2-stable DB=mongodb
15
- - RAILS=4-2-stable DB=sqlite3
16
- - RAILS=4-2-stable DB=mysql
17
- - RAILS=4-2-stable DB=postgres
18
-
19
- - RAILS=4-1-stable DB=mongodb
20
- - RAILS=4-1-stable DB=sqlite3
21
- - RAILS=4-1-stable DB=mysql
22
- - RAILS=4-1-stable DB=postgres
23
-
24
- - RAILS=4-0-stable DB=sqlite3
25
- - RAILS=4-0-stable DB=mysql
26
- - RAILS=4-0-stable DB=postgres
27
-
28
- - RAILS=3-2-stable DB=sqlite
29
- - RAILS=3-2-stable DB=mysql
30
- - RAILS=3-2-stable DB=postgres
31
-
32
- - RAILS=3-1-stable DB=sqlite
33
- - RAILS=3-1-stable DB=mysql
34
- - RAILS=3-1-stable DB=postgres
35
-
36
- matrix:
37
- include:
38
- - rvm: 2.2.3
39
- env: RAILS=master DB=sqlite3
40
- - rvm: 2.2.3
41
- env: RAILS=master DB=mysql
42
- - rvm: 2.2.3
43
- env: RAILS=master DB=postgres
44
- exclude:
45
- - rvm: 2.2.3
46
- env: RAILS=3-1-stable DB=sqlite
47
- - rvm: 2.2.3
48
- env: RAILS=3-1-stable DB=mysql
49
- - rvm: 2.2.3
50
- env: RAILS=3-1-stable DB=postgres
51
- allow_failures:
52
- - env: RAILS=master DB=sqlite3
53
- - env: RAILS=master DB=mysql
54
- - env: RAILS=master DB=postgres
55
- - rvm: 2.2.3
56
- env: RAILS=3-2-stable DB=sqlite
57
- - rvm: 2.2.3
58
- env: RAILS=3-2-stable DB=mysql
59
- - rvm: 2.2.3
60
- env: RAILS=3-2-stable DB=postgres
61
-
62
- before_script:
63
- - mysql -e 'create database ransack collate utf8_general_ci;'
64
- - mysql -e 'use ransack;show variables like "%character%";show variables like "%collation%";'
65
- - psql -c 'create database ransack;' -U postgres
66
-
67
- addons:
68
- code_climate:
69
- repo_token: 8b701c4364d51a0217105e08c06922d600cec3d9e60d546a89e3ddfe46e0664e
@@ -1,179 +0,0 @@
1
- # UGLY, UGLY MONKEY PATCHES FOR BACKWARDS COMPAT!!! AVERT YOUR EYES!!
2
- if Arel::Nodes::And < Arel::Nodes::Binary
3
- class Ransack::Visitor
4
- def visit_Ransack_Nodes_And(object)
5
- nodes = object.values.map { |o| accept(o) }.compact
6
- return nil unless nodes.size > 0
7
-
8
- if nodes.size > 1
9
- nodes.inject(&:and)
10
- else
11
- nodes.first
12
- end
13
- end
14
- end
15
- end
16
-
17
- class ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase
18
- def table
19
- Arel::Table.new(
20
- table_name,
21
- :as => aliased_table_name,
22
- :engine => active_record.arel_engine,
23
- :columns => active_record.columns
24
- )
25
- end
26
- end
27
-
28
- module Arel
29
-
30
- class Table
31
- alias :table_name :name
32
-
33
- def [] name
34
- ::Arel::Attribute.new self, name.to_sym
35
- end
36
- end
37
-
38
- module Nodes
39
- class Node
40
- def not
41
- Nodes::Not.new self
42
- end
43
- end
44
-
45
- remove_const :And
46
- class And < Arel::Nodes::Node
47
- attr_reader :children
48
-
49
- def initialize children, right = nil
50
- unless Array === children
51
- children = [children, right]
52
- end
53
- @children = children
54
- end
55
-
56
- def left
57
- children.first
58
- end
59
-
60
- def right
61
- children[1]
62
- end
63
- end
64
-
65
- class NamedFunction < Arel::Nodes::Function
66
- attr_accessor :name, :distinct
67
-
68
- include Arel::Predications
69
-
70
- def initialize name, expr, aliaz = nil
71
- super(expr, aliaz)
72
- @name = name
73
- @distinct = false
74
- end
75
- end
76
-
77
- class InfixOperation < Binary
78
- include Arel::Expressions
79
- include Arel::Predications
80
-
81
- attr_reader :operator
82
-
83
- def initialize operator, left, right
84
- super(left, right)
85
- @operator = operator
86
- end
87
- end
88
-
89
- class Multiplication < InfixOperation
90
- def initialize left, right
91
- super(:*, left, right)
92
- end
93
- end
94
-
95
- class Division < InfixOperation
96
- def initialize left, right
97
- super(:/, left, right)
98
- end
99
- end
100
-
101
- class Addition < InfixOperation
102
- def initialize left, right
103
- super(:+, left, right)
104
- end
105
- end
106
-
107
- class Subtraction < InfixOperation
108
- def initialize left, right
109
- super(:-, left, right)
110
- end
111
- end
112
- end
113
-
114
- module Visitors
115
- class ToSql
116
- def column_for attr
117
- name = attr.name.to_s
118
- table = attr.relation.table_name
119
-
120
- column_cache[table][name]
121
- end
122
-
123
- def column_cache
124
- @column_cache ||= Hash.new do |hash, key|
125
- hash[key] = Hash[
126
- @engine.connection
127
- .columns(key, "#{key} Columns")
128
- .map { |c| [c.name, c] }
129
- ]
130
- end
131
- end
132
-
133
- def visit_Arel_Nodes_InfixOperation o
134
- "#{visit o.left} #{o.operator} #{visit o.right}"
135
- end
136
-
137
- def visit_Arel_Nodes_NamedFunction o
138
- "#{
139
- o.name
140
- }(#{
141
- o.distinct ? Ransack::Constants::DISTINCT : Ransack::Constants::EMPTY
142
- }#{
143
- o.expressions.map { |x| visit x }.join(Ransack::Constants::COMMA_SPACE)
144
- })#{
145
- o.alias ? " AS #{visit o.alias}" : Ransack::Constants::EMPTY
146
- }"
147
- end
148
-
149
- def visit_Arel_Nodes_And o
150
- o.children.map { |x| visit x }.join(Ransack::Constants::SPACED_AND)
151
- end
152
-
153
- def visit_Arel_Nodes_Not o
154
- "NOT (#{visit o.expr})"
155
- end
156
-
157
- def visit_Arel_Nodes_Values o
158
- "VALUES (#{
159
- o.expressions.zip(o.columns)
160
- .map { |value, attr|
161
- if Nodes::SqlLiteral === value
162
- visit_Arel_Nodes_SqlLiteral value
163
- else
164
- quote(value, attr && column_for(attr))
165
- end
166
- }
167
- .join(Ransack::Constants::COMMA_SPACE)
168
- })"
169
- end
170
- end
171
- end
172
-
173
- module Predications
174
- def as other
175
- Nodes::As.new self, Nodes::SqlLiteral.new(other)
176
- end
177
- end
178
-
179
- end
@@ -1,201 +0,0 @@
1
- require 'ransack/context'
2
- require 'polyamorous'
3
- require 'ransack/adapters/active_record/3.0/compat'
4
-
5
- module Ransack
6
-
7
- module Adapters
8
- module ActiveRecord
9
- class Context < ::Ransack::Context
10
- # Because the AR::Associations namespace is insane
11
- JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
12
- JoinBase = JoinDependency::JoinBase
13
-
14
- # Redefine a few things for ActiveRecord 3.0.
15
-
16
- def initialize(object, options = {})
17
- super
18
- @arel_visitor = Arel::Visitors.visitor_for @engine
19
- end
20
-
21
- def relation_for(object)
22
- object.scoped
23
- end
24
-
25
- def evaluate(search, opts = {})
26
- viz = Visitor.new
27
- relation = @object.where(viz.accept(search.base))
28
- if search.sorts.any?
29
- relation = relation.except(:order)
30
- .reorder(viz.accept(search.sorts))
31
- end
32
- if opts[:distinct]
33
- relation.select(Constants::DISTINCT + @klass.quoted_table_name +
34
- Constants::DOT_ASTERIX)
35
- else
36
- relation
37
- end
38
- end
39
-
40
- def attribute_method?(str, klass = @klass)
41
- exists = false
42
-
43
- if ransackable_attribute?(str, klass)
44
- exists = true
45
- elsif (segments = str.split(/_/)).size > 1
46
- remainder = []
47
- found_assoc = nil
48
- while !found_assoc && remainder.unshift(segments.pop) &&
49
- segments.size > 0 do
50
- assoc, poly_class = unpolymorphize_association(
51
- segments.join(Constants::UNDERSCORE)
52
- )
53
- if found_assoc = get_association(assoc, klass)
54
- exists = attribute_method?(
55
- remainder.join(Constants::UNDERSCORE),
56
- poly_class || found_assoc.klass
57
- )
58
- end
59
- end
60
- end
61
-
62
- exists
63
- end
64
-
65
- def table_for(parent)
66
- parent.table
67
- end
68
-
69
- def type_for(attr)
70
- return nil unless attr && attr.valid?
71
- klassify(attr.parent)
72
- .columns_hash[attr.arel_attribute.name.to_s]
73
- .type
74
- end
75
-
76
- # All dependent JoinAssociation items used in the search query
77
- #
78
- def join_associations
79
- @join_dependency.join_associations
80
- end
81
-
82
- def join_sources
83
- raise NotImplementedError,
84
- "ActiveRecord 3.0 does not use join_sources or support joining relations with Arel::Join nodes. Use join_associations."
85
- end
86
-
87
- def alias_tracker
88
- raise NotImplementedError,
89
- "ActiveRecord 3.0 does not have an alias tracker"
90
- end
91
-
92
- private
93
-
94
- def get_parent_and_attribute_name(str, parent = @base)
95
- attr_name = nil
96
-
97
- if ransackable_attribute?(str, klassify(parent))
98
- attr_name = str
99
- elsif (segments = str.split(/_/)).size > 1
100
- remainder = []
101
- found_assoc = nil
102
- while remainder.unshift(segments.pop) && segments.size > 0 &&
103
- !found_assoc do
104
- assoc, klass = unpolymorphize_association(
105
- segments.join(Constants::UNDERSCORE)
106
- )
107
- if found_assoc = get_association(assoc, parent)
108
- join = build_or_find_association(
109
- found_assoc.name, parent, klass
110
- )
111
- parent, attr_name = get_parent_and_attribute_name(
112
- remainder.join(Constants::UNDERSCORE), join
113
- )
114
- end
115
- end
116
- end
117
- [parent, attr_name]
118
- end
119
-
120
- def get_association(str, parent = @base)
121
- klass = klassify parent
122
- ransackable_association?(str, klass) &&
123
- klass.reflect_on_all_associations.detect { |a| a.name.to_s == str }
124
- end
125
-
126
- def join_dependency(relation)
127
- if relation.respond_to?(:join_dependency) # Squeel will enable this
128
- relation.join_dependency
129
- else
130
- build_join_dependency(relation)
131
- end
132
- end
133
-
134
- def build_join_dependency(relation)
135
- buckets = relation.joins_values.group_by do |join|
136
- case join
137
- when String
138
- Constants::STRING_JOIN
139
- when Hash, Symbol, Array
140
- Constants::ASSOCIATION_JOIN
141
- when ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
142
- Constants::STASHED_JOIN
143
- when Arel::Nodes::Join
144
- Constants::JOIN_NODE
145
- else
146
- raise 'unknown class: %s' % join.class.name
147
- end
148
- end
149
-
150
- association_joins = buckets[Constants::ASSOCIATION_JOIN] || []
151
-
152
- stashed_association_joins = buckets[Constants::STASHED_JOIN] || []
153
-
154
- join_nodes = buckets[Constants::JOIN_NODE] || []
155
-
156
- string_joins = (buckets[Constants::STRING_JOIN] || []).map(&:strip).uniq
157
-
158
- join_list = relation.send :custom_join_sql, (string_joins + join_nodes)
159
-
160
- join_dependency = JoinDependency.new(
161
- relation.klass,
162
- association_joins,
163
- join_list
164
- )
165
-
166
- join_nodes.each do |join|
167
- join_dependency.table_aliases[join.left.name.downcase] = 1
168
- end
169
-
170
- join_dependency.graft(*stashed_association_joins)
171
- end
172
-
173
- def build_or_find_association(name, parent = @base, klass = nil)
174
- found_association = @join_dependency.join_associations
175
- .detect do |assoc|
176
- assoc.reflection.name == name &&
177
- assoc.parent == parent &&
178
- (!klass || assoc.reflection.klass == klass)
179
- end
180
- unless found_association
181
- @join_dependency.send(
182
- :build, Polyamorous::Join.new(name, @join_type, klass), parent
183
- )
184
- found_association = @join_dependency.join_associations.last
185
-
186
- default_conditions = found_association.active_record.scoped.arel.constraints
187
- if default_conditions.any?
188
- and_default_conditions = "AND #{default_conditions.reduce(&:and).to_sql}"
189
- end
190
-
191
- # Leverage the stashed association functionality in AR
192
- @object = @object.joins(found_association).joins(and_default_conditions)
193
- end
194
-
195
- found_association
196
- end
197
-
198
- end
199
- end
200
- end
201
- end
@@ -1,215 +0,0 @@
1
- require 'ransack/context'
2
- require 'ransack/adapters/active_record/compat'
3
- require 'polyamorous'
4
-
5
- module Ransack
6
- module Adapters
7
- module ActiveRecord
8
- class Context < ::Ransack::Context
9
- # Because the AR::Associations namespace is insane
10
- JoinDependency = ::ActiveRecord::Associations::JoinDependency
11
- JoinPart = JoinDependency::JoinPart
12
-
13
- # Redefine a few things for ActiveRecord 3.1.
14
-
15
- def initialize(object, options = {})
16
- super
17
- @arel_visitor = Arel::Visitors.visitor_for @engine
18
- end
19
-
20
- def relation_for(object)
21
- object.scoped
22
- end
23
-
24
- def evaluate(search, opts = {})
25
- viz = Visitor.new
26
- relation = @object.where(viz.accept(search.base))
27
- if search.sorts.any?
28
- relation = relation.except(:order)
29
- .reorder(viz.accept(search.sorts))
30
- end
31
- if opts[:distinct]
32
- relation.select(Constants::DISTINCT + @klass.quoted_table_name +
33
- Constants::DOT_ASTERIX)
34
- else
35
- relation
36
- end
37
- end
38
-
39
- def attribute_method?(str, klass = @klass)
40
- exists = false
41
-
42
- if ransackable_attribute?(str, klass)
43
- exists = true
44
- elsif (segments = str.split(/_/)).size > 1
45
- remainder = []
46
- found_assoc = nil
47
- while !found_assoc && remainder.unshift(segments.pop) &&
48
- segments.size > 0 do
49
- assoc, poly_class = unpolymorphize_association(
50
- segments.join(Constants::UNDERSCORE)
51
- )
52
- if found_assoc = get_association(assoc, klass)
53
- exists = attribute_method?(
54
- remainder.join(Constants::UNDERSCORE),
55
- poly_class || found_assoc.klass
56
- )
57
- end
58
- end
59
- end
60
-
61
- exists
62
- end
63
-
64
- def table_for(parent)
65
- parent.table
66
- end
67
-
68
- def type_for(attr)
69
- return nil unless attr && attr.valid?
70
- name = attr.arel_attribute.name.to_s
71
- table = attr.arel_attribute.relation.table_name
72
-
73
- unless @engine.connection_pool.table_exists?(table)
74
- raise "No table named #{table} exists"
75
- end
76
-
77
- @engine.connection_pool.columns_hash[table][name].type
78
- end
79
-
80
- def join_associations
81
- @join_dependency.join_associations
82
- end
83
-
84
- # All dependent Arel::Join nodes used in the search query
85
- #
86
- # This could otherwise be done as `@object.arel.join_sources`, except
87
- # that ActiveRecord's build_joins sets up its own JoinDependency.
88
- # This extracts what we need to access the joins using our existing
89
- # JoinDependency to track table aliases.
90
- #
91
- def join_sources
92
- base = Arel::SelectManager.new(@object.engine, @object.table)
93
- @object.joins_values.each do |assoc|
94
- next unless assoc.is_a?(JoinDependency::JoinAssociation)
95
- assoc.join_to(base)
96
- end
97
- base.join_sources
98
- end
99
-
100
- def alias_tracker
101
- @join_dependency.alias_tracker
102
- end
103
-
104
- private
105
-
106
- def get_parent_and_attribute_name(str, parent = @base)
107
- attr_name = nil
108
-
109
- if ransackable_attribute?(str, klassify(parent))
110
- attr_name = str
111
- elsif (segments = str.split(/_/)).size > 1
112
- remainder = []
113
- found_assoc = nil
114
- while remainder.unshift(segments.pop) && segments.size > 0 &&
115
- !found_assoc do
116
- assoc, klass = unpolymorphize_association(
117
- segments.join(Constants::UNDERSCORE)
118
- )
119
- if found_assoc = get_association(assoc, parent)
120
- join = build_or_find_association(
121
- found_assoc.name, parent, klass
122
- )
123
- parent, attr_name = get_parent_and_attribute_name(
124
- remainder.join(Constants::UNDERSCORE), join
125
- )
126
- end
127
- end
128
- end
129
-
130
- [parent, attr_name]
131
- end
132
-
133
- def get_association(str, parent = @base)
134
- klass = klassify parent
135
- ransackable_association?(str, klass) &&
136
- klass.reflect_on_all_associations.detect { |a| a.name.to_s == str }
137
- end
138
-
139
- def join_dependency(relation)
140
- if relation.respond_to?(:join_dependency) # Squeel will enable this
141
- relation.join_dependency
142
- else
143
- build_join_dependency(relation)
144
- end
145
- end
146
-
147
- def build_join_dependency(relation)
148
- buckets = relation.joins_values.group_by do |join|
149
- case join
150
- when String
151
- Constants::STRING_JOIN
152
- when Hash, Symbol, Array
153
- Constants::ASSOCIATION_JOIN
154
- when JoinDependency::JoinAssociation
155
- Constants::STASHED_JOIN
156
- when Arel::Nodes::Join
157
- Constants::JOIN_NODE
158
- else
159
- raise 'unknown class: %s' % join.class.name
160
- end
161
- end
162
-
163
- association_joins = buckets[Constants::ASSOCIATION_JOIN] || []
164
-
165
- stashed_association_joins = buckets[Constants::STASHED_JOIN] || []
166
-
167
- join_nodes = buckets[Constants::JOIN_NODE] || []
168
-
169
- string_joins = (buckets[Constants::STRING_JOIN] || []).map(&:strip).uniq
170
-
171
- join_list = relation.send :custom_join_ast,
172
- relation.table.from(relation.table), string_joins
173
-
174
- join_dependency = JoinDependency.new(
175
- relation.klass,
176
- association_joins,
177
- join_list
178
- )
179
-
180
- join_nodes.each do |join|
181
- join_dependency.alias_tracker.aliases[join.left.name.downcase] = 1
182
- end
183
-
184
- join_dependency.graft(*stashed_association_joins)
185
- end
186
-
187
- def build_or_find_association(name, parent = @base, klass = nil)
188
- found_association = @join_dependency.join_associations
189
- .detect do |assoc|
190
- assoc.reflection.name == name &&
191
- assoc.parent == parent &&
192
- (!klass || assoc.reflection.klass == klass)
193
- end
194
- unless found_association
195
- @join_dependency.send(
196
- :build, Polyamorous::Join.new(name, @join_type, klass), parent
197
- )
198
- found_association = @join_dependency.join_associations.last
199
-
200
- default_conditions = found_association.active_record.scoped.arel.constraints
201
- if default_conditions.any?
202
- and_default_conditions = "AND #{default_conditions.reduce(&:and).to_sql}"
203
- end
204
-
205
- # Leverage the stashed association functionality in AR
206
- @object = @object.joins(found_association).joins(and_default_conditions)
207
- end
208
-
209
- found_association
210
- end
211
-
212
- end
213
- end
214
- end
215
- end
@@ -1,44 +0,0 @@
1
- require 'ransack/context'
2
- require 'ransack/adapters/active_record/3.1/context'
3
- require 'ransack/adapters/active_record/compat'
4
- require 'polyamorous'
5
-
6
- module Ransack
7
- module Adapters
8
- module ActiveRecord
9
- class Context < ::Ransack::Context
10
-
11
- # Redefine a few things for ActiveRecord 3.2.
12
-
13
- def initialize(object, options = {})
14
- super
15
- @arel_visitor = @engine.connection.visitor
16
- end
17
-
18
- def relation_for(object)
19
- object.scoped
20
- end
21
-
22
- def type_for(attr)
23
- return nil unless attr && attr.valid?
24
- name = attr.arel_attribute.name.to_s
25
- table = attr.arel_attribute.relation.table_name
26
-
27
- schema_cache = @engine.connection.schema_cache
28
- raise "No table named #{table} exists" unless schema_cache.table_exists?(table)
29
- schema_cache.columns_hash[table][name].type
30
- end
31
-
32
- def evaluate(search, opts = {})
33
- viz = Visitor.new
34
- relation = @object.where(viz.accept(search.base))
35
- if search.sorts.any?
36
- relation = relation.except(:order).reorder(viz.accept(search.sorts))
37
- end
38
- opts[:distinct] ? relation.uniq : relation
39
- end
40
-
41
- end
42
- end
43
- end
44
- end