ransack 1.8.4 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) 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/cronjob.yml +102 -0
  5. data/.github/workflows/deploy.yml +35 -0
  6. data/.github/workflows/rubocop.yml +20 -0
  7. data/.github/workflows/test-deploy.yml +29 -0
  8. data/.github/workflows/test.yml +130 -0
  9. data/.gitignore +3 -0
  10. data/{lib/ransack/adapters/mongoid/3.2/.gitkeep → .nojekyll} +0 -0
  11. data/.rubocop.yml +44 -0
  12. data/CHANGELOG.md +352 -0
  13. data/CONTRIBUTING.md +25 -13
  14. data/Gemfile +26 -27
  15. data/README.md +65 -815
  16. data/Rakefile +1 -22
  17. data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
  18. data/bug_report_templates/test-ransacker-arel-present-predicate.rb +71 -0
  19. data/docs/.gitignore +19 -0
  20. data/docs/.nojekyll +0 -0
  21. data/docs/babel.config.js +3 -0
  22. data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
  23. data/docs/docs/getting-started/_category_.json +4 -0
  24. data/docs/docs/getting-started/advanced-mode.md +46 -0
  25. data/docs/docs/getting-started/configuration.md +47 -0
  26. data/docs/docs/getting-started/search-matches.md +67 -0
  27. data/docs/docs/getting-started/simple-mode.md +284 -0
  28. data/docs/docs/getting-started/sorting.md +79 -0
  29. data/docs/docs/getting-started/using-predicates.md +282 -0
  30. data/docs/docs/going-further/_category_.json +4 -0
  31. data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
  32. data/docs/docs/going-further/associations.md +70 -0
  33. data/docs/docs/going-further/custom-predicates.md +52 -0
  34. data/docs/docs/going-further/documentation.md +43 -0
  35. data/docs/docs/going-further/exporting-to-csv.md +49 -0
  36. data/docs/docs/going-further/external-guides.md +57 -0
  37. data/docs/docs/going-further/form-customisation.md +63 -0
  38. data/docs/docs/going-further/i18n.md +53 -0
  39. data/docs/docs/going-further/img/create_release.png +0 -0
  40. data/docs/docs/going-further/merging-searches.md +41 -0
  41. data/docs/docs/going-further/other-notes.md +428 -0
  42. data/docs/docs/going-further/polymorphic-search.md +40 -0
  43. data/docs/docs/going-further/ransackers.md +331 -0
  44. data/docs/docs/going-further/release_process.md +36 -0
  45. data/docs/docs/going-further/saving-queries.md +82 -0
  46. data/docs/docs/going-further/searching-postgres.md +57 -0
  47. data/docs/docs/going-further/wiki-contributors.md +82 -0
  48. data/docs/docs/intro.md +99 -0
  49. data/docs/docusaurus.config.js +120 -0
  50. data/docs/package.json +38 -0
  51. data/docs/sidebars.js +31 -0
  52. data/docs/src/components/HomepageFeatures/index.js +64 -0
  53. data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  54. data/docs/src/css/custom.css +39 -0
  55. data/docs/src/pages/index.module.css +23 -0
  56. data/docs/src/pages/markdown-page.md +7 -0
  57. data/docs/static/.nojekyll +0 -0
  58. data/docs/static/img/docusaurus.png +0 -0
  59. data/docs/static/img/favicon.ico +0 -0
  60. data/docs/static/img/logo.svg +1 -0
  61. data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
  62. data/docs/static/img/tutorial/localeDropdown.png +0 -0
  63. data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  64. data/docs/static/img/undraw_docusaurus_react.svg +170 -0
  65. data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  66. data/docs/static/logo/ransack-h.png +0 -0
  67. data/docs/static/logo/ransack-h.svg +34 -0
  68. data/docs/static/logo/ransack-v.png +0 -0
  69. data/docs/static/logo/ransack-v.svg +34 -0
  70. data/docs/static/logo/ransack.png +0 -0
  71. data/docs/static/logo/ransack.svg +21 -0
  72. data/docs/yarn.lock +8436 -0
  73. data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +70 -0
  74. data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +92 -0
  75. data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -0
  76. data/lib/polyamorous/activerecord_7.0_ruby_2/join_association.rb +1 -0
  77. data/lib/polyamorous/activerecord_7.0_ruby_2/join_dependency.rb +1 -0
  78. data/lib/polyamorous/activerecord_7.0_ruby_2/reflection.rb +1 -0
  79. data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
  80. data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
  81. data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
  82. data/lib/polyamorous/join.rb +70 -0
  83. data/lib/polyamorous/polyamorous.rb +24 -0
  84. data/lib/polyamorous/swapping_reflection_class.rb +11 -0
  85. data/lib/polyamorous/tree_node.rb +7 -0
  86. data/lib/polyamorous.rb +1 -0
  87. data/lib/ransack/adapters/active_record/base.rb +14 -3
  88. data/lib/ransack/adapters/active_record/context.rb +140 -196
  89. data/lib/ransack/adapters/active_record/ransack/constants.rb +19 -4
  90. data/lib/ransack/adapters/active_record/ransack/context.rb +9 -19
  91. data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +7 -7
  92. data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
  93. data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
  94. data/lib/ransack/adapters/active_record.rb +0 -9
  95. data/lib/ransack/adapters.rb +2 -0
  96. data/lib/ransack/configuration.rb +52 -2
  97. data/lib/ransack/constants.rb +1 -5
  98. data/lib/ransack/context.rb +29 -24
  99. data/lib/ransack/helpers/form_builder.rb +12 -6
  100. data/lib/ransack/helpers/form_helper.rb +11 -3
  101. data/lib/ransack/helpers.rb +1 -1
  102. data/lib/ransack/locale/ar.yml +70 -0
  103. data/lib/ransack/locale/az.yml +70 -0
  104. data/lib/ransack/locale/bg.yml +70 -0
  105. data/lib/ransack/locale/ca.yml +70 -0
  106. data/lib/ransack/locale/el.yml +70 -0
  107. data/lib/ransack/locale/es.yml +22 -22
  108. data/lib/ransack/locale/fa.yml +70 -0
  109. data/lib/ransack/locale/fi.yml +71 -0
  110. data/lib/ransack/locale/nl.yml +4 -4
  111. data/lib/ransack/locale/ru.yml +70 -0
  112. data/lib/ransack/locale/sk.yml +70 -0
  113. data/lib/ransack/locale/sv.yml +70 -0
  114. data/lib/ransack/locale/tr.yml +70 -0
  115. data/lib/ransack/locale/zh-CN.yml +12 -12
  116. data/lib/ransack/nodes/attribute.rb +2 -2
  117. data/lib/ransack/nodes/condition.rb +7 -1
  118. data/lib/ransack/nodes/grouping.rb +3 -8
  119. data/lib/ransack/nodes/sort.rb +3 -3
  120. data/lib/ransack/nodes/value.rb +3 -3
  121. data/lib/ransack/predicate.rb +13 -20
  122. data/lib/ransack/search.rb +7 -4
  123. data/lib/ransack/translate.rb +115 -115
  124. data/lib/ransack/version.rb +1 -1
  125. data/lib/ransack/visitor.rb +1 -12
  126. data/lib/ransack.rb +7 -5
  127. data/ransack.gemspec +9 -25
  128. data/spec/blueprints/articles.rb +1 -1
  129. data/spec/blueprints/comments.rb +1 -1
  130. data/spec/blueprints/notes.rb +1 -1
  131. data/spec/blueprints/tags.rb +1 -1
  132. data/spec/console.rb +5 -5
  133. data/spec/helpers/polyamorous_helper.rb +13 -0
  134. data/spec/helpers/ransack_helper.rb +1 -1
  135. data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
  136. data/spec/polyamorous/join_association_spec.rb +30 -0
  137. data/spec/polyamorous/join_dependency_spec.rb +81 -0
  138. data/spec/polyamorous/join_spec.rb +19 -0
  139. data/spec/ransack/adapters/active_record/base_spec.rb +105 -11
  140. data/spec/ransack/adapters/active_record/context_spec.rb +63 -24
  141. data/spec/ransack/configuration_spec.rb +24 -0
  142. data/spec/ransack/helpers/form_builder_spec.rb +3 -15
  143. data/spec/ransack/helpers/form_helper_spec.rb +135 -168
  144. data/spec/ransack/nodes/condition_spec.rb +13 -0
  145. data/spec/ransack/nodes/grouping_spec.rb +2 -2
  146. data/spec/ransack/nodes/value_spec.rb +115 -0
  147. data/spec/ransack/predicate_spec.rb +54 -2
  148. data/spec/ransack/search_spec.rb +266 -36
  149. data/spec/spec_helper.rb +14 -5
  150. data/spec/support/schema.rb +99 -21
  151. metadata +117 -187
  152. data/.travis.yml +0 -86
  153. data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
  154. data/lib/ransack/adapters/active_record/3.0/context.rb +0 -203
  155. data/lib/ransack/adapters/active_record/3.1/context.rb +0 -212
  156. data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
  157. data/lib/ransack/adapters/active_record/compat.rb +0 -14
  158. data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
  159. data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
  160. data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
  161. data/lib/ransack/adapters/mongoid/base.rb +0 -134
  162. data/lib/ransack/adapters/mongoid/context.rb +0 -212
  163. data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
  164. data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
  165. data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
  166. data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
  167. data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
  168. data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
  169. data/lib/ransack/adapters/mongoid/table.rb +0 -35
  170. data/lib/ransack/adapters/mongoid.rb +0 -15
  171. data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -314
  172. data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
  173. data/spec/mongoid/configuration_spec.rb +0 -162
  174. data/spec/mongoid/dependencies_spec.rb +0 -8
  175. data/spec/mongoid/helpers/ransack_helper.rb +0 -11
  176. data/spec/mongoid/nodes/condition_spec.rb +0 -49
  177. data/spec/mongoid/nodes/grouping_spec.rb +0 -13
  178. data/spec/mongoid/predicate_spec.rb +0 -155
  179. data/spec/mongoid/search_spec.rb +0 -445
  180. data/spec/mongoid/support/mongoid.yml +0 -11
  181. data/spec/mongoid/support/schema.rb +0 -135
  182. data/spec/mongoid/translate_spec.rb +0 -14
  183. data/spec/mongoid_spec_helper.rb +0 -63
  184. data/spec/ransack/dependencies_spec.rb +0 -12
data/.travis.yml DELETED
@@ -1,86 +0,0 @@
1
- language: ruby
2
-
3
- sudo: false
4
-
5
- rvm:
6
- - 2.3.3
7
- - 2.2.6
8
- - 2.1.10
9
- - 2.0
10
-
11
- env:
12
- - RAILS=5-0-stable DB=sqlite3
13
- - RAILS=5-0-stable DB=mysql
14
- - RAILS=5-0-stable DB=postgres
15
-
16
- - RAILS=4-2-stable DB=sqlite3
17
- - RAILS=4-2-stable DB=mysql
18
- - RAILS=4-2-stable DB=postgres
19
-
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
- exclude:
38
- - rvm: 2.1.10
39
- env: RAILS=5-0-stable DB=sqlite3
40
- - rvm: 2.1.10
41
- env: RAILS=5-0-stable DB=mysql
42
- - rvm: 2.1.10
43
- env: RAILS=5-0-stable DB=postgres
44
-
45
- - rvm: 2.0
46
- env: RAILS=5-0-stable DB=sqlite3
47
- - rvm: 2.0
48
- env: RAILS=5-0-stable DB=mysql
49
- - rvm: 2.0
50
- env: RAILS=5-0-stable DB=postgres
51
-
52
- - rvm: 2.0
53
- env: RAILS=4-2-stable DB=sqlite3
54
- - rvm: 2.0
55
- env: RAILS=4-2-stable DB=mysql
56
- - rvm: 2.0
57
- env: RAILS=4-2-stable DB=postgres
58
-
59
- include:
60
- - rvm: 2.3.3
61
- env: RAILS=master DB=sqlite3
62
- - rvm: 2.3.3
63
- env: RAILS=master DB=mysql
64
- - rvm: 2.3.3
65
- env: RAILS=master DB=postgres
66
-
67
- - rvm: 2.2.6
68
- env: RAILS=master DB=sqlite3
69
- - rvm: 2.2.6
70
- env: RAILS=master DB=mysql
71
- - rvm: 2.2.6
72
- env: RAILS=master DB=postgres
73
-
74
- allow_failures:
75
- - env: RAILS=master DB=sqlite3
76
- - env: RAILS=master DB=mysql
77
- - env: RAILS=master DB=postgres
78
-
79
- before_script:
80
- - mysql -e 'create database ransack collate utf8_general_ci;'
81
- - mysql -e 'use ransack;show variables like "%character%";show variables like "%collation%";'
82
- - psql -c 'create database ransack;' -U postgres
83
-
84
- addons:
85
- code_climate:
86
- 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 : ''.freeze
142
- }#{
143
- o.expressions.map { |x| visit x }.join(', '.freeze)
144
- })#{
145
- o.alias ? " AS #{visit o.alias}" : ''.freeze
146
- }"
147
- end
148
-
149
- def visit_Arel_Nodes_And o
150
- o.children.map { |x| visit x }.join(' AND '.freeze)
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(', '.freeze)
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,203 +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
-
11
- # Because the AR::Associations namespace is insane
12
- if defined? ::ActiveRecord::Associations::ClassMethods::JoinDependency
13
- JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
14
- end
15
-
16
- # Redefine a few things for ActiveRecord 3.0.
17
-
18
- def initialize(object, options = {})
19
- super
20
- @arel_visitor = Arel::Visitors.visitor_for @engine
21
- end
22
-
23
- def relation_for(object)
24
- object.scoped
25
- end
26
-
27
- def evaluate(search, opts = {})
28
- viz = Visitor.new
29
- relation = @object.where(viz.accept(search.base))
30
- if search.sorts.any?
31
- relation = relation.except(:order)
32
- .reorder(viz.accept(search.sorts))
33
- end
34
- if opts[:distinct]
35
- relation.select(Constants::DISTINCT + @klass.quoted_table_name +
36
- Constants::DOT_ASTERIX)
37
- else
38
- relation
39
- end
40
- end
41
-
42
- def attribute_method?(str, klass = @klass)
43
- exists = false
44
-
45
- if ransackable_attribute?(str, klass)
46
- exists = true
47
- elsif (segments = str.split(/_/)).size > 1
48
- remainder = []
49
- found_assoc = nil
50
- while !found_assoc && remainder.unshift(segments.pop) &&
51
- segments.size > 0 do
52
- assoc, poly_class = unpolymorphize_association(
53
- segments.join(Constants::UNDERSCORE)
54
- )
55
- if found_assoc = get_association(assoc, klass)
56
- exists = attribute_method?(
57
- remainder.join(Constants::UNDERSCORE),
58
- poly_class || found_assoc.klass
59
- )
60
- end
61
- end
62
- end
63
-
64
- exists
65
- end
66
-
67
- def table_for(parent)
68
- parent.table
69
- end
70
-
71
- def type_for(attr)
72
- return nil unless attr && attr.valid?
73
- klassify(attr.parent)
74
- .columns_hash[attr.arel_attribute.name.to_s]
75
- .type
76
- end
77
-
78
- # All dependent JoinAssociation items used in the search query
79
- #
80
- def join_associations
81
- @join_dependency.join_associations
82
- end
83
-
84
- def join_sources
85
- raise NotImplementedError,
86
- "ActiveRecord 3.0 does not use join_sources or support joining relations with Arel::Join nodes. Use join_associations."
87
- end
88
-
89
- def alias_tracker
90
- raise NotImplementedError,
91
- "ActiveRecord 3.0 does not have an alias tracker"
92
- end
93
-
94
- private
95
-
96
- def get_parent_and_attribute_name(str, parent = @base)
97
- attr_name = nil
98
-
99
- if ransackable_attribute?(str, klassify(parent))
100
- attr_name = str
101
- elsif (segments = str.split(/_/)).size > 1
102
- remainder = []
103
- found_assoc = nil
104
- while remainder.unshift(segments.pop) && segments.size > 0 &&
105
- !found_assoc do
106
- assoc, klass = unpolymorphize_association(
107
- segments.join(Constants::UNDERSCORE)
108
- )
109
- if found_assoc = get_association(assoc, parent)
110
- join = build_or_find_association(
111
- found_assoc.name, parent, klass
112
- )
113
- parent, attr_name = get_parent_and_attribute_name(
114
- remainder.join(Constants::UNDERSCORE), join
115
- )
116
- end
117
- end
118
- end
119
- [parent, attr_name]
120
- end
121
-
122
- def get_association(str, parent = @base)
123
- klass = klassify parent
124
- ransackable_association?(str, klass) &&
125
- klass.reflect_on_all_associations.detect { |a| a.name.to_s == str }
126
- end
127
-
128
- def join_dependency(relation)
129
- if relation.respond_to?(:join_dependency) # Polyamorous enables this
130
- relation.join_dependency
131
- else
132
- build_join_dependency(relation)
133
- end
134
- end
135
-
136
- def build_join_dependency(relation)
137
- buckets = relation.joins_values.group_by do |join|
138
- case join
139
- when String
140
- Constants::STRING_JOIN
141
- when Hash, Symbol, Array
142
- Constants::ASSOCIATION_JOIN
143
- when ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
144
- Constants::STASHED_JOIN
145
- when Arel::Nodes::Join
146
- Constants::JOIN_NODE
147
- else
148
- raise 'unknown class: %s' % join.class.name
149
- end
150
- end
151
-
152
- association_joins = buckets[Constants::ASSOCIATION_JOIN] || []
153
-
154
- stashed_association_joins = buckets[Constants::STASHED_JOIN] || []
155
-
156
- join_nodes = buckets[Constants::JOIN_NODE] || []
157
-
158
- string_joins = (buckets[Constants::STRING_JOIN] || []).map(&:strip).uniq
159
-
160
- join_list = relation.send :custom_join_sql, (string_joins + join_nodes)
161
-
162
- join_dependency = JoinDependency.new(
163
- relation.klass,
164
- association_joins,
165
- join_list
166
- )
167
-
168
- join_nodes.each do |join|
169
- join_dependency.table_aliases[join.left.name.downcase] = 1
170
- end
171
-
172
- join_dependency.graft(*stashed_association_joins)
173
- end
174
-
175
- def build_or_find_association(name, parent = @base, klass = nil)
176
- found_association = @join_dependency.join_associations
177
- .detect do |assoc|
178
- assoc.reflection.name == name &&
179
- assoc.parent == parent &&
180
- (!klass || assoc.reflection.klass == klass)
181
- end
182
- unless found_association
183
- @join_dependency.send(
184
- :build, Polyamorous::Join.new(name, @join_type, klass), parent
185
- )
186
- found_association = @join_dependency.join_associations.last
187
-
188
- default_conditions = found_association.active_record.scoped.arel.constraints
189
- if default_conditions.any?
190
- and_default_conditions = "AND #{default_conditions.reduce(&:and).to_sql}"
191
- end
192
-
193
- # Leverage the stashed association functionality in AR
194
- @object = @object.joins(found_association).joins(and_default_conditions)
195
- end
196
-
197
- found_association
198
- end
199
-
200
- end
201
- end
202
- end
203
- end
@@ -1,212 +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
-
10
- # Redefine a few things for ActiveRecord 3.1.
11
-
12
- def initialize(object, options = {})
13
- super
14
- @arel_visitor = Arel::Visitors.visitor_for @engine
15
- end
16
-
17
- def relation_for(object)
18
- object.scoped
19
- end
20
-
21
- def evaluate(search, opts = {})
22
- viz = Visitor.new
23
- relation = @object.where(viz.accept(search.base))
24
- if search.sorts.any?
25
- relation = relation.except(:order)
26
- .reorder(viz.accept(search.sorts))
27
- end
28
- if opts[:distinct]
29
- relation.select(Constants::DISTINCT + @klass.quoted_table_name +
30
- Constants::DOT_ASTERIX)
31
- else
32
- relation
33
- end
34
- end
35
-
36
- def attribute_method?(str, klass = @klass)
37
- exists = false
38
-
39
- if ransackable_attribute?(str, klass)
40
- exists = true
41
- elsif (segments = str.split(/_/)).size > 1
42
- remainder = []
43
- found_assoc = nil
44
- while !found_assoc && remainder.unshift(segments.pop) &&
45
- segments.size > 0 do
46
- assoc, poly_class = unpolymorphize_association(
47
- segments.join(Constants::UNDERSCORE)
48
- )
49
- if found_assoc = get_association(assoc, klass)
50
- exists = attribute_method?(
51
- remainder.join(Constants::UNDERSCORE),
52
- poly_class || found_assoc.klass
53
- )
54
- end
55
- end
56
- end
57
-
58
- exists
59
- end
60
-
61
- def table_for(parent)
62
- parent.table
63
- end
64
-
65
- def type_for(attr)
66
- return nil unless attr && attr.valid?
67
- name = attr.arel_attribute.name.to_s
68
- table = attr.arel_attribute.relation.table_name
69
-
70
- unless @engine.connection_pool.table_exists?(table)
71
- raise "No table named #{table} exists"
72
- end
73
-
74
- @engine.connection_pool.columns_hash[table][name].type
75
- end
76
-
77
- def join_associations
78
- @join_dependency.join_associations
79
- end
80
-
81
- # All dependent Arel::Join nodes used in the search query
82
- #
83
- # This could otherwise be done as `@object.arel.join_sources`, except
84
- # that ActiveRecord's build_joins sets up its own JoinDependency.
85
- # This extracts what we need to access the joins using our existing
86
- # JoinDependency to track table aliases.
87
- #
88
- def join_sources
89
- base = Arel::SelectManager.new(@object.engine, @object.table)
90
- @object.joins_values.each do |assoc|
91
- next unless assoc.is_a?(JoinDependency::JoinAssociation)
92
- assoc.join_to(base)
93
- end
94
- base.join_sources
95
- end
96
-
97
- def alias_tracker
98
- @join_dependency.alias_tracker
99
- end
100
-
101
- private
102
-
103
- def get_parent_and_attribute_name(str, parent = @base)
104
- attr_name = nil
105
-
106
- if ransackable_attribute?(str, klassify(parent))
107
- attr_name = str
108
- elsif (segments = str.split(/_/)).size > 1
109
- remainder = []
110
- found_assoc = nil
111
- while remainder.unshift(segments.pop) && segments.size > 0 &&
112
- !found_assoc do
113
- assoc, klass = unpolymorphize_association(
114
- segments.join(Constants::UNDERSCORE)
115
- )
116
- if found_assoc = get_association(assoc, parent)
117
- join = build_or_find_association(
118
- found_assoc.name, parent, klass
119
- )
120
- parent, attr_name = get_parent_and_attribute_name(
121
- remainder.join(Constants::UNDERSCORE), join
122
- )
123
- end
124
- end
125
- end
126
-
127
- [parent, attr_name]
128
- end
129
-
130
- def get_association(str, parent = @base)
131
- klass = klassify parent
132
- ransackable_association?(str, klass) &&
133
- klass.reflect_on_all_associations.detect { |a| a.name.to_s == str }
134
- end
135
-
136
- def join_dependency(relation)
137
- if relation.respond_to?(:join_dependency) # Polyamorous enables this
138
- relation.join_dependency
139
- else
140
- build_join_dependency(relation)
141
- end
142
- end
143
-
144
- def build_join_dependency(relation)
145
- buckets = relation.joins_values.group_by do |join|
146
- case join
147
- when String
148
- Constants::STRING_JOIN
149
- when Hash, Symbol, Array
150
- Constants::ASSOCIATION_JOIN
151
- when JoinDependency::JoinAssociation
152
- Constants::STASHED_JOIN
153
- when Arel::Nodes::Join
154
- Constants::JOIN_NODE
155
- else
156
- raise 'unknown class: %s' % join.class.name
157
- end
158
- end
159
-
160
- association_joins = buckets[Constants::ASSOCIATION_JOIN] || []
161
-
162
- stashed_association_joins = buckets[Constants::STASHED_JOIN] || []
163
-
164
- join_nodes = buckets[Constants::JOIN_NODE] || []
165
-
166
- string_joins = (buckets[Constants::STRING_JOIN] || []).map(&:strip).uniq
167
-
168
- join_list = relation.send :custom_join_ast,
169
- relation.table.from(relation.table), string_joins
170
-
171
- join_dependency = JoinDependency.new(
172
- relation.klass,
173
- association_joins,
174
- join_list
175
- )
176
-
177
- join_nodes.each do |join|
178
- join_dependency.alias_tracker.aliases[join.left.name.downcase] = 1
179
- end
180
-
181
- join_dependency.graft(*stashed_association_joins)
182
- end
183
-
184
- def build_or_find_association(name, parent = @base, klass = nil)
185
- found_association = @join_dependency.join_associations
186
- .detect do |assoc|
187
- assoc.reflection.name == name &&
188
- assoc.parent == parent &&
189
- (!klass || assoc.reflection.klass == klass)
190
- end
191
- unless found_association
192
- @join_dependency.send(
193
- :build, Polyamorous::Join.new(name, @join_type, klass), parent
194
- )
195
- found_association = @join_dependency.join_associations.last
196
-
197
- default_conditions = found_association.active_record.scoped.arel.constraints
198
- if default_conditions.any?
199
- and_default_conditions = "AND #{default_conditions.reduce(&:and).to_sql}"
200
- end
201
-
202
- # Leverage the stashed association functionality in AR
203
- @object = @object.joins(found_association).joins(and_default_conditions)
204
- end
205
-
206
- found_association
207
- end
208
-
209
- end
210
- end
211
- end
212
- end