ransack 1.8.4 → 3.2.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 (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