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
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