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.
- checksums.yaml +5 -5
- data/.github/FUNDING.yml +3 -0
- data/.github/SECURITY.md +12 -0
- data/.github/workflows/cronjob.yml +102 -0
- data/.github/workflows/deploy.yml +35 -0
- data/.github/workflows/rubocop.yml +20 -0
- data/.github/workflows/test-deploy.yml +29 -0
- data/.github/workflows/test.yml +130 -0
- data/.gitignore +3 -0
- data/{lib/ransack/adapters/mongoid/3.2/.gitkeep → .nojekyll} +0 -0
- data/.rubocop.yml +44 -0
- data/CHANGELOG.md +352 -0
- data/CONTRIBUTING.md +25 -13
- data/Gemfile +26 -27
- data/README.md +65 -815
- data/Rakefile +1 -22
- data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
- data/bug_report_templates/test-ransacker-arel-present-predicate.rb +71 -0
- data/docs/.gitignore +19 -0
- data/docs/.nojekyll +0 -0
- data/docs/babel.config.js +3 -0
- data/docs/blog/2022-03-27-ransack-3.0.0.md +20 -0
- data/docs/docs/getting-started/_category_.json +4 -0
- data/docs/docs/getting-started/advanced-mode.md +46 -0
- data/docs/docs/getting-started/configuration.md +47 -0
- data/docs/docs/getting-started/search-matches.md +67 -0
- data/docs/docs/getting-started/simple-mode.md +284 -0
- data/docs/docs/getting-started/sorting.md +79 -0
- data/docs/docs/getting-started/using-predicates.md +282 -0
- data/docs/docs/going-further/_category_.json +4 -0
- data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
- data/docs/docs/going-further/associations.md +70 -0
- data/docs/docs/going-further/custom-predicates.md +52 -0
- data/docs/docs/going-further/documentation.md +43 -0
- data/docs/docs/going-further/exporting-to-csv.md +49 -0
- data/docs/docs/going-further/external-guides.md +57 -0
- data/docs/docs/going-further/form-customisation.md +63 -0
- data/docs/docs/going-further/i18n.md +53 -0
- data/docs/docs/going-further/img/create_release.png +0 -0
- data/docs/docs/going-further/merging-searches.md +41 -0
- data/docs/docs/going-further/other-notes.md +428 -0
- data/docs/docs/going-further/polymorphic-search.md +40 -0
- data/docs/docs/going-further/ransackers.md +331 -0
- data/docs/docs/going-further/release_process.md +36 -0
- data/docs/docs/going-further/saving-queries.md +82 -0
- data/docs/docs/going-further/searching-postgres.md +57 -0
- data/docs/docs/going-further/wiki-contributors.md +82 -0
- data/docs/docs/intro.md +99 -0
- data/docs/docusaurus.config.js +120 -0
- data/docs/package.json +38 -0
- data/docs/sidebars.js +31 -0
- data/docs/src/components/HomepageFeatures/index.js +64 -0
- data/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- data/docs/src/css/custom.css +39 -0
- data/docs/src/pages/index.module.css +23 -0
- data/docs/src/pages/markdown-page.md +7 -0
- data/docs/static/.nojekyll +0 -0
- data/docs/static/img/docusaurus.png +0 -0
- data/docs/static/img/favicon.ico +0 -0
- data/docs/static/img/logo.svg +1 -0
- data/docs/static/img/tutorial/docsVersionDropdown.png +0 -0
- data/docs/static/img/tutorial/localeDropdown.png +0 -0
- data/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
- data/docs/static/img/undraw_docusaurus_react.svg +170 -0
- data/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- data/docs/static/logo/ransack-h.png +0 -0
- data/docs/static/logo/ransack-h.svg +34 -0
- data/docs/static/logo/ransack-v.png +0 -0
- data/docs/static/logo/ransack-v.svg +34 -0
- data/docs/static/logo/ransack.png +0 -0
- data/docs/static/logo/ransack.svg +21 -0
- data/docs/yarn.lock +8436 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +70 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +92 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -0
- data/lib/polyamorous/activerecord_7.0_ruby_2/join_association.rb +1 -0
- data/lib/polyamorous/activerecord_7.0_ruby_2/join_dependency.rb +1 -0
- data/lib/polyamorous/activerecord_7.0_ruby_2/reflection.rb +1 -0
- data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
- data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
- data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
- data/lib/polyamorous/join.rb +70 -0
- data/lib/polyamorous/polyamorous.rb +24 -0
- data/lib/polyamorous/swapping_reflection_class.rb +11 -0
- data/lib/polyamorous/tree_node.rb +7 -0
- data/lib/polyamorous.rb +1 -0
- data/lib/ransack/adapters/active_record/base.rb +14 -3
- data/lib/ransack/adapters/active_record/context.rb +140 -196
- data/lib/ransack/adapters/active_record/ransack/constants.rb +19 -4
- data/lib/ransack/adapters/active_record/ransack/context.rb +9 -19
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +7 -7
- data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
- data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
- data/lib/ransack/adapters/active_record.rb +0 -9
- data/lib/ransack/adapters.rb +2 -0
- data/lib/ransack/configuration.rb +52 -2
- data/lib/ransack/constants.rb +1 -5
- data/lib/ransack/context.rb +29 -24
- data/lib/ransack/helpers/form_builder.rb +12 -6
- data/lib/ransack/helpers/form_helper.rb +11 -3
- data/lib/ransack/helpers.rb +1 -1
- data/lib/ransack/locale/ar.yml +70 -0
- data/lib/ransack/locale/az.yml +70 -0
- data/lib/ransack/locale/bg.yml +70 -0
- data/lib/ransack/locale/ca.yml +70 -0
- data/lib/ransack/locale/el.yml +70 -0
- data/lib/ransack/locale/es.yml +22 -22
- data/lib/ransack/locale/fa.yml +70 -0
- data/lib/ransack/locale/fi.yml +71 -0
- data/lib/ransack/locale/nl.yml +4 -4
- data/lib/ransack/locale/ru.yml +70 -0
- data/lib/ransack/locale/sk.yml +70 -0
- data/lib/ransack/locale/sv.yml +70 -0
- data/lib/ransack/locale/tr.yml +70 -0
- data/lib/ransack/locale/zh-CN.yml +12 -12
- data/lib/ransack/nodes/attribute.rb +2 -2
- data/lib/ransack/nodes/condition.rb +7 -1
- data/lib/ransack/nodes/grouping.rb +3 -8
- data/lib/ransack/nodes/sort.rb +3 -3
- data/lib/ransack/nodes/value.rb +3 -3
- data/lib/ransack/predicate.rb +13 -20
- data/lib/ransack/search.rb +7 -4
- data/lib/ransack/translate.rb +115 -115
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +1 -12
- data/lib/ransack.rb +7 -5
- data/ransack.gemspec +9 -25
- data/spec/blueprints/articles.rb +1 -1
- data/spec/blueprints/comments.rb +1 -1
- data/spec/blueprints/notes.rb +1 -1
- data/spec/blueprints/tags.rb +1 -1
- data/spec/console.rb +5 -5
- data/spec/helpers/polyamorous_helper.rb +13 -0
- data/spec/helpers/ransack_helper.rb +1 -1
- data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
- data/spec/polyamorous/join_association_spec.rb +30 -0
- data/spec/polyamorous/join_dependency_spec.rb +81 -0
- data/spec/polyamorous/join_spec.rb +19 -0
- data/spec/ransack/adapters/active_record/base_spec.rb +105 -11
- data/spec/ransack/adapters/active_record/context_spec.rb +63 -24
- data/spec/ransack/configuration_spec.rb +24 -0
- data/spec/ransack/helpers/form_builder_spec.rb +3 -15
- data/spec/ransack/helpers/form_helper_spec.rb +135 -168
- data/spec/ransack/nodes/condition_spec.rb +13 -0
- data/spec/ransack/nodes/grouping_spec.rb +2 -2
- data/spec/ransack/nodes/value_spec.rb +115 -0
- data/spec/ransack/predicate_spec.rb +54 -2
- data/spec/ransack/search_spec.rb +266 -36
- data/spec/spec_helper.rb +14 -5
- data/spec/support/schema.rb +99 -21
- metadata +117 -187
- data/.travis.yml +0 -86
- data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
- data/lib/ransack/adapters/active_record/3.0/context.rb +0 -203
- data/lib/ransack/adapters/active_record/3.1/context.rb +0 -212
- data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
- data/lib/ransack/adapters/active_record/compat.rb +0 -14
- data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
- data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
- data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
- data/lib/ransack/adapters/mongoid/base.rb +0 -134
- data/lib/ransack/adapters/mongoid/context.rb +0 -212
- data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
- data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
- data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
- data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
- data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
- data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
- data/lib/ransack/adapters/mongoid/table.rb +0 -35
- data/lib/ransack/adapters/mongoid.rb +0 -15
- data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -314
- data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
- data/spec/mongoid/configuration_spec.rb +0 -162
- data/spec/mongoid/dependencies_spec.rb +0 -8
- data/spec/mongoid/helpers/ransack_helper.rb +0 -11
- data/spec/mongoid/nodes/condition_spec.rb +0 -49
- data/spec/mongoid/nodes/grouping_spec.rb +0 -13
- data/spec/mongoid/predicate_spec.rb +0 -155
- data/spec/mongoid/search_spec.rb +0 -445
- data/spec/mongoid/support/mongoid.yml +0 -11
- data/spec/mongoid/support/schema.rb +0 -135
- data/spec/mongoid/translate_spec.rb +0 -14
- data/spec/mongoid_spec_helper.rb +0 -63
- data/spec/ransack/dependencies_spec.rb +0 -12
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
require 'ransack/context'
|
|
2
|
-
require '
|
|
3
|
-
require 'polyamorous'
|
|
2
|
+
require 'polyamorous/polyamorous'
|
|
4
3
|
|
|
5
4
|
module Ransack
|
|
6
5
|
module Adapters
|
|
7
6
|
module ActiveRecord
|
|
8
7
|
class Context < ::Ransack::Context
|
|
9
8
|
|
|
10
|
-
# Because the AR::Associations namespace is insane
|
|
11
|
-
if defined? ::ActiveRecord::Associations::JoinDependency
|
|
12
|
-
JoinDependency = ::ActiveRecord::Associations::JoinDependency
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def initialize(object, options = {})
|
|
16
|
-
super
|
|
17
|
-
@arel_visitor = @engine.connection.visitor
|
|
18
|
-
end
|
|
19
|
-
|
|
20
9
|
def relation_for(object)
|
|
21
10
|
object.all
|
|
22
11
|
end
|
|
@@ -25,19 +14,50 @@ module Ransack
|
|
|
25
14
|
return nil unless attr && attr.valid?
|
|
26
15
|
name = attr.arel_attribute.name.to_s
|
|
27
16
|
table = attr.arel_attribute.relation.table_name
|
|
28
|
-
schema_cache =
|
|
29
|
-
unless schema_cache.send(
|
|
17
|
+
schema_cache = self.klass.connection.schema_cache
|
|
18
|
+
unless schema_cache.send(:data_source_exists?, table)
|
|
30
19
|
raise "No table named #{table} exists."
|
|
31
20
|
end
|
|
32
|
-
|
|
21
|
+
attr.klass.columns.find { |column| column.name == name }.type
|
|
33
22
|
end
|
|
34
23
|
|
|
35
24
|
def evaluate(search, opts = {})
|
|
36
25
|
viz = Visitor.new
|
|
37
26
|
relation = @object.where(viz.accept(search.base))
|
|
27
|
+
|
|
38
28
|
if search.sorts.any?
|
|
39
|
-
relation = relation.except(:order)
|
|
29
|
+
relation = relation.except(:order)
|
|
30
|
+
# Rather than applying all of the search's sorts in one fell swoop,
|
|
31
|
+
# as the original implementation does, we apply one at a time.
|
|
32
|
+
#
|
|
33
|
+
# If the sort (returned by the Visitor above) is a symbol, we know
|
|
34
|
+
# that it represents a scope on the model and we can apply that
|
|
35
|
+
# scope.
|
|
36
|
+
#
|
|
37
|
+
# Otherwise, we fall back to the applying the sort with the "order"
|
|
38
|
+
# method as the original implementation did. Actually the original
|
|
39
|
+
# implementation used "reorder," which was overkill since we already
|
|
40
|
+
# have a clean slate after "relation.except(:order)" above.
|
|
41
|
+
viz.accept(search.sorts).each do |scope_or_sort|
|
|
42
|
+
if scope_or_sort.is_a?(Symbol)
|
|
43
|
+
relation = relation.send(scope_or_sort)
|
|
44
|
+
else
|
|
45
|
+
case Ransack.options[:postgres_fields_sort_option]
|
|
46
|
+
when :nulls_first
|
|
47
|
+
scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST") : Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
|
|
48
|
+
when :nulls_last
|
|
49
|
+
scope_or_sort = scope_or_sort.direction == :asc ? Arel.sql("#{scope_or_sort.to_sql} NULLS LAST") : Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
|
|
50
|
+
when :nulls_always_first
|
|
51
|
+
scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
|
|
52
|
+
when :nulls_always_last
|
|
53
|
+
scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
relation = relation.order(scope_or_sort)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
40
59
|
end
|
|
60
|
+
|
|
41
61
|
opts[:distinct] ? relation.distinct : relation
|
|
42
62
|
end
|
|
43
63
|
|
|
@@ -45,7 +65,7 @@ module Ransack
|
|
|
45
65
|
exists = false
|
|
46
66
|
if ransackable_attribute?(str, klass)
|
|
47
67
|
exists = true
|
|
48
|
-
elsif (segments = str.split(
|
|
68
|
+
elsif (segments = str.split(Constants::UNDERSCORE)).size > 1
|
|
49
69
|
remainder = []
|
|
50
70
|
found_assoc = nil
|
|
51
71
|
while !found_assoc && remainder.unshift(segments.pop) &&
|
|
@@ -80,84 +100,46 @@ module Ransack
|
|
|
80
100
|
end
|
|
81
101
|
end
|
|
82
102
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if ::ActiveRecord::VERSION::MAJOR >= 5
|
|
100
|
-
[
|
|
101
|
-
Arel::SelectManager.new(@object.table),
|
|
102
|
-
@join_dependency.join_constraints(@object.joins_values, @join_type)
|
|
103
|
-
]
|
|
104
|
-
else
|
|
105
|
-
[
|
|
106
|
-
Arel::SelectManager.new(@object.engine, @object.table),
|
|
107
|
-
@join_dependency.join_constraints(@object.joins_values)
|
|
108
|
-
]
|
|
109
|
-
end
|
|
110
|
-
joins.each do |aliased_join|
|
|
111
|
-
base.from(aliased_join)
|
|
112
|
-
end
|
|
113
|
-
base.join_sources
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
else
|
|
117
|
-
|
|
118
|
-
# All dependent JoinAssociation items used in the search query.
|
|
119
|
-
#
|
|
120
|
-
# Deprecated: this goes away in ActiveRecord 4.1. Use join_sources.
|
|
121
|
-
#
|
|
122
|
-
def join_associations
|
|
123
|
-
@join_dependency.join_associations
|
|
103
|
+
# All dependent Arel::Join nodes used in the search query.
|
|
104
|
+
#
|
|
105
|
+
# This could otherwise be done as `@object.arel.join_sources`, except
|
|
106
|
+
# that ActiveRecord's build_joins sets up its own JoinDependency.
|
|
107
|
+
# This extracts what we need to access the joins using our existing
|
|
108
|
+
# JoinDependency to track table aliases.
|
|
109
|
+
#
|
|
110
|
+
def join_sources
|
|
111
|
+
base, joins = begin
|
|
112
|
+
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, @object.table.name, [])
|
|
113
|
+
constraints = @join_dependency.join_constraints(@object.joins_values, alias_tracker, @object.references_values)
|
|
114
|
+
|
|
115
|
+
[
|
|
116
|
+
Arel::SelectManager.new(@object.table),
|
|
117
|
+
constraints
|
|
118
|
+
]
|
|
124
119
|
end
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
base = Arel::SelectManager.new(@object.engine, @object.table)
|
|
128
|
-
joins = @object.joins_values
|
|
129
|
-
joins.each do |assoc|
|
|
130
|
-
assoc.join_to(base)
|
|
131
|
-
end
|
|
132
|
-
base.join_sources
|
|
120
|
+
joins.each do |aliased_join|
|
|
121
|
+
base.from(aliased_join)
|
|
133
122
|
end
|
|
134
|
-
|
|
123
|
+
base.join_sources
|
|
135
124
|
end
|
|
136
125
|
|
|
137
126
|
def alias_tracker
|
|
138
|
-
@join_dependency.alias_tracker
|
|
127
|
+
@join_dependency.send(:alias_tracker)
|
|
139
128
|
end
|
|
140
129
|
|
|
141
130
|
def lock_association(association)
|
|
142
131
|
@lock_associations << association
|
|
143
132
|
end
|
|
144
133
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
end
|
|
155
|
-
else
|
|
156
|
-
def remove_association(association)
|
|
157
|
-
return if @lock_associations.include?(association)
|
|
158
|
-
@join_dependency.join_parts.delete(association)
|
|
159
|
-
@object.joins_values.delete(association)
|
|
160
|
-
end
|
|
134
|
+
def remove_association(association)
|
|
135
|
+
return if @lock_associations.include?(association)
|
|
136
|
+
@join_dependency.instance_variable_get(:@join_root).children.delete_if { |stashed|
|
|
137
|
+
stashed.eql?(association)
|
|
138
|
+
}
|
|
139
|
+
@object.joins_values.delete_if { |jd|
|
|
140
|
+
jd.instance_variables.include?(:@join_root) &&
|
|
141
|
+
jd.instance_variable_get(:@join_root).children.map(&:object_id) == [association.object_id]
|
|
142
|
+
}
|
|
161
143
|
end
|
|
162
144
|
|
|
163
145
|
# Build an Arel subquery that selects keys for the top query,
|
|
@@ -181,8 +163,7 @@ module Ransack
|
|
|
181
163
|
def build_correlated_subquery(association)
|
|
182
164
|
join_constraints = extract_joins(association)
|
|
183
165
|
join_root = join_constraints.shift
|
|
184
|
-
|
|
185
|
-
correlated_key = join_root.right.expr.left
|
|
166
|
+
correlated_key = extract_correlated_key(join_root)
|
|
186
167
|
subquery = Arel::SelectManager.new(association.base_klass)
|
|
187
168
|
subquery.from(join_root.left)
|
|
188
169
|
subquery.project(correlated_key)
|
|
@@ -198,11 +179,32 @@ module Ransack
|
|
|
198
179
|
|
|
199
180
|
private
|
|
200
181
|
|
|
201
|
-
def
|
|
202
|
-
|
|
203
|
-
|
|
182
|
+
def extract_correlated_key(join_root)
|
|
183
|
+
case join_root
|
|
184
|
+
when Arel::Nodes::OuterJoin
|
|
185
|
+
# one of join_root.right/join_root.left is expected to be Arel::Nodes::On
|
|
186
|
+
if join_root.right.is_a?(Arel::Nodes::On)
|
|
187
|
+
extract_correlated_key(join_root.right.expr)
|
|
188
|
+
elsif join_root.left.is_a?(Arel::Nodes::On)
|
|
189
|
+
extract_correlated_key(join_root.left.expr)
|
|
190
|
+
else
|
|
191
|
+
raise 'Ransack encountered an unexpected arel structure'
|
|
192
|
+
end
|
|
193
|
+
when Arel::Nodes::Equality
|
|
194
|
+
pk = primary_key
|
|
195
|
+
if join_root.left == pk
|
|
196
|
+
join_root.right
|
|
197
|
+
elsif join_root.right == pk
|
|
198
|
+
join_root.left
|
|
199
|
+
else
|
|
200
|
+
nil
|
|
201
|
+
end
|
|
202
|
+
when Arel::Nodes::And
|
|
203
|
+
extract_correlated_key(join_root.left) || extract_correlated_key(join_root.right)
|
|
204
204
|
else
|
|
205
|
-
|
|
205
|
+
# eg parent was Arel::Nodes::And and the evaluated side was one of
|
|
206
|
+
# Arel::Nodes::Grouping or MultiTenant::TenantEnforcementClause
|
|
207
|
+
nil
|
|
206
208
|
end
|
|
207
209
|
end
|
|
208
210
|
|
|
@@ -250,7 +252,9 @@ module Ransack
|
|
|
250
252
|
# Checkout active_record/relation/query_methods.rb +build_joins+ for
|
|
251
253
|
# reference. Lots of duplicated code maybe we can avoid it
|
|
252
254
|
def build_joins(relation)
|
|
253
|
-
buckets = relation.joins_values
|
|
255
|
+
buckets = relation.joins_values + relation.left_outer_joins_values
|
|
256
|
+
|
|
257
|
+
buckets = buckets.group_by do |join|
|
|
254
258
|
case join
|
|
255
259
|
when String
|
|
256
260
|
:string_join
|
|
@@ -268,133 +272,73 @@ module Ransack
|
|
|
268
272
|
association_joins = buckets[:association_join]
|
|
269
273
|
stashed_association_joins = buckets[:stashed_join]
|
|
270
274
|
join_nodes = buckets[:join_node].uniq
|
|
271
|
-
string_joins = buckets[:string_join].map(&:strip)
|
|
275
|
+
string_joins = buckets[:string_join].map(&:strip)
|
|
276
|
+
string_joins.uniq!
|
|
272
277
|
|
|
273
|
-
join_list =
|
|
274
|
-
if ::ActiveRecord::VERSION::MAJOR >= 5
|
|
275
|
-
join_nodes +
|
|
276
|
-
convert_join_strings_to_ast(relation.table, string_joins)
|
|
277
|
-
else
|
|
278
|
-
relation.send :custom_join_ast,
|
|
279
|
-
relation.table.from(relation.table), string_joins
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
join_dependency = JoinDependency.new(
|
|
283
|
-
relation.klass, association_joins, join_list
|
|
284
|
-
)
|
|
278
|
+
join_list = join_nodes + convert_join_strings_to_ast(relation.table, string_joins)
|
|
285
279
|
|
|
280
|
+
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
|
|
281
|
+
join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin)
|
|
282
|
+
join_dependency.instance_variable_set(:@alias_tracker, alias_tracker)
|
|
286
283
|
join_nodes.each do |join|
|
|
287
|
-
join_dependency.alias_tracker.aliases[join.left.name.downcase] = 1
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
if ::ActiveRecord::VERSION::STRING >= Constants::RAILS_4_1
|
|
291
|
-
join_dependency
|
|
292
|
-
else
|
|
293
|
-
join_dependency.graft(*stashed_association_joins)
|
|
284
|
+
join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
|
|
294
285
|
end
|
|
286
|
+
join_dependency
|
|
295
287
|
end
|
|
296
288
|
|
|
297
289
|
def convert_join_strings_to_ast(table, joins)
|
|
290
|
+
joins.map! { |join| table.create_string_join(Arel.sql(join)) unless join.blank? }
|
|
291
|
+
joins.compact!
|
|
298
292
|
joins
|
|
299
|
-
.flatten
|
|
300
|
-
.reject(&:blank?)
|
|
301
|
-
.map { |join| table.create_string_join(Arel.sql(join)) }
|
|
302
293
|
end
|
|
303
294
|
|
|
304
295
|
def build_or_find_association(name, parent = @base, klass = nil)
|
|
305
296
|
find_association(name, parent, klass) or build_association(name, parent, klass)
|
|
306
297
|
end
|
|
307
298
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
@
|
|
312
|
-
|
|
313
|
-
(@associations_pot.empty? || @associations_pot[assoc] == parent) &&
|
|
314
|
-
(!klass || assoc.reflection.klass == klass)
|
|
315
|
-
end
|
|
299
|
+
def find_association(name, parent = @base, klass = nil)
|
|
300
|
+
@join_dependency.instance_variable_get(:@join_root).children.detect do |assoc|
|
|
301
|
+
assoc.reflection.name == name && assoc.table &&
|
|
302
|
+
(@associations_pot.empty? || @associations_pot[assoc] == parent || !@associations_pot.key?(assoc)) &&
|
|
303
|
+
(!klass || assoc.reflection.klass == klass)
|
|
316
304
|
end
|
|
305
|
+
end
|
|
317
306
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
# TODO maybe we dont need to push associations here, we could loop
|
|
328
|
-
# through the @associations_pot instead
|
|
329
|
-
@join_dependency.join_root.children.push found_association
|
|
330
|
-
|
|
331
|
-
# Builds the arel nodes properly for this association
|
|
332
|
-
@join_dependency.send(
|
|
333
|
-
:construct_tables!, jd.join_root, found_association
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
# Leverage the stashed association functionality in AR
|
|
337
|
-
@object = @object.joins(jd)
|
|
338
|
-
|
|
339
|
-
found_association
|
|
340
|
-
end
|
|
341
|
-
|
|
342
|
-
def extract_joins(association)
|
|
343
|
-
parent = @join_dependency.join_root
|
|
344
|
-
reflection = association.reflection
|
|
345
|
-
join_constraints = if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_1
|
|
346
|
-
association.join_constraints(
|
|
347
|
-
parent.table,
|
|
348
|
-
parent.base_klass,
|
|
349
|
-
association,
|
|
350
|
-
Arel::Nodes::OuterJoin,
|
|
351
|
-
association.tables,
|
|
352
|
-
reflection.scope_chain,
|
|
353
|
-
reflection.chain
|
|
354
|
-
)
|
|
355
|
-
else
|
|
356
|
-
association.join_constraints(
|
|
357
|
-
parent.table,
|
|
358
|
-
parent.base_klass,
|
|
359
|
-
Arel::Nodes::OuterJoin,
|
|
360
|
-
association.tables,
|
|
361
|
-
reflection.chain
|
|
362
|
-
)
|
|
363
|
-
end
|
|
364
|
-
join_constraints.to_a.flatten
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
else
|
|
368
|
-
|
|
369
|
-
def build_association(name, parent = @base, klass = nil)
|
|
370
|
-
@join_dependency.send(
|
|
371
|
-
:build,
|
|
372
|
-
Polyamorous::Join.new(name, @join_type, klass),
|
|
373
|
-
parent
|
|
374
|
-
)
|
|
375
|
-
found_association = @join_dependency.join_associations.last
|
|
376
|
-
# Leverage the stashed association functionality in AR
|
|
377
|
-
@object = @object.joins(found_association)
|
|
307
|
+
def build_association(name, parent = @base, klass = nil)
|
|
308
|
+
jd = Polyamorous::JoinDependency.new(
|
|
309
|
+
parent.base_klass,
|
|
310
|
+
parent.table,
|
|
311
|
+
Polyamorous::Join.new(name, @join_type, klass),
|
|
312
|
+
@join_type
|
|
313
|
+
)
|
|
314
|
+
found_association = jd.instance_variable_get(:@join_root).children.last
|
|
378
315
|
|
|
379
|
-
|
|
380
|
-
end
|
|
316
|
+
@associations_pot[found_association] = parent
|
|
381
317
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
end
|
|
318
|
+
# TODO maybe we dont need to push associations here, we could loop
|
|
319
|
+
# through the @associations_pot instead
|
|
320
|
+
@join_dependency.instance_variable_get(:@join_root).children.push found_association
|
|
386
321
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
.detect do |assoc|
|
|
390
|
-
assoc.reflection.name == name &&
|
|
391
|
-
assoc.parent == parent &&
|
|
392
|
-
(!klass || assoc.reflection.klass == klass)
|
|
393
|
-
end
|
|
394
|
-
end
|
|
322
|
+
# Builds the arel nodes properly for this association
|
|
323
|
+
@tables_pot[found_association] = @join_dependency.construct_tables_for_association!(jd.instance_variable_get(:@join_root), found_association)
|
|
395
324
|
|
|
325
|
+
# Leverage the stashed association functionality in AR
|
|
326
|
+
@object = @object.joins(jd)
|
|
327
|
+
found_association
|
|
396
328
|
end
|
|
397
329
|
|
|
330
|
+
def extract_joins(association)
|
|
331
|
+
parent = @join_dependency.instance_variable_get(:@join_root)
|
|
332
|
+
reflection = association.reflection
|
|
333
|
+
join_constraints = association.join_constraints_with_tables(
|
|
334
|
+
parent.table,
|
|
335
|
+
parent.base_klass,
|
|
336
|
+
Arel::Nodes::OuterJoin,
|
|
337
|
+
@join_dependency.instance_variable_get(:@alias_tracker),
|
|
338
|
+
@tables_pot[association]
|
|
339
|
+
)
|
|
340
|
+
join_constraints.to_a.flatten
|
|
341
|
+
end
|
|
398
342
|
end
|
|
399
343
|
end
|
|
400
344
|
end
|
|
@@ -13,6 +13,18 @@ module Ransack
|
|
|
13
13
|
formatter: proc { |v| "%#{escape_wildcards(v)}%" }
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
|
+
['i_cont'.freeze, {
|
|
17
|
+
arel_predicate: 'matches'.freeze,
|
|
18
|
+
formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" },
|
|
19
|
+
case_insensitive: true
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
['not_i_cont'.freeze, {
|
|
23
|
+
arel_predicate: 'does_not_match'.freeze,
|
|
24
|
+
formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" },
|
|
25
|
+
case_insensitive: true
|
|
26
|
+
}
|
|
27
|
+
],
|
|
16
28
|
['start'.freeze, {
|
|
17
29
|
arel_predicate: 'matches'.freeze,
|
|
18
30
|
formatter: proc { |v| "#{escape_wildcards(v)}%" }
|
|
@@ -85,7 +97,7 @@ module Ransack
|
|
|
85
97
|
arel_predicate: proc { |v| v ? EQ : NOT_EQ },
|
|
86
98
|
compounds: false,
|
|
87
99
|
type: :boolean,
|
|
88
|
-
validator: proc { |v| BOOLEAN_VALUES.include?(v)},
|
|
100
|
+
validator: proc { |v| BOOLEAN_VALUES.include?(v) },
|
|
89
101
|
formatter: proc { |v| nil }
|
|
90
102
|
}
|
|
91
103
|
],
|
|
@@ -102,9 +114,12 @@ module Ransack
|
|
|
102
114
|
# replace % \ to \% \\
|
|
103
115
|
def escape_wildcards(unescaped)
|
|
104
116
|
case ActiveRecord::Base.connection.adapter_name
|
|
105
|
-
when "Mysql2".freeze
|
|
106
|
-
# Necessary for
|
|
107
|
-
unescaped.to_s.gsub(/([
|
|
117
|
+
when "Mysql2".freeze
|
|
118
|
+
# Necessary for MySQL
|
|
119
|
+
unescaped.to_s.gsub(/([\\%_])/, '\\\\\\1')
|
|
120
|
+
when "PostgreSQL".freeze
|
|
121
|
+
# Necessary for PostgreSQL
|
|
122
|
+
unescaped.to_s.gsub(/([\\%_.])/, '\\\\\\1')
|
|
108
123
|
else
|
|
109
124
|
unescaped
|
|
110
125
|
end
|
|
@@ -28,24 +28,18 @@ module Ransack
|
|
|
28
28
|
@join_type = options[:join_type] || Polyamorous::OuterJoin
|
|
29
29
|
@search_key = options[:search_key] || Ransack.options[:search_key]
|
|
30
30
|
@associations_pot = {}
|
|
31
|
+
@tables_pot = {}
|
|
31
32
|
@lock_associations = []
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@engine = @base.base_klass.arel_engine
|
|
36
|
-
else
|
|
37
|
-
@base = @join_dependency.join_base
|
|
38
|
-
@engine = @base.arel_engine
|
|
39
|
-
end
|
|
34
|
+
@base = @join_dependency.instance_variable_get(:@join_root)
|
|
35
|
+
end
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@bind_pairs
|
|
45
|
-
parent, attr_name = get_parent_and_attribute_name(key)
|
|
46
|
-
if parent && attr_name
|
|
47
|
-
hash[key] = [parent, attr_name]
|
|
48
|
-
end
|
|
37
|
+
def bind_pair_for(key)
|
|
38
|
+
@bind_pairs ||= {}
|
|
39
|
+
|
|
40
|
+
@bind_pairs[key] ||= begin
|
|
41
|
+
parent, attr_name = get_parent_and_attribute_name(key.to_s)
|
|
42
|
+
[parent, attr_name] if parent && attr_name
|
|
49
43
|
end
|
|
50
44
|
end
|
|
51
45
|
|
|
@@ -54,10 +48,6 @@ module Ransack
|
|
|
54
48
|
obj
|
|
55
49
|
elsif obj.respond_to? :klass
|
|
56
50
|
obj.klass
|
|
57
|
-
elsif obj.respond_to? :active_record # Rails 3
|
|
58
|
-
obj.active_record
|
|
59
|
-
elsif obj.respond_to? :base_klass # Rails 4
|
|
60
|
-
obj.base_klass
|
|
61
51
|
else
|
|
62
52
|
raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
|
|
63
53
|
end
|
|
@@ -30,11 +30,11 @@ module Ransack
|
|
|
30
30
|
def format_predicate(attribute)
|
|
31
31
|
arel_pred = arel_predicate_for_attribute(attribute)
|
|
32
32
|
arel_values = formatted_values_for_attribute(attribute)
|
|
33
|
-
predicate = attribute.
|
|
33
|
+
predicate = attr_value_for_attribute(attribute).public_send(arel_pred, arel_values)
|
|
34
34
|
|
|
35
35
|
if in_predicate?(predicate)
|
|
36
|
-
predicate.right = predicate.right.map do |
|
|
37
|
-
casted_array?(
|
|
36
|
+
predicate.right = predicate.right.map do |pr|
|
|
37
|
+
casted_array?(pr) ? format_values_for(pr) : pr
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -43,16 +43,16 @@ module Ransack
|
|
|
43
43
|
|
|
44
44
|
def in_predicate?(predicate)
|
|
45
45
|
return unless defined?(Arel::Nodes::Casted)
|
|
46
|
-
predicate.class == Arel::Nodes::In
|
|
46
|
+
predicate.class == Arel::Nodes::In || predicate.class == Arel::Nodes::NotIn
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def casted_array?(predicate)
|
|
50
|
-
predicate.
|
|
50
|
+
predicate.value.is_a?(Array) && predicate.is_a?(Arel::Nodes::Casted)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def format_values_for(predicate)
|
|
54
|
-
predicate.
|
|
55
|
-
|
|
54
|
+
predicate.value.map do |val|
|
|
55
|
+
val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
@@ -2,11 +2,7 @@ module Ransack
|
|
|
2
2
|
module Translate
|
|
3
3
|
|
|
4
4
|
def self.i18n_key(klass)
|
|
5
|
-
|
|
6
|
-
klass.model_name.i18n_key.to_s.tr('.'.freeze, '/'.freeze)
|
|
7
|
-
else
|
|
8
|
-
klass.model_name.i18n_key.to_s.freeze
|
|
9
|
-
end
|
|
5
|
+
klass.model_name.i18n_key
|
|
10
6
|
end
|
|
11
7
|
end
|
|
12
8
|
end
|
|
@@ -20,5 +20,28 @@ module Ransack
|
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
def visit_Ransack_Nodes_Sort(object)
|
|
24
|
+
if object.valid?
|
|
25
|
+
if object.attr.is_a?(Arel::Attributes::Attribute)
|
|
26
|
+
object.attr.send(object.dir)
|
|
27
|
+
else
|
|
28
|
+
ordered(object)
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
scope_name = :"sort_by_#{object.name}_#{object.dir}"
|
|
32
|
+
scope_name if object.context.object.respond_to?(scope_name)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def ordered(object)
|
|
39
|
+
case object.dir
|
|
40
|
+
when 'asc'.freeze
|
|
41
|
+
Arel::Nodes::Ascending.new(object.attr)
|
|
42
|
+
when 'desc'.freeze
|
|
43
|
+
Arel::Nodes::Descending.new(object.attr)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
23
46
|
end
|
|
24
47
|
end
|
|
@@ -12,12 +12,3 @@ ActiveSupport.on_load(:active_record) do
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
require 'ransack/adapters/active_record/context'
|
|
15
|
-
|
|
16
|
-
case ActiveRecord::VERSION::STRING
|
|
17
|
-
when /^3\.0\./
|
|
18
|
-
require 'ransack/adapters/active_record/3.0/context'
|
|
19
|
-
when /^3\.1\./
|
|
20
|
-
require 'ransack/adapters/active_record/3.1/context'
|
|
21
|
-
when /^3\.2\./
|
|
22
|
-
require 'ransack/adapters/active_record/3.2/context'
|
|
23
|
-
end
|