ransack 2.1.1 → 2.5.0
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 +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/SECURITY.md +12 -0
- data/.github/workflows/cronjob.yml +102 -0
- data/.github/workflows/rubocop.yml +20 -0
- data/.github/workflows/test.yml +163 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +44 -0
- data/CHANGELOG.md +64 -1
- data/CONTRIBUTING.md +16 -11
- data/Gemfile +23 -17
- data/README.md +190 -57
- 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/img/create_release.png +0 -0
- data/docs/release_process.md +17 -0
- data/lib/polyamorous/{activerecord_5.2.1_ruby_2 → activerecord_5.2_ruby_2}/join_association.rb +2 -9
- data/lib/polyamorous/{activerecord_5.2.1_ruby_2 → activerecord_5.2_ruby_2}/join_dependency.rb +25 -3
- data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +11 -0
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +1 -0
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +80 -0
- data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +1 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +74 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +93 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +1 -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/polyamorous.rb +24 -0
- data/lib/polyamorous.rb +1 -25
- data/lib/ransack/adapters/active_record/base.rb +5 -1
- data/lib/ransack/adapters/active_record/context.rb +71 -68
- data/lib/ransack/adapters/active_record/ransack/constants.rb +18 -3
- data/lib/ransack/adapters/active_record/ransack/context.rb +2 -6
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +13 -5
- data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -1
- data/lib/ransack/configuration.rb +31 -1
- data/lib/ransack/constants.rb +3 -5
- data/lib/ransack/context.rb +19 -18
- data/lib/ransack/helpers/form_builder.rb +8 -14
- data/lib/ransack/helpers/form_helper.rb +1 -1
- data/lib/ransack/helpers.rb +1 -1
- data/lib/ransack/locale/az.yml +1 -1
- data/lib/ransack/locale/ca.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/sk.yml +70 -0
- data/lib/ransack/locale/sv.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +1 -1
- data/lib/ransack/nodes/condition.rb +7 -1
- data/lib/ransack/nodes/grouping.rb +1 -1
- data/lib/ransack/nodes/sort.rb +3 -3
- data/lib/ransack/nodes/value.rb +1 -1
- data/lib/ransack/predicate.rb +2 -1
- data/lib/ransack/search.rb +4 -1
- data/lib/ransack/translate.rb +115 -115
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack.rb +3 -3
- data/ransack.gemspec +8 -23
- 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 +3 -8
- data/spec/helpers/ransack_helper.rb +1 -1
- data/spec/{ransack → polyamorous}/join_association_spec.rb +8 -1
- data/spec/{ransack → polyamorous}/join_dependency_spec.rb +18 -7
- data/spec/{ransack → polyamorous}/join_spec.rb +0 -0
- data/spec/ransack/adapters/active_record/base_spec.rb +26 -15
- data/spec/ransack/adapters/active_record/context_spec.rb +60 -18
- data/spec/ransack/configuration_spec.rb +24 -0
- data/spec/ransack/helpers/form_helper_spec.rb +16 -16
- data/spec/ransack/nodes/condition_spec.rb +13 -0
- data/spec/ransack/nodes/grouping_spec.rb +2 -2
- data/spec/ransack/predicate_spec.rb +54 -2
- data/spec/ransack/search_spec.rb +238 -36
- data/spec/spec_helper.rb +10 -5
- data/spec/support/schema.rb +37 -3
- metadata +45 -139
- data/.travis.yml +0 -37
- data/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +0 -2
- data/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +0 -2
- data/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +0 -32
- data/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +0 -112
- data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +0 -32
- data/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +0 -113
@@ -0,0 +1,74 @@
|
|
1
|
+
module Polyamorous
|
2
|
+
module JoinAssociationExtensions
|
3
|
+
include SwappingReflectionClass
|
4
|
+
def self.prepended(base)
|
5
|
+
base.class_eval { attr_reader :join_type }
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(reflection, children, polymorphic_class = nil, join_type = Arel::Nodes::InnerJoin)
|
9
|
+
@join_type = join_type
|
10
|
+
if polymorphic_class && ::ActiveRecord::Base > polymorphic_class
|
11
|
+
swapping_reflection_klass(reflection, polymorphic_class) do |reflection|
|
12
|
+
super(reflection, children)
|
13
|
+
self.reflection.options[:polymorphic] = true
|
14
|
+
end
|
15
|
+
else
|
16
|
+
super(reflection, children)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Same as #join_constraints, but instead of constructing tables from the
|
21
|
+
# given block, uses the ones passed
|
22
|
+
def join_constraints_with_tables(foreign_table, foreign_klass, join_type, alias_tracker, tables)
|
23
|
+
joins = []
|
24
|
+
chain = []
|
25
|
+
|
26
|
+
reflection.chain.each.with_index do |reflection, i|
|
27
|
+
table = tables[i]
|
28
|
+
|
29
|
+
@table ||= table
|
30
|
+
chain << [reflection, table]
|
31
|
+
end
|
32
|
+
|
33
|
+
# The chain starts with the target table, but we want to end with it here (makes
|
34
|
+
# more sense in this context), so we reverse
|
35
|
+
chain.reverse_each do |reflection, table|
|
36
|
+
klass = reflection.klass
|
37
|
+
|
38
|
+
join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
|
39
|
+
|
40
|
+
unless join_scope.references_values.empty?
|
41
|
+
join_dependency = join_scope.construct_join_dependency(
|
42
|
+
join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin
|
43
|
+
)
|
44
|
+
join_scope.joins!(join_dependency)
|
45
|
+
end
|
46
|
+
|
47
|
+
arel = join_scope.arel(alias_tracker.aliases)
|
48
|
+
nodes = arel.constraints.first
|
49
|
+
|
50
|
+
if nodes.is_a?(Arel::Nodes::And)
|
51
|
+
others = nodes.children.extract! do |node|
|
52
|
+
!Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
joins << table.create_join(table, table.create_on(nodes), join_type)
|
57
|
+
|
58
|
+
if others && !others.empty?
|
59
|
+
joins.concat arel.join_sources
|
60
|
+
append_constraints(joins.last, others)
|
61
|
+
end
|
62
|
+
|
63
|
+
# The current table in this iteration becomes the foreign table in the next
|
64
|
+
foreign_table, foreign_klass = table, klass
|
65
|
+
end
|
66
|
+
|
67
|
+
joins
|
68
|
+
end
|
69
|
+
|
70
|
+
def ==(other)
|
71
|
+
base_klass == other.base_klass
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# active_record_6.1_ruby_2/join_dependency.rb
|
2
|
+
module Polyamorous
|
3
|
+
module JoinDependencyExtensions
|
4
|
+
# Replaces ActiveRecord::Associations::JoinDependency#build
|
5
|
+
def build(associations, base_klass)
|
6
|
+
associations.map do |name, right|
|
7
|
+
if name.is_a? Join
|
8
|
+
reflection = find_reflection base_klass, name.name
|
9
|
+
reflection.check_validity!
|
10
|
+
reflection.check_eager_loadable!
|
11
|
+
|
12
|
+
klass = if reflection.polymorphic?
|
13
|
+
name.klass || base_klass
|
14
|
+
else
|
15
|
+
reflection.klass
|
16
|
+
end
|
17
|
+
JoinAssociation.new(reflection, build(right, klass), name.klass, name.type)
|
18
|
+
else
|
19
|
+
reflection = find_reflection base_klass, name
|
20
|
+
reflection.check_validity!
|
21
|
+
reflection.check_eager_loadable!
|
22
|
+
|
23
|
+
if reflection.polymorphic?
|
24
|
+
raise ActiveRecord::EagerLoadPolymorphicError.new(reflection)
|
25
|
+
end
|
26
|
+
JoinAssociation.new(reflection, build(right, reflection.klass))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def join_constraints(joins_to_add, alias_tracker, references)
|
32
|
+
@alias_tracker = alias_tracker
|
33
|
+
@joined_tables = {}
|
34
|
+
@references = {}
|
35
|
+
|
36
|
+
references.each do |table_name|
|
37
|
+
@references[table_name.to_sym] = table_name if table_name.is_a?(String)
|
38
|
+
end
|
39
|
+
|
40
|
+
joins = make_join_constraints(join_root, join_type)
|
41
|
+
|
42
|
+
joins.concat joins_to_add.flat_map { |oj|
|
43
|
+
if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
|
44
|
+
walk join_root, oj.join_root, oj.join_type
|
45
|
+
else
|
46
|
+
make_join_constraints(oj.join_root, oj.join_type)
|
47
|
+
end
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def construct_tables_for_association!(join_root, association)
|
52
|
+
tables = table_aliases_for(join_root, association)
|
53
|
+
association.table = tables.first
|
54
|
+
tables
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def table_aliases_for(parent, node)
|
60
|
+
node.reflection.chain.map { |reflection|
|
61
|
+
alias_tracker.aliased_table_for(reflection.klass.arel_table) do
|
62
|
+
root = reflection == node.reflection
|
63
|
+
name = reflection.alias_candidate(parent.table_name)
|
64
|
+
root ? name : "#{name}_join"
|
65
|
+
end
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
# Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
|
71
|
+
#
|
72
|
+
def walk_tree(associations, hash)
|
73
|
+
case associations
|
74
|
+
when TreeNode
|
75
|
+
associations.add_to_tree(hash)
|
76
|
+
when Hash
|
77
|
+
associations.each do |k, v|
|
78
|
+
cache =
|
79
|
+
if TreeNode === k
|
80
|
+
k.add_to_tree(hash)
|
81
|
+
else
|
82
|
+
hash[k] ||= {}
|
83
|
+
end
|
84
|
+
walk_tree(v, cache)
|
85
|
+
end
|
86
|
+
else
|
87
|
+
super(associations, hash)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'polyamorous/activerecord_6.0_ruby_2/reflection'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'polyamorous/activerecord_6.1_ruby_2/join_association'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'polyamorous/activerecord_6.1_ruby_2/join_dependency'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'polyamorous/activerecord_6.1_ruby_2/reflection'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
if defined?(::ActiveRecord)
|
2
|
+
module Polyamorous
|
3
|
+
InnerJoin = Arel::Nodes::InnerJoin
|
4
|
+
OuterJoin = Arel::Nodes::OuterJoin
|
5
|
+
|
6
|
+
JoinDependency = ::ActiveRecord::Associations::JoinDependency
|
7
|
+
JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'polyamorous/tree_node'
|
11
|
+
require 'polyamorous/join'
|
12
|
+
require 'polyamorous/swapping_reflection_class'
|
13
|
+
|
14
|
+
ar_version = ::ActiveRecord::VERSION::STRING[0, 3]
|
15
|
+
%w(join_association join_dependency reflection).each do |file|
|
16
|
+
require "polyamorous/activerecord_#{ar_version}_ruby_2/#{file}"
|
17
|
+
end
|
18
|
+
|
19
|
+
ActiveRecord::Reflection::AbstractReflection.send(:prepend, Polyamorous::ReflectionExtensions)
|
20
|
+
|
21
|
+
Polyamorous::JoinDependency.send(:prepend, Polyamorous::JoinDependencyExtensions)
|
22
|
+
Polyamorous::JoinDependency.singleton_class.send(:prepend, Polyamorous::JoinDependencyExtensions::ClassMethods)
|
23
|
+
Polyamorous::JoinAssociation.send(:prepend, Polyamorous::JoinAssociationExtensions)
|
24
|
+
end
|
data/lib/polyamorous.rb
CHANGED
@@ -1,25 +1 @@
|
|
1
|
-
|
2
|
-
module Polyamorous
|
3
|
-
InnerJoin = Arel::Nodes::InnerJoin
|
4
|
-
OuterJoin = Arel::Nodes::OuterJoin
|
5
|
-
|
6
|
-
JoinDependency = ::ActiveRecord::Associations::JoinDependency
|
7
|
-
JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
|
8
|
-
end
|
9
|
-
|
10
|
-
require 'polyamorous/tree_node'
|
11
|
-
require 'polyamorous/join'
|
12
|
-
require 'polyamorous/swapping_reflection_class'
|
13
|
-
|
14
|
-
ar_version = ::ActiveRecord::VERSION::STRING[0,3]
|
15
|
-
ar_version = ::ActiveRecord::VERSION::STRING[0,5] if ar_version >= "5.2"
|
16
|
-
ar_version = "5.2.1" if ::ActiveRecord::VERSION::STRING >= "5.2.1"
|
17
|
-
|
18
|
-
%w(join_association join_dependency).each do |file|
|
19
|
-
require "polyamorous/activerecord_#{ar_version}_ruby_2/#{file}"
|
20
|
-
end
|
21
|
-
|
22
|
-
Polyamorous::JoinDependency.send(:prepend, Polyamorous::JoinDependencyExtensions)
|
23
|
-
Polyamorous::JoinDependency.singleton_class.send(:prepend, Polyamorous::JoinDependencyExtensions::ClassMethods)
|
24
|
-
Polyamorous::JoinAssociation.send(:prepend, Polyamorous::JoinAssociationExtensions)
|
25
|
-
end
|
1
|
+
require 'polyamorous/polyamorous'
|
@@ -18,6 +18,10 @@ module Ransack
|
|
18
18
|
Search.new(self, params, options)
|
19
19
|
end
|
20
20
|
|
21
|
+
def ransack!(params = {}, options = {})
|
22
|
+
ransack(params, options.merge(ignore_unknown_conditions: false))
|
23
|
+
end
|
24
|
+
|
21
25
|
def ransacker(name, opts = {}, &block)
|
22
26
|
self._ransackers = _ransackers.merge name.to_s => Ransacker
|
23
27
|
.new(self, name, opts, &block)
|
@@ -66,7 +70,7 @@ module Ransack
|
|
66
70
|
end
|
67
71
|
|
68
72
|
# ransack_scope_skip_sanitize_args, by default, returns an empty array.
|
69
|
-
# i.e. use the sanitize_scope_args setting to
|
73
|
+
# i.e. use the sanitize_scope_args setting to determine if args should be converted.
|
70
74
|
# For overriding with a list of scopes which should be passed the args as-is.
|
71
75
|
#
|
72
76
|
def ransackable_scopes_skip_sanitize_args
|
@@ -1,18 +1,11 @@
|
|
1
1
|
require 'ransack/context'
|
2
|
-
require 'polyamorous'
|
2
|
+
require 'polyamorous/polyamorous'
|
3
3
|
|
4
4
|
module Ransack
|
5
5
|
module Adapters
|
6
6
|
module ActiveRecord
|
7
7
|
class Context < ::Ransack::Context
|
8
8
|
|
9
|
-
def initialize(object, options = {})
|
10
|
-
super
|
11
|
-
if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2
|
12
|
-
@arel_visitor = @engine.connection.visitor
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
9
|
def relation_for(object)
|
17
10
|
object.all
|
18
11
|
end
|
@@ -49,6 +42,17 @@ module Ransack
|
|
49
42
|
if scope_or_sort.is_a?(Symbol)
|
50
43
|
relation = relation.send(scope_or_sort)
|
51
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
|
+
|
52
56
|
relation = relation.order(scope_or_sort)
|
53
57
|
end
|
54
58
|
end
|
@@ -104,20 +108,21 @@ module Ransack
|
|
104
108
|
# JoinDependency to track table aliases.
|
105
109
|
#
|
106
110
|
def join_sources
|
107
|
-
base, joins =
|
108
|
-
if ::ActiveRecord::VERSION::STRING > Constants::RAILS_5_2_0
|
111
|
+
base, joins = begin
|
109
112
|
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, @object.table.name, [])
|
110
|
-
|
111
|
-
|
113
|
+
constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1)
|
114
|
+
@join_dependency.join_constraints(@object.joins_values, alias_tracker, @object.references_values)
|
115
|
+
elsif ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
|
116
|
+
@join_dependency.join_constraints(@object.joins_values, alias_tracker)
|
117
|
+
else
|
112
118
|
@join_dependency.join_constraints(@object.joins_values, @join_type, alias_tracker)
|
113
|
-
|
114
|
-
|
119
|
+
end
|
120
|
+
|
115
121
|
[
|
116
122
|
Arel::SelectManager.new(@object.table),
|
117
|
-
|
123
|
+
constraints
|
118
124
|
]
|
119
125
|
end
|
120
|
-
joins = joins.collect(&:joins).flatten if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2
|
121
126
|
joins.each do |aliased_join|
|
122
127
|
base.from(aliased_join)
|
123
128
|
end
|
@@ -163,7 +168,7 @@ module Ransack
|
|
163
168
|
def build_correlated_subquery(association)
|
164
169
|
join_constraints = extract_joins(association)
|
165
170
|
join_root = join_constraints.shift
|
166
|
-
correlated_key = join_root
|
171
|
+
correlated_key = extract_correlated_key(join_root)
|
167
172
|
subquery = Arel::SelectManager.new(association.base_klass)
|
168
173
|
subquery.from(join_root.left)
|
169
174
|
subquery.project(correlated_key)
|
@@ -179,6 +184,35 @@ module Ransack
|
|
179
184
|
|
180
185
|
private
|
181
186
|
|
187
|
+
def extract_correlated_key(join_root)
|
188
|
+
case join_root
|
189
|
+
when Arel::Nodes::OuterJoin
|
190
|
+
# one of join_root.right/join_root.left is expected to be Arel::Nodes::On
|
191
|
+
if join_root.right.is_a?(Arel::Nodes::On)
|
192
|
+
extract_correlated_key(join_root.right.expr)
|
193
|
+
elsif join_root.left.is_a?(Arel::Nodes::On)
|
194
|
+
extract_correlated_key(join_root.left.expr)
|
195
|
+
else
|
196
|
+
raise 'Ransack encountered an unexpected arel structure'
|
197
|
+
end
|
198
|
+
when Arel::Nodes::Equality
|
199
|
+
pk = primary_key
|
200
|
+
if join_root.left == pk
|
201
|
+
join_root.right
|
202
|
+
elsif join_root.right == pk
|
203
|
+
join_root.left
|
204
|
+
else
|
205
|
+
nil
|
206
|
+
end
|
207
|
+
when Arel::Nodes::And
|
208
|
+
extract_correlated_key(join_root.left) || extract_correlated_key(join_root.right)
|
209
|
+
else
|
210
|
+
# eg parent was Arel::Nodes::And and the evaluated side was one of
|
211
|
+
# Arel::Nodes::Grouping or MultiTenant::TenantEnforcementClause
|
212
|
+
nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
182
216
|
def get_parent_and_attribute_name(str, parent = @base)
|
183
217
|
attr_name = nil
|
184
218
|
|
@@ -248,24 +282,15 @@ module Ransack
|
|
248
282
|
|
249
283
|
join_list = join_nodes + convert_join_strings_to_ast(relation.table, string_joins)
|
250
284
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
|
255
|
-
end
|
256
|
-
elsif ::ActiveRecord::VERSION::STRING == Constants::RAILS_5_2_0
|
257
|
-
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
|
258
|
-
join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, alias_tracker)
|
259
|
-
join_nodes.each do |join|
|
260
|
-
join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
|
261
|
-
end
|
285
|
+
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
|
286
|
+
join_dependency = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
|
287
|
+
Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin)
|
262
288
|
else
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
end
|
289
|
+
Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins)
|
290
|
+
end
|
291
|
+
join_dependency.instance_variable_set(:@alias_tracker, alias_tracker)
|
292
|
+
join_nodes.each do |join|
|
293
|
+
join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
|
269
294
|
end
|
270
295
|
join_dependency
|
271
296
|
end
|
@@ -289,32 +314,23 @@ module Ransack
|
|
289
314
|
end
|
290
315
|
|
291
316
|
def build_association(name, parent = @base, klass = nil)
|
292
|
-
if ::ActiveRecord::VERSION::STRING
|
293
|
-
jd = Polyamorous::JoinDependency.new(
|
294
|
-
parent.base_klass,
|
295
|
-
Polyamorous::Join.new(name, @join_type, klass),
|
296
|
-
[]
|
297
|
-
)
|
298
|
-
found_association = jd.join_root.children.last
|
299
|
-
elsif ::ActiveRecord::VERSION::STRING == Constants::RAILS_5_2_0
|
300
|
-
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, parent.table.name, [])
|
317
|
+
if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
|
301
318
|
jd = Polyamorous::JoinDependency.new(
|
302
319
|
parent.base_klass,
|
303
|
-
parent.
|
320
|
+
parent.table,
|
304
321
|
Polyamorous::Join.new(name, @join_type, klass),
|
305
|
-
|
322
|
+
@join_type
|
306
323
|
)
|
307
324
|
found_association = jd.instance_variable_get(:@join_root).children.last
|
308
325
|
else
|
309
326
|
jd = Polyamorous::JoinDependency.new(
|
310
327
|
parent.base_klass,
|
311
|
-
parent.
|
312
|
-
Polyamorous::Join.new(name, @join_type, klass)
|
328
|
+
parent.table,
|
329
|
+
Polyamorous::Join.new(name, @join_type, klass)
|
313
330
|
)
|
314
331
|
found_association = jd.instance_variable_get(:@join_root).children.last
|
315
332
|
end
|
316
333
|
|
317
|
-
|
318
334
|
@associations_pot[found_association] = parent
|
319
335
|
|
320
336
|
# TODO maybe we dont need to push associations here, we could loop
|
@@ -322,40 +338,27 @@ module Ransack
|
|
322
338
|
@join_dependency.instance_variable_get(:@join_root).children.push found_association
|
323
339
|
|
324
340
|
# Builds the arel nodes properly for this association
|
325
|
-
if ::ActiveRecord::VERSION::STRING
|
326
|
-
@join_dependency.
|
341
|
+
if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1)
|
342
|
+
@tables_pot[found_association] = @join_dependency.construct_tables_for_association!(jd.instance_variable_get(:@join_root), found_association)
|
327
343
|
else
|
328
|
-
@join_dependency.send(
|
329
|
-
:construct_tables!, jd.instance_variable_get(:@join_root), found_association
|
330
|
-
)
|
344
|
+
@join_dependency.send(:construct_tables!, jd.instance_variable_get(:@join_root))
|
331
345
|
end
|
332
346
|
|
333
347
|
# Leverage the stashed association functionality in AR
|
334
348
|
@object = @object.joins(jd)
|
335
|
-
|
336
349
|
found_association
|
337
350
|
end
|
338
351
|
|
339
352
|
def extract_joins(association)
|
340
353
|
parent = @join_dependency.instance_variable_get(:@join_root)
|
341
354
|
reflection = association.reflection
|
342
|
-
join_constraints = if ::ActiveRecord::VERSION::STRING
|
343
|
-
association.
|
344
|
-
parent.table,
|
345
|
-
parent.base_klass,
|
346
|
-
association,
|
347
|
-
Arel::Nodes::OuterJoin,
|
348
|
-
association.tables,
|
349
|
-
reflection.scope_chain,
|
350
|
-
reflection.chain
|
351
|
-
)
|
352
|
-
elsif ::ActiveRecord::VERSION::STRING <= Constants::RAILS_5_2_0
|
353
|
-
association.join_constraints(
|
355
|
+
join_constraints = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_1)
|
356
|
+
association.join_constraints_with_tables(
|
354
357
|
parent.table,
|
355
358
|
parent.base_klass,
|
356
359
|
Arel::Nodes::OuterJoin,
|
357
|
-
|
358
|
-
|
360
|
+
@join_dependency.instance_variable_get(:@alias_tracker),
|
361
|
+
@tables_pot[association]
|
359
362
|
)
|
360
363
|
else
|
361
364
|
association.join_constraints(
|
@@ -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,8 +114,11 @@ 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
|
117
|
+
when "Mysql2".freeze
|
118
|
+
# Necessary for MySQL
|
119
|
+
unescaped.to_s.gsub(/([\\%_])/, '\\\\\\1')
|
120
|
+
when "PostgreSQL".freeze
|
121
|
+
# Necessary for PostgreSQL
|
107
122
|
unescaped.to_s.gsub(/([\\%_.])/, '\\\\\\1')
|
108
123
|
else
|
109
124
|
unescaped
|
@@ -28,14 +28,10 @@ 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
|
-
@base = @join_dependency.instance_variable_get(:@join_root)
|
35
|
-
else
|
36
|
-
@base = @join_dependency.join_root
|
37
|
-
@engine = @base.base_klass.arel_engine
|
38
|
-
end
|
34
|
+
@base = @join_dependency.instance_variable_get(:@join_root)
|
39
35
|
end
|
40
36
|
|
41
37
|
def bind_pair_for(key)
|
@@ -30,7 +30,7 @@ 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
36
|
predicate.right = predicate.right.map do |pr|
|
@@ -43,16 +43,24 @@ 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
|
+
value_from(predicate).is_a?(Array) && predicate.is_a?(Arel::Nodes::Casted)
|
51
|
+
end
|
52
|
+
|
53
|
+
def value_from(predicate)
|
54
|
+
if predicate.respond_to?(:value)
|
55
|
+
predicate.value # Rails 6.1
|
56
|
+
elsif predicate.respond_to?(:val)
|
57
|
+
predicate.val # Rails 5.2, 6.0
|
58
|
+
end
|
51
59
|
end
|
52
60
|
|
53
61
|
def format_values_for(predicate)
|
54
|
-
predicate.
|
55
|
-
|
62
|
+
value_from(predicate).map do |val|
|
63
|
+
val.is_a?(String) ? Arel::Nodes.build_quoted(val) : val
|
56
64
|
end
|
57
65
|
end
|
58
66
|
|
@@ -33,7 +33,9 @@ module Ransack
|
|
33
33
|
:up_arrow => '▼'.freeze,
|
34
34
|
:down_arrow => '▲'.freeze,
|
35
35
|
:default_arrow => nil,
|
36
|
-
:sanitize_scope_args => true
|
36
|
+
:sanitize_scope_args => true,
|
37
|
+
:postgres_fields_sort_option => nil,
|
38
|
+
:strip_whitespace => true
|
37
39
|
}
|
38
40
|
|
39
41
|
def configure
|
@@ -141,6 +143,21 @@ module Ransack
|
|
141
143
|
self.options[:sanitize_scope_args] = boolean
|
142
144
|
end
|
143
145
|
|
146
|
+
# The `NULLS FIRST` and `NULLS LAST` options can be used to determine
|
147
|
+
# whether nulls appear before or after non-null values in the sort ordering.
|
148
|
+
#
|
149
|
+
# User may want to configure it like this:
|
150
|
+
#
|
151
|
+
# Ransack.configure do |c|
|
152
|
+
# c.postgres_fields_sort_option = :nulls_first # or e.g. :nulls_always_last
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# See this feature: https://www.postgresql.org/docs/13/queries-order.html
|
156
|
+
#
|
157
|
+
def postgres_fields_sort_option=(setting)
|
158
|
+
self.options[:postgres_fields_sort_option] = setting
|
159
|
+
end
|
160
|
+
|
144
161
|
# By default, Ransack displays sort order indicator arrows in sort links.
|
145
162
|
# The default may be globally overridden in an initializer file like
|
146
163
|
# `config/initializers/ransack.rb` as follows:
|
@@ -154,6 +171,19 @@ module Ransack
|
|
154
171
|
self.options[:hide_sort_order_indicators] = boolean
|
155
172
|
end
|
156
173
|
|
174
|
+
# By default, Ransack displays strips all whitespace when searching for a string.
|
175
|
+
# The default may be globally changed in an initializer file like
|
176
|
+
# `config/initializers/ransack.rb` as follows:
|
177
|
+
#
|
178
|
+
# Ransack.configure do |config|
|
179
|
+
# # Enable whitespace stripping for string searches
|
180
|
+
# config.strip_whitespace = true
|
181
|
+
# end
|
182
|
+
#
|
183
|
+
def strip_whitespace=(boolean)
|
184
|
+
self.options[:strip_whitespace] = boolean
|
185
|
+
end
|
186
|
+
|
157
187
|
def arel_predicate_with_suffix(arel_predicate, suffix)
|
158
188
|
if arel_predicate === Proc
|
159
189
|
proc { |v| "#{arel_predicate.call(v)}#{suffix}" }
|
data/lib/ransack/constants.rb
CHANGED
@@ -36,7 +36,7 @@ module Ransack
|
|
36
36
|
'lt'.freeze, 'lteq'.freeze,
|
37
37
|
'gt'.freeze, 'gteq'.freeze,
|
38
38
|
'in'.freeze, 'not_in'.freeze
|
39
|
-
|
39
|
+
].freeze
|
40
40
|
A_S_I = ['a'.freeze, 's'.freeze, 'i'.freeze].freeze
|
41
41
|
|
42
42
|
EQ = 'eq'.freeze
|
@@ -45,12 +45,10 @@ module Ransack
|
|
45
45
|
NOT_EQ_ALL = 'not_eq_all'.freeze
|
46
46
|
CONT = 'cont'.freeze
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
RAILS_5_2_0 = '5.2.0'.freeze
|
48
|
+
RAILS_6_0 = '6.0.0'.freeze
|
49
|
+
RAILS_6_1 = '6.1.0'.freeze
|
51
50
|
|
52
51
|
RANSACK_SLASH_SEARCHES = 'ransack/searches'.freeze
|
53
52
|
RANSACK_SLASH_SEARCHES_SLASH_SEARCH = 'ransack/searches/search'.freeze
|
54
53
|
end
|
55
54
|
end
|
56
|
-
|