ransack 2.3.0 → 4.1.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/codeql.yml +72 -0
- data/.github/workflows/cronjob.yml +99 -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 +131 -0
- data/.nojekyll +0 -0
- data/.rubocop.yml +50 -0
- data/CHANGELOG.md +263 -1
- data/CONTRIBUTING.md +51 -29
- data/Gemfile +24 -10
- data/README.md +49 -917
- data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +78 -0
- data/bug_report_templates/test-ransacker-arel-present-predicate.rb +75 -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 +288 -0
- data/docs/docs/getting-started/sorting.md +71 -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 +46 -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 +42 -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/yarn.lock +8879 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +70 -0
- data/{polyamorous/lib/polyamorous/activerecord_6.0_ruby_2 → lib/polyamorous/activerecord_6.1_ruby_2}/join_dependency.rb +33 -12
- 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/{polyamorous/lib → lib/polyamorous}/polyamorous.rb +3 -8
- data/lib/ransack/adapters/active_record/base.rb +83 -10
- data/lib/ransack/adapters/active_record/context.rb +61 -116
- data/lib/ransack/configuration.rb +53 -10
- data/lib/ransack/constants.rb +126 -7
- data/lib/ransack/context.rb +34 -5
- data/lib/ransack/helpers/form_builder.rb +11 -17
- data/lib/ransack/helpers/form_helper.rb +14 -5
- data/lib/ransack/helpers.rb +1 -1
- data/lib/ransack/locale/sk.yml +70 -0
- data/lib/ransack/locale/sv.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +3 -3
- data/lib/ransack/nodes/condition.rb +87 -8
- data/lib/ransack/nodes/grouping.rb +4 -4
- data/lib/ransack/nodes/node.rb +1 -1
- data/lib/ransack/nodes/sort.rb +3 -3
- data/lib/ransack/nodes/value.rb +3 -3
- data/lib/ransack/predicate.rb +3 -2
- data/lib/ransack/ransacker.rb +1 -1
- data/lib/ransack/search.rb +15 -7
- data/lib/ransack/translate.rb +6 -6
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +38 -2
- data/lib/ransack.rb +6 -10
- data/ransack.gemspec +9 -24
- 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 +2 -17
- data/spec/helpers/ransack_helper.rb +1 -1
- data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
- data/spec/{ransack → polyamorous}/join_association_spec.rb +3 -1
- data/spec/{ransack → polyamorous}/join_dependency_spec.rb +0 -16
- data/spec/ransack/adapters/active_record/base_spec.rb +125 -16
- data/spec/ransack/adapters/active_record/context_spec.rb +19 -18
- data/spec/ransack/configuration_spec.rb +33 -9
- data/spec/ransack/helpers/form_builder_spec.rb +8 -8
- data/spec/ransack/helpers/form_helper_spec.rb +109 -20
- data/spec/ransack/nodes/condition_spec.rb +37 -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 +75 -2
- data/spec/ransack/search_spec.rb +239 -38
- data/spec/ransack/translate_spec.rb +1 -1
- data/spec/spec_helper.rb +9 -5
- data/spec/support/schema.rb +111 -12
- metadata +105 -195
- data/.travis.yml +0 -49
- data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -116
- data/lib/ransack/adapters/active_record/ransack/context.rb +0 -60
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +0 -61
- data/lib/ransack/adapters/active_record/ransack/translate.rb +0 -8
- data/lib/ransack/adapters/active_record/ransack/visitor.rb +0 -47
- data/lib/ransack/adapters.rb +0 -64
- data/lib/ransack/nodes.rb +0 -8
- data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_association.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_5.0_ruby_2/join_dependency.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_association.rb +0 -31
- data/polyamorous/lib/polyamorous/activerecord_5.1_ruby_2/join_dependency.rb +0 -112
- data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_association.rb +0 -31
- data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/join_dependency.rb +0 -112
- data/polyamorous/lib/polyamorous/activerecord_5.2.0_ruby_2/reflection.rb +0 -12
- data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_association.rb +0 -22
- data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/join_dependency.rb +0 -81
- data/polyamorous/lib/polyamorous/activerecord_5.2.1_ruby_2/reflection.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -2
- data/polyamorous/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +0 -2
- data/polyamorous/lib/polyamorous/version.rb +0 -3
- data/polyamorous/polyamorous.gemspec +0 -35
- /data/{logo → docs/static/logo}/ransack-h.png +0 -0
- /data/{logo → docs/static/logo}/ransack-h.svg +0 -0
- /data/{logo → docs/static/logo}/ransack-v.png +0 -0
- /data/{logo → docs/static/logo}/ransack-v.svg +0 -0
- /data/{logo → docs/static/logo}/ransack.png +0 -0
- /data/{logo → docs/static/logo}/ransack.svg +0 -0
- /data/{polyamorous/lib → lib}/polyamorous/join.rb +0 -0
- /data/{polyamorous/lib → lib}/polyamorous/swapping_reflection_class.rb +0 -0
- /data/{polyamorous/lib → lib}/polyamorous/tree_node.rb +0 -0
- /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
- /data/spec/{ransack → polyamorous}/join_spec.rb +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
end
|
|
70
|
+
end
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# active_record_6.0_ruby_2/join_dependency.rb
|
|
2
|
-
|
|
3
1
|
module Polyamorous
|
|
4
2
|
module JoinDependencyExtensions
|
|
5
3
|
# Replaces ActiveRecord::Associations::JoinDependency#build
|
|
@@ -29,14 +27,18 @@ module Polyamorous
|
|
|
29
27
|
end
|
|
30
28
|
end
|
|
31
29
|
|
|
32
|
-
def join_constraints(joins_to_add, alias_tracker)
|
|
30
|
+
def join_constraints(joins_to_add, alias_tracker, references)
|
|
33
31
|
@alias_tracker = alias_tracker
|
|
32
|
+
@joined_tables = {}
|
|
33
|
+
@references = {}
|
|
34
|
+
|
|
35
|
+
references.each do |table_name|
|
|
36
|
+
@references[table_name.to_sym] = table_name if table_name.is_a?(String)
|
|
37
|
+
end
|
|
34
38
|
|
|
35
|
-
construct_tables!(join_root)
|
|
36
39
|
joins = make_join_constraints(join_root, join_type)
|
|
37
40
|
|
|
38
41
|
joins.concat joins_to_add.flat_map { |oj|
|
|
39
|
-
construct_tables!(oj.join_root)
|
|
40
42
|
if join_root.match?(oj.join_root) && join_root.table.name == oj.join_root.table.name
|
|
41
43
|
walk join_root, oj.join_root, oj.join_type
|
|
42
44
|
else
|
|
@@ -45,14 +47,33 @@ module Polyamorous
|
|
|
45
47
|
}
|
|
46
48
|
end
|
|
47
49
|
|
|
50
|
+
def construct_tables_for_association!(join_root, association)
|
|
51
|
+
tables = table_aliases_for(join_root, association)
|
|
52
|
+
association.table = tables.first
|
|
53
|
+
tables
|
|
54
|
+
end
|
|
55
|
+
|
|
48
56
|
private
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
|
|
58
|
+
def table_aliases_for(parent, node)
|
|
59
|
+
@joined_tables ||= {}
|
|
60
|
+
node.reflection.chain.map { |reflection|
|
|
61
|
+
table, terminated = @joined_tables[reflection]
|
|
62
|
+
root = reflection == node.reflection
|
|
63
|
+
|
|
64
|
+
if table && (!root || !terminated)
|
|
65
|
+
@joined_tables[reflection] = [table, true] if root
|
|
66
|
+
table
|
|
67
|
+
else
|
|
68
|
+
table = alias_tracker.aliased_table_for(reflection.klass.arel_table) do
|
|
69
|
+
name = reflection.alias_candidate(parent.table_name)
|
|
70
|
+
root ? name : "#{name}_join"
|
|
71
|
+
end
|
|
72
|
+
@joined_tables[reflection] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
|
|
73
|
+
table
|
|
74
|
+
end
|
|
75
|
+
}
|
|
76
|
+
end
|
|
56
77
|
|
|
57
78
|
module ClassMethods
|
|
58
79
|
# Prepended before ActiveRecord::Associations::JoinDependency#walk_tree
|
|
@@ -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 @@
|
|
|
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'
|
|
@@ -11,17 +11,12 @@ if defined?(::ActiveRecord)
|
|
|
11
11
|
require 'polyamorous/join'
|
|
12
12
|
require 'polyamorous/swapping_reflection_class'
|
|
13
13
|
|
|
14
|
-
ar_version = ::ActiveRecord::VERSION::STRING[0,3]
|
|
15
|
-
|
|
16
|
-
ar_version = "5.2.1" if ::ActiveRecord::VERSION::STRING >= "5.2.1" && ::ActiveRecord.version < ::Gem::Version.new("6.0")
|
|
17
|
-
%w(join_association join_dependency).each do |file|
|
|
14
|
+
ar_version = ::ActiveRecord::VERSION::STRING[0, 3]
|
|
15
|
+
%w(join_association join_dependency reflection).each do |file|
|
|
18
16
|
require "polyamorous/activerecord_#{ar_version}_ruby_2/#{file}"
|
|
19
17
|
end
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
require "polyamorous/activerecord_#{ar_version}_ruby_2/reflection.rb"
|
|
23
|
-
::ActiveRecord::Reflection::AbstractReflection.send(:prepend, Polyamorous::ReflectionExtensions)
|
|
24
|
-
end
|
|
19
|
+
ActiveRecord::Reflection::AbstractReflection.send(:prepend, Polyamorous::ReflectionExtensions)
|
|
25
20
|
|
|
26
21
|
Polyamorous::JoinDependency.send(:prepend, Polyamorous::JoinDependencyExtensions)
|
|
27
22
|
Polyamorous::JoinDependency.singleton_class.send(:prepend, Polyamorous::JoinDependencyExtensions::ClassMethods)
|
|
@@ -4,7 +4,6 @@ module Ransack
|
|
|
4
4
|
module Base
|
|
5
5
|
|
|
6
6
|
def self.extended(base)
|
|
7
|
-
alias :search :ransack unless base.respond_to? :search
|
|
8
7
|
base.class_eval do
|
|
9
8
|
class_attribute :_ransackers
|
|
10
9
|
class_attribute :_ransack_aliases
|
|
@@ -14,10 +13,13 @@ module Ransack
|
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
def ransack(params = {}, options = {})
|
|
17
|
-
ActiveSupport::Deprecation.warn("#search is deprecated and will be removed in 2.3, please use #ransack instead") if __callee__ == :search
|
|
18
16
|
Search.new(self, params, options)
|
|
19
17
|
end
|
|
20
18
|
|
|
19
|
+
def ransack!(params = {}, options = {})
|
|
20
|
+
ransack(params, options.merge(ignore_unknown_conditions: false))
|
|
21
|
+
end
|
|
22
|
+
|
|
21
23
|
def ransacker(name, opts = {}, &block)
|
|
22
24
|
self._ransackers = _ransackers.merge name.to_s => Ransacker
|
|
23
25
|
.new(self, name, opts, &block)
|
|
@@ -33,12 +35,7 @@ module Ransack
|
|
|
33
35
|
# For overriding with a whitelist array of strings.
|
|
34
36
|
#
|
|
35
37
|
def ransackable_attributes(auth_object = nil)
|
|
36
|
-
@ransackable_attributes ||=
|
|
37
|
-
column_names + _ransackers.keys + _ransack_aliases.keys +
|
|
38
|
-
attribute_aliases.keys
|
|
39
|
-
else
|
|
40
|
-
column_names + _ransackers.keys + _ransack_aliases.keys
|
|
41
|
-
end
|
|
38
|
+
@ransackable_attributes ||= deprecated_ransackable_list(:ransackable_attributes)
|
|
42
39
|
end
|
|
43
40
|
|
|
44
41
|
# Ransackable_associations, by default, returns the names
|
|
@@ -46,7 +43,7 @@ module Ransack
|
|
|
46
43
|
# For overriding with a whitelist array of strings.
|
|
47
44
|
#
|
|
48
45
|
def ransackable_associations(auth_object = nil)
|
|
49
|
-
@ransackable_associations ||=
|
|
46
|
+
@ransackable_associations ||= deprecated_ransackable_list(:ransackable_associations)
|
|
50
47
|
end
|
|
51
48
|
|
|
52
49
|
# Ransortable_attributes, by default, returns the names
|
|
@@ -66,13 +63,89 @@ module Ransack
|
|
|
66
63
|
end
|
|
67
64
|
|
|
68
65
|
# ransack_scope_skip_sanitize_args, by default, returns an empty array.
|
|
69
|
-
# i.e. use the sanitize_scope_args setting to
|
|
66
|
+
# i.e. use the sanitize_scope_args setting to determine if args should be converted.
|
|
70
67
|
# For overriding with a list of scopes which should be passed the args as-is.
|
|
71
68
|
#
|
|
72
69
|
def ransackable_scopes_skip_sanitize_args
|
|
73
70
|
[]
|
|
74
71
|
end
|
|
75
72
|
|
|
73
|
+
# Bare list of all potentially searchable attributes. Searchable attributes
|
|
74
|
+
# need to be explicitly allowlisted through the `ransackable_attributes`
|
|
75
|
+
# method in each model, but if you're allowing almost everything to be
|
|
76
|
+
# searched, this list can be used as a base for exclusions.
|
|
77
|
+
#
|
|
78
|
+
def authorizable_ransackable_attributes
|
|
79
|
+
if Ransack::SUPPORTS_ATTRIBUTE_ALIAS
|
|
80
|
+
column_names + _ransackers.keys + _ransack_aliases.keys +
|
|
81
|
+
attribute_aliases.keys
|
|
82
|
+
else
|
|
83
|
+
column_names + _ransackers.keys + _ransack_aliases.keys
|
|
84
|
+
end.uniq
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Bare list of all potentially searchable associations. Searchable
|
|
88
|
+
# associations need to be explicitly allowlisted through the
|
|
89
|
+
# `ransackable_associations` method in each model, but if you're
|
|
90
|
+
# allowing almost everything to be searched, this list can be used as a
|
|
91
|
+
# base for exclusions.
|
|
92
|
+
#
|
|
93
|
+
def authorizable_ransackable_associations
|
|
94
|
+
reflect_on_all_associations.map { |a| a.name.to_s }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def deprecated_ransackable_list(method)
|
|
100
|
+
list_type = method.to_s.delete_prefix("ransackable_")
|
|
101
|
+
|
|
102
|
+
if explicitly_defined?(method)
|
|
103
|
+
warn_deprecated <<~ERROR
|
|
104
|
+
Ransack's builtin `#{method}` method is deprecated and will result
|
|
105
|
+
in an error in the future. If you want to authorize the full list
|
|
106
|
+
of searchable #{list_type} for this model, use
|
|
107
|
+
`authorizable_#{method}` instead of delegating to `super`.
|
|
108
|
+
ERROR
|
|
109
|
+
|
|
110
|
+
public_send("authorizable_#{method}")
|
|
111
|
+
else
|
|
112
|
+
raise <<~MESSAGE
|
|
113
|
+
Ransack needs #{name} #{list_type} explicitly allowlisted as
|
|
114
|
+
searchable. Define a `#{method}` class method in your `#{name}`
|
|
115
|
+
model, watching out for items you DON'T want searchable (for
|
|
116
|
+
example, `encrypted_password`, `password_reset_token`, `owner` or
|
|
117
|
+
other sensitive information). You can use the following as a base:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
class #{name} < ApplicationRecord
|
|
121
|
+
|
|
122
|
+
# ...
|
|
123
|
+
|
|
124
|
+
def self.#{method}(auth_object = nil)
|
|
125
|
+
#{public_send("authorizable_#{method}").sort.inspect}
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# ...
|
|
129
|
+
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
MESSAGE
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def explicitly_defined?(method)
|
|
137
|
+
definer_ancestor = singleton_class.ancestors.find do |ancestor|
|
|
138
|
+
ancestor.instance_methods(false).include?(method)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
definer_ancestor != Ransack::Adapters::ActiveRecord::Base
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def warn_deprecated(message)
|
|
145
|
+
caller_location = caller_locations.find { |location| !location.path.start_with?(File.expand_path("../..", __dir__)) }
|
|
146
|
+
|
|
147
|
+
warn "DEPRECATION WARNING: #{message.squish} (called at #{caller_location.path}:#{caller_location.lineno})"
|
|
148
|
+
end
|
|
76
149
|
end
|
|
77
150
|
end
|
|
78
151
|
end
|
|
@@ -1,26 +1,20 @@
|
|
|
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
|
|
19
12
|
|
|
20
13
|
def type_for(attr)
|
|
21
14
|
return nil unless attr && attr.valid?
|
|
15
|
+
relation = attr.arel_attribute.relation
|
|
22
16
|
name = attr.arel_attribute.name.to_s
|
|
23
|
-
table =
|
|
17
|
+
table = relation.respond_to?(:table_name) ? relation.table_name : relation.name
|
|
24
18
|
schema_cache = self.klass.connection.schema_cache
|
|
25
19
|
unless schema_cache.send(:data_source_exists?, table)
|
|
26
20
|
raise "No table named #{table} exists."
|
|
@@ -49,6 +43,17 @@ module Ransack
|
|
|
49
43
|
if scope_or_sort.is_a?(Symbol)
|
|
50
44
|
relation = relation.send(scope_or_sort)
|
|
51
45
|
else
|
|
46
|
+
case Ransack.options[:postgres_fields_sort_option]
|
|
47
|
+
when :nulls_first
|
|
48
|
+
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")
|
|
49
|
+
when :nulls_last
|
|
50
|
+
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")
|
|
51
|
+
when :nulls_always_first
|
|
52
|
+
scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS FIRST")
|
|
53
|
+
when :nulls_always_last
|
|
54
|
+
scope_or_sort = Arel.sql("#{scope_or_sort.to_sql} NULLS LAST")
|
|
55
|
+
end
|
|
56
|
+
|
|
52
57
|
relation = relation.order(scope_or_sort)
|
|
53
58
|
end
|
|
54
59
|
end
|
|
@@ -104,26 +109,15 @@ module Ransack
|
|
|
104
109
|
# JoinDependency to track table aliases.
|
|
105
110
|
#
|
|
106
111
|
def join_sources
|
|
107
|
-
base, joins =
|
|
108
|
-
if ::ActiveRecord::VERSION::STRING > Constants::RAILS_5_2_0
|
|
112
|
+
base, joins = begin
|
|
109
113
|
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, @object.table.name, [])
|
|
110
|
-
constraints =
|
|
111
|
-
@join_dependency.join_constraints(@object.joins_values, alias_tracker)
|
|
112
|
-
else
|
|
113
|
-
@join_dependency.join_constraints(@object.joins_values, @join_type, alias_tracker)
|
|
114
|
-
end
|
|
114
|
+
constraints = @join_dependency.join_constraints(@object.joins_values, alias_tracker, @object.references_values)
|
|
115
115
|
|
|
116
116
|
[
|
|
117
117
|
Arel::SelectManager.new(@object.table),
|
|
118
118
|
constraints
|
|
119
119
|
]
|
|
120
|
-
else
|
|
121
|
-
[
|
|
122
|
-
Arel::SelectManager.new(@object.table),
|
|
123
|
-
@join_dependency.join_constraints(@object.joins_values, @join_type)
|
|
124
|
-
]
|
|
125
120
|
end
|
|
126
|
-
joins = joins.collect(&:joins).flatten if ::ActiveRecord::VERSION::STRING < Constants::RAILS_5_2
|
|
127
121
|
joins.each do |aliased_join|
|
|
128
122
|
base.from(aliased_join)
|
|
129
123
|
end
|
|
@@ -144,6 +138,7 @@ module Ransack
|
|
|
144
138
|
stashed.eql?(association)
|
|
145
139
|
}
|
|
146
140
|
@object.joins_values.delete_if { |jd|
|
|
141
|
+
jd.instance_variables.include?(:@join_root) &&
|
|
147
142
|
jd.instance_variable_get(:@join_root).children.map(&:object_id) == [association.object_id]
|
|
148
143
|
}
|
|
149
144
|
end
|
|
@@ -186,16 +181,31 @@ module Ransack
|
|
|
186
181
|
private
|
|
187
182
|
|
|
188
183
|
def extract_correlated_key(join_root)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
184
|
+
case join_root
|
|
185
|
+
when Arel::Nodes::OuterJoin
|
|
186
|
+
# one of join_root.right/join_root.left is expected to be Arel::Nodes::On
|
|
187
|
+
if join_root.right.is_a?(Arel::Nodes::On)
|
|
188
|
+
extract_correlated_key(join_root.right.expr)
|
|
189
|
+
elsif join_root.left.is_a?(Arel::Nodes::On)
|
|
190
|
+
extract_correlated_key(join_root.left.expr)
|
|
191
|
+
else
|
|
192
|
+
raise 'Ransack encountered an unexpected arel structure'
|
|
193
|
+
end
|
|
194
|
+
when Arel::Nodes::Equality
|
|
195
|
+
pk = primary_key
|
|
196
|
+
if join_root.left == pk
|
|
197
|
+
join_root.right
|
|
198
|
+
elsif join_root.right == pk
|
|
199
|
+
join_root.left
|
|
200
|
+
else
|
|
201
|
+
nil
|
|
202
|
+
end
|
|
203
|
+
when Arel::Nodes::And
|
|
204
|
+
extract_correlated_key(join_root.left) || extract_correlated_key(join_root.right)
|
|
197
205
|
else
|
|
198
|
-
|
|
206
|
+
# eg parent was Arel::Nodes::And and the evaluated side was one of
|
|
207
|
+
# Arel::Nodes::Grouping or MultiTenant::TenantEnforcementClause
|
|
208
|
+
nil
|
|
199
209
|
end
|
|
200
210
|
end
|
|
201
211
|
|
|
@@ -268,28 +278,11 @@ module Ransack
|
|
|
268
278
|
|
|
269
279
|
join_list = join_nodes + convert_join_strings_to_ast(relation.table, string_joins)
|
|
270
280
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
elsif ::ActiveRecord::VERSION::STRING == Constants::RAILS_5_2_0
|
|
277
|
-
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
|
|
278
|
-
join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, alias_tracker)
|
|
279
|
-
join_nodes.each do |join|
|
|
280
|
-
join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
|
|
281
|
-
end
|
|
282
|
-
else
|
|
283
|
-
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
|
|
284
|
-
join_dependency = if ::Gem::Version.new(::ActiveRecord::VERSION::STRING) >= ::Gem::Version.new(Constants::RAILS_6_0)
|
|
285
|
-
Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin)
|
|
286
|
-
else
|
|
287
|
-
Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins)
|
|
288
|
-
end
|
|
289
|
-
join_dependency.instance_variable_set(:@alias_tracker, alias_tracker)
|
|
290
|
-
join_nodes.each do |join|
|
|
291
|
-
join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
|
|
292
|
-
end
|
|
281
|
+
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, relation.table.name, join_list)
|
|
282
|
+
join_dependency = Polyamorous::JoinDependency.new(relation.klass, relation.table, association_joins, Arel::Nodes::OuterJoin)
|
|
283
|
+
join_dependency.instance_variable_set(:@alias_tracker, alias_tracker)
|
|
284
|
+
join_nodes.each do |join|
|
|
285
|
+
join_dependency.send(:alias_tracker).aliases[join.left.name.downcase] = 1
|
|
293
286
|
end
|
|
294
287
|
join_dependency
|
|
295
288
|
end
|
|
@@ -313,38 +306,13 @@ module Ransack
|
|
|
313
306
|
end
|
|
314
307
|
|
|
315
308
|
def build_association(name, parent = @base, klass = nil)
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
found_association = jd.instance_variable_get(:@join_root).children.last
|
|
324
|
-
elsif ::Gem::Version.new(::ActiveRecord::VERSION::STRING) < ::Gem::Version.new(Constants::RAILS_5_2_0)
|
|
325
|
-
jd = Polyamorous::JoinDependency.new(
|
|
326
|
-
parent.base_klass,
|
|
327
|
-
Polyamorous::Join.new(name, @join_type, klass),
|
|
328
|
-
[]
|
|
329
|
-
)
|
|
330
|
-
found_association = jd.join_root.children.last
|
|
331
|
-
elsif ::ActiveRecord::VERSION::STRING == Constants::RAILS_5_2_0
|
|
332
|
-
alias_tracker = ::ActiveRecord::Associations::AliasTracker.create(self.klass.connection, parent.table.name, [])
|
|
333
|
-
jd = Polyamorous::JoinDependency.new(
|
|
334
|
-
parent.base_klass,
|
|
335
|
-
parent.table,
|
|
336
|
-
Polyamorous::Join.new(name, @join_type, klass),
|
|
337
|
-
alias_tracker
|
|
338
|
-
)
|
|
339
|
-
found_association = jd.instance_variable_get(:@join_root).children.last
|
|
340
|
-
else
|
|
341
|
-
jd = Polyamorous::JoinDependency.new(
|
|
342
|
-
parent.base_klass,
|
|
343
|
-
parent.table,
|
|
344
|
-
Polyamorous::Join.new(name, @join_type, klass)
|
|
345
|
-
)
|
|
346
|
-
found_association = jd.instance_variable_get(:@join_root).children.last
|
|
347
|
-
end
|
|
309
|
+
jd = Polyamorous::JoinDependency.new(
|
|
310
|
+
parent.base_klass,
|
|
311
|
+
parent.table,
|
|
312
|
+
Polyamorous::Join.new(name, @join_type, klass),
|
|
313
|
+
@join_type
|
|
314
|
+
)
|
|
315
|
+
found_association = jd.instance_variable_get(:@join_root).children.last
|
|
348
316
|
|
|
349
317
|
@associations_pot[found_association] = parent
|
|
350
318
|
|
|
@@ -353,11 +321,7 @@ module Ransack
|
|
|
353
321
|
@join_dependency.instance_variable_get(:@join_root).children.push found_association
|
|
354
322
|
|
|
355
323
|
# Builds the arel nodes properly for this association
|
|
356
|
-
|
|
357
|
-
@join_dependency.send(:construct_tables!, jd.instance_variable_get(:@join_root))
|
|
358
|
-
else
|
|
359
|
-
@join_dependency.send(:construct_tables!, jd.instance_variable_get(:@join_root), found_association)
|
|
360
|
-
end
|
|
324
|
+
@tables_pot[found_association] = @join_dependency.construct_tables_for_association!(jd.instance_variable_get(:@join_root), found_association)
|
|
361
325
|
|
|
362
326
|
# Leverage the stashed association functionality in AR
|
|
363
327
|
@object = @object.joins(jd)
|
|
@@ -367,32 +331,13 @@ module Ransack
|
|
|
367
331
|
def extract_joins(association)
|
|
368
332
|
parent = @join_dependency.instance_variable_get(:@join_root)
|
|
369
333
|
reflection = association.reflection
|
|
370
|
-
join_constraints =
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
reflection.scope_chain,
|
|
378
|
-
reflection.chain
|
|
379
|
-
)
|
|
380
|
-
elsif ::ActiveRecord::VERSION::STRING <= Constants::RAILS_5_2_0
|
|
381
|
-
association.join_constraints(
|
|
382
|
-
parent.table,
|
|
383
|
-
parent.base_klass,
|
|
384
|
-
Arel::Nodes::OuterJoin,
|
|
385
|
-
association.tables,
|
|
386
|
-
reflection.chain
|
|
387
|
-
)
|
|
388
|
-
else
|
|
389
|
-
association.join_constraints(
|
|
390
|
-
parent.table,
|
|
391
|
-
parent.base_klass,
|
|
392
|
-
Arel::Nodes::OuterJoin,
|
|
393
|
-
@join_dependency.instance_variable_get(:@alias_tracker)
|
|
394
|
-
)
|
|
395
|
-
end
|
|
334
|
+
join_constraints = association.join_constraints_with_tables(
|
|
335
|
+
parent.table,
|
|
336
|
+
parent.base_klass,
|
|
337
|
+
Arel::Nodes::OuterJoin,
|
|
338
|
+
@join_dependency.instance_variable_get(:@alias_tracker),
|
|
339
|
+
@tables_pot[association]
|
|
340
|
+
)
|
|
396
341
|
join_constraints.to_a.flatten
|
|
397
342
|
end
|
|
398
343
|
end
|