ransack 1.8.4 → 3.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|