ransack 2.3.2 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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 +251 -1
- data/CONTRIBUTING.md +51 -29
- data/Gemfile +12 -10
- data/README.md +45 -907
- 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/join_association.rb +70 -0
- data/{polyamorous/lib/polyamorous/activerecord_6.0_ruby_2 → lib/polyamorous/activerecord}/join_dependency.rb +33 -12
- data/lib/polyamorous/activerecord/reflection.rb +11 -0
- data/{polyamorous/lib → lib/polyamorous}/polyamorous.rb +3 -4
- data/lib/ransack/adapters/active_record/base.rb +83 -10
- data/lib/ransack/adapters/active_record/context.rb +56 -44
- data/lib/ransack/configuration.rb +53 -10
- data/lib/ransack/constants.rb +126 -4
- data/lib/ransack/context.rb +34 -5
- data/lib/ransack/helpers/form_builder.rb +6 -6
- data/lib/ransack/helpers/form_helper.rb +14 -5
- data/lib/ransack/helpers.rb +1 -1
- data/lib/ransack/locale/sv.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +3 -3
- data/lib/ransack/nodes/condition.rb +80 -9
- 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 +1 -1
- 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 +5 -8
- data/ransack.gemspec +9 -15
- 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 -8
- 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 +37 -2
- data/spec/ransack/search_spec.rb +238 -30
- data/spec/ransack/translate_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -5
- data/spec/support/schema.rb +108 -11
- metadata +98 -62
- data/.travis.yml +0 -47
- data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -128
- data/lib/ransack/adapters/active_record/ransack/context.rb +0 -55
- 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.2_ruby_2/join_association.rb +0 -20
- data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -79
- data/polyamorous/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -12
- 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 -27
- /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
data/lib/ransack/translate.rb
CHANGED
|
@@ -32,6 +32,7 @@ module Ransack
|
|
|
32
32
|
defaults = base_ancestors.map do |klass|
|
|
33
33
|
"ransack.attributes.#{i18n_key(klass)}.#{original_name}".to_sym
|
|
34
34
|
end
|
|
35
|
+
defaults << options.delete(:default) if options[:default]
|
|
35
36
|
|
|
36
37
|
translated_names = attribute_names.map do |name|
|
|
37
38
|
attribute_name(context, name, options[:include_associations])
|
|
@@ -48,9 +49,8 @@ module Ransack
|
|
|
48
49
|
defaults << "%{attributes}".freeze
|
|
49
50
|
end
|
|
50
51
|
|
|
51
|
-
defaults << options.delete(:default) if options[:default]
|
|
52
52
|
options.reverse_merge! count: 1, default: defaults
|
|
53
|
-
I18n.translate(defaults.shift, options.merge(interpolations))
|
|
53
|
+
I18n.translate(defaults.shift, **options.merge(interpolations))
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def association(key, options = {})
|
|
@@ -66,8 +66,8 @@ module Ransack
|
|
|
66
66
|
[:"ransack.associations.#{i18n_key(context.klass)}.#{key}"]
|
|
67
67
|
end
|
|
68
68
|
defaults << context.traverse(key).model_name.human
|
|
69
|
-
options = { :
|
|
70
|
-
I18n.translate(defaults.shift, options)
|
|
69
|
+
options = { count: 1, default: defaults }
|
|
70
|
+
I18n.translate(defaults.shift, **options)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
private
|
|
@@ -83,7 +83,7 @@ module Ransack
|
|
|
83
83
|
options = { count: 1, default: defaults }
|
|
84
84
|
interpolations = build_interpolations(associated_class)
|
|
85
85
|
|
|
86
|
-
I18n.translate(defaults.shift, options.merge(interpolations))
|
|
86
|
+
I18n.translate(defaults.shift, **options.merge(interpolations))
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def default_attribute_name
|
|
@@ -149,7 +149,7 @@ module Ransack
|
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
def i18n_key(klass)
|
|
152
|
-
|
|
152
|
+
klass.model_name.i18n_key
|
|
153
153
|
end
|
|
154
154
|
end
|
|
155
155
|
end
|
data/lib/ransack/version.rb
CHANGED
data/lib/ransack/visitor.rb
CHANGED
|
@@ -26,7 +26,14 @@ module Ransack
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def visit_and(object)
|
|
29
|
-
|
|
29
|
+
nodes = object.values.map { |o| accept(o) }.compact
|
|
30
|
+
return nil unless nodes.size > 0
|
|
31
|
+
|
|
32
|
+
if nodes.size > 1
|
|
33
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::And.new(nodes))
|
|
34
|
+
else
|
|
35
|
+
nodes.first
|
|
36
|
+
end
|
|
30
37
|
end
|
|
31
38
|
|
|
32
39
|
def visit_or(object)
|
|
@@ -35,17 +42,46 @@ module Ransack
|
|
|
35
42
|
end
|
|
36
43
|
|
|
37
44
|
def quoted?(object)
|
|
38
|
-
|
|
45
|
+
case object
|
|
46
|
+
when Arel::Nodes::SqlLiteral, Bignum, Fixnum
|
|
47
|
+
false
|
|
48
|
+
else
|
|
49
|
+
true
|
|
50
|
+
end
|
|
39
51
|
end
|
|
40
52
|
|
|
41
53
|
def visit(object)
|
|
42
54
|
send(DISPATCH[object.class], object)
|
|
43
55
|
end
|
|
44
56
|
|
|
57
|
+
def visit_Ransack_Nodes_Sort(object)
|
|
58
|
+
if object.valid?
|
|
59
|
+
if object.attr.is_a?(Arel::Attributes::Attribute)
|
|
60
|
+
object.attr.send(object.dir)
|
|
61
|
+
else
|
|
62
|
+
ordered(object)
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
scope_name = :"sort_by_#{object.name}_#{object.dir}"
|
|
66
|
+
scope_name if object.context.object.respond_to?(scope_name)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
45
70
|
DISPATCH = Hash.new do |hash, klass|
|
|
46
71
|
hash[klass] = "visit_#{
|
|
47
72
|
klass.name.gsub(Constants::TWO_COLONS, Constants::UNDERSCORE)
|
|
48
73
|
}"
|
|
49
74
|
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def ordered(object)
|
|
79
|
+
case object.dir
|
|
80
|
+
when 'asc'.freeze
|
|
81
|
+
Arel::Nodes::Ascending.new(object.attr)
|
|
82
|
+
when 'desc'.freeze
|
|
83
|
+
Arel::Nodes::Descending.new(object.attr)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
50
86
|
end
|
|
51
87
|
end
|
data/lib/ransack.rb
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
require 'active_support/core_ext'
|
|
2
2
|
require 'ransack/configuration'
|
|
3
|
-
require '
|
|
4
|
-
require 'polyamorous'
|
|
5
|
-
|
|
6
|
-
Ransack::Adapters.object_mapper.require_constants
|
|
3
|
+
require 'polyamorous/polyamorous'
|
|
7
4
|
|
|
8
5
|
module Ransack
|
|
9
6
|
extend Configuration
|
|
10
|
-
class UntraversableAssociationError < StandardError; end
|
|
7
|
+
class UntraversableAssociationError < StandardError; end
|
|
11
8
|
end
|
|
12
9
|
|
|
13
10
|
Ransack.configure do |config|
|
|
14
11
|
Ransack::Constants::AREL_PREDICATES.each do |name|
|
|
15
|
-
config.add_predicate name, :
|
|
12
|
+
config.add_predicate name, arel_predicate: name
|
|
16
13
|
end
|
|
17
14
|
Ransack::Constants::DERIVED_PREDICATES.each do |args|
|
|
18
15
|
config.add_predicate(*args)
|
|
@@ -22,8 +19,8 @@ end
|
|
|
22
19
|
require 'ransack/search'
|
|
23
20
|
require 'ransack/ransacker'
|
|
24
21
|
require 'ransack/translate'
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
require 'ransack/active_record'
|
|
23
|
+
require 'ransack/context'
|
|
27
24
|
|
|
28
25
|
ActiveSupport.on_load(:action_controller) do
|
|
29
26
|
require 'ransack/helpers'
|
data/ransack.gemspec
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
2
3
|
$:.push File.expand_path("../lib", __FILE__)
|
|
3
4
|
require "ransack/version"
|
|
4
5
|
|
|
@@ -6,27 +7,20 @@ Gem::Specification.new do |s|
|
|
|
6
7
|
s.name = "ransack"
|
|
7
8
|
s.version = Ransack::VERSION
|
|
8
9
|
s.platform = Gem::Platform::RUBY
|
|
9
|
-
s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack","Sean Carroll"]
|
|
10
|
-
s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com","sfcarroll@gmail.com"]
|
|
10
|
+
s.authors = ["Ernie Miller", "Ryan Bigg", "Jon Atack", "Sean Carroll", "David Rodríguez"]
|
|
11
|
+
s.email = ["ernie@erniemiller.org", "radarlistener@gmail.com", "jonnyatack@gmail.com", "sfcarroll@gmail.com"]
|
|
11
12
|
s.homepage = "https://github.com/activerecord-hackery/ransack"
|
|
12
|
-
s.summary = %q{Object-based searching for Active Record
|
|
13
|
+
s.summary = %q{Object-based searching for Active Record.}
|
|
13
14
|
s.description = %q{Ransack is the successor to the MetaSearch gem. It improves and expands upon MetaSearch's functionality, but does not have a 100%-compatible API.}
|
|
14
|
-
s.required_ruby_version = '>=
|
|
15
|
+
s.required_ruby_version = '>= 3.0'
|
|
15
16
|
s.license = 'MIT'
|
|
16
17
|
|
|
17
|
-
s.add_dependency 'activerecord', '>=
|
|
18
|
-
s.add_dependency 'activesupport', '>=
|
|
18
|
+
s.add_dependency 'activerecord', '>= 6.1.5'
|
|
19
|
+
s.add_dependency 'activesupport', '>= 6.1.5'
|
|
19
20
|
s.add_dependency 'i18n'
|
|
20
|
-
s.add_dependency 'polyamorous', Ransack::VERSION.to_s
|
|
21
21
|
|
|
22
22
|
s.files = `git ls-files`.split("\n")
|
|
23
|
-
|
|
24
|
-
s.
|
|
25
|
-
.split("\n")
|
|
26
|
-
|
|
27
|
-
s.executables = `git ls-files -- bin/*`
|
|
28
|
-
.split("\n")
|
|
29
|
-
.map { |f| File.basename(f) }
|
|
30
|
-
|
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
31
25
|
s.require_paths = ["lib"]
|
|
32
26
|
end
|
data/spec/blueprints/articles.rb
CHANGED
data/spec/blueprints/comments.rb
CHANGED
data/spec/blueprints/notes.rb
CHANGED
data/spec/blueprints/tags.rb
CHANGED
data/spec/console.rb
CHANGED
|
@@ -14,11 +14,11 @@ Sham.define do
|
|
|
14
14
|
title { Faker::Lorem.sentence }
|
|
15
15
|
body { Faker::Lorem.paragraph }
|
|
16
16
|
salary { |index| 30000 + (index * 1000) }
|
|
17
|
-
tag_name { Faker::Lorem.words(3).join(' ') }
|
|
18
|
-
note { Faker::Lorem.words(7).join(' ') }
|
|
19
|
-
only_admin { Faker::Lorem.words(3).join(' ') }
|
|
20
|
-
only_search { Faker::Lorem.words(3).join(' ') }
|
|
21
|
-
only_sort { Faker::Lorem.words(3).join(' ') }
|
|
17
|
+
tag_name { Faker::Lorem.words(number: 3).join(' ') }
|
|
18
|
+
note { Faker::Lorem.words(number: 7).join(' ') }
|
|
19
|
+
only_admin { Faker::Lorem.words(number: 3).join(' ') }
|
|
20
|
+
only_search { Faker::Lorem.words(number: 3).join(' ') }
|
|
21
|
+
only_sort { Faker::Lorem.words(number: 3).join(' ') }
|
|
22
22
|
notable_id { |id| id }
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -3,14 +3,8 @@ module PolyamorousHelper
|
|
|
3
3
|
Polyamorous::JoinAssociation.new reflection, children, klass
|
|
4
4
|
end
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Polyamorous::JoinDependency.new klass, klass.arel_table, associations, Polyamorous::InnerJoin
|
|
9
|
-
end
|
|
10
|
-
else
|
|
11
|
-
def new_join_dependency(klass, associations = {})
|
|
12
|
-
Polyamorous::JoinDependency.new klass, klass.arel_table, associations
|
|
13
|
-
end
|
|
6
|
+
def new_join_dependency(klass, associations = {})
|
|
7
|
+
Polyamorous::JoinDependency.new klass, klass.arel_table, associations, Polyamorous::InnerJoin
|
|
14
8
|
end
|
|
15
9
|
|
|
16
10
|
def new_join(name, type = Polyamorous::InnerJoin, klass = nil)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Polyamorous
|
|
4
|
+
describe "ActiveRecord Compatibility" do
|
|
5
|
+
it 'works with self joins and includes' do
|
|
6
|
+
trade_account = Account.create!
|
|
7
|
+
Account.create!(trade_account: trade_account)
|
|
8
|
+
|
|
9
|
+
accounts = Account.joins(:trade_account).includes(:trade_account, :agent_account)
|
|
10
|
+
account = accounts.first
|
|
11
|
+
|
|
12
|
+
expect(account.agent_account).to be_nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -10,7 +10,9 @@ module Polyamorous
|
|
|
10
10
|
new_join_association(reflection, parent.children, Article)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
subject { new_join_association(reflection, parent.children, Person) }
|
|
14
|
+
|
|
15
|
+
it 'leaves the original reflection intact for thread safety' do
|
|
14
16
|
reflection.instance_variable_set(:@klass, Article)
|
|
15
17
|
join_association
|
|
16
18
|
.swapping_reflection_klass(reflection, Person) do |new_reflection|
|
|
@@ -77,21 +77,5 @@ module Polyamorous
|
|
|
77
77
|
specify { expect(subject.send(:join_root).drop(1)[1].table_name)
|
|
78
78
|
.to eq 'comments' }
|
|
79
79
|
end
|
|
80
|
-
|
|
81
|
-
context '#left_outer_join in Rails 5 overrides join type specified',
|
|
82
|
-
if: ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MAJOR < 6 && ActiveRecord::VERSION::MINOR < 2 do
|
|
83
|
-
|
|
84
|
-
let(:join_type_class) do
|
|
85
|
-
new_join_dependency(
|
|
86
|
-
Person,
|
|
87
|
-
new_join(:articles)
|
|
88
|
-
).join_constraints(
|
|
89
|
-
[],
|
|
90
|
-
Arel::Nodes::OuterJoin
|
|
91
|
-
).first.joins.map(&:class)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
specify { expect(join_type_class).to eq [Arel::Nodes::OuterJoin] }
|
|
95
|
-
end
|
|
96
80
|
end
|
|
97
81
|
end
|
|
@@ -8,7 +8,6 @@ module Ransack
|
|
|
8
8
|
subject { ::ActiveRecord::Base }
|
|
9
9
|
|
|
10
10
|
it { should respond_to :ransack }
|
|
11
|
-
it { should respond_to :search }
|
|
12
11
|
|
|
13
12
|
describe '#search' do
|
|
14
13
|
subject { Person.ransack }
|
|
@@ -44,12 +43,12 @@ module Ransack
|
|
|
44
43
|
|
|
45
44
|
it 'applies stringy boolean scopes with true value in an array' do
|
|
46
45
|
s = Person.ransack('of_age' => ['true'])
|
|
47
|
-
expect(s.result.to_sql).to (include 'age >= 18')
|
|
46
|
+
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{(age >= '18')} : 'age >= 18')
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
it 'applies stringy boolean scopes with false value in an array' do
|
|
51
50
|
s = Person.ransack('of_age' => ['false'])
|
|
52
|
-
expect(s.result.to_sql).to (include 'age < 18')
|
|
51
|
+
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age < '18'} : 'age < 18')
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
it 'ignores unlisted scopes' do
|
|
@@ -69,15 +68,25 @@ module Ransack
|
|
|
69
68
|
|
|
70
69
|
it 'passes values to scopes' do
|
|
71
70
|
s = Person.ransack('over_age' => 18)
|
|
72
|
-
expect(s.result.to_sql).to (include 'age > 18')
|
|
71
|
+
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
|
|
73
72
|
end
|
|
74
73
|
|
|
75
74
|
it 'chains scopes' do
|
|
76
75
|
s = Person.ransack('over_age' => 18, 'active' => true)
|
|
77
|
-
expect(s.result.to_sql).to (include 'age > 18')
|
|
76
|
+
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '18'} : 'age > 18')
|
|
78
77
|
expect(s.result.to_sql).to (include 'active = 1')
|
|
79
78
|
end
|
|
80
79
|
|
|
80
|
+
it 'applies scopes that define string SQL joins' do
|
|
81
|
+
allow(Article)
|
|
82
|
+
.to receive(:ransackable_scopes)
|
|
83
|
+
.and_return([:latest_comment_cont])
|
|
84
|
+
|
|
85
|
+
# Including a negative condition to test removing the scope
|
|
86
|
+
s = Search.new(Article, notes_note_not_eq: 'Test', latest_comment_cont: 'Test')
|
|
87
|
+
expect(s.result.to_sql).to include 'latest_comment'
|
|
88
|
+
end
|
|
89
|
+
|
|
81
90
|
context "with sanitize_custom_scope_booleans set to false" do
|
|
82
91
|
before(:all) do
|
|
83
92
|
Ransack.configure { |c| c.sanitize_custom_scope_booleans = false }
|
|
@@ -89,12 +98,12 @@ module Ransack
|
|
|
89
98
|
|
|
90
99
|
it 'passes true values to scopes' do
|
|
91
100
|
s = Person.ransack('over_age' => 1)
|
|
92
|
-
expect(s.result.to_sql).to (include 'age > 1')
|
|
101
|
+
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
|
|
93
102
|
end
|
|
94
103
|
|
|
95
104
|
it 'passes false values to scopes' do
|
|
96
105
|
s = Person.ransack('over_age' => 0)
|
|
97
|
-
expect(s.result.to_sql).to (include 'age > 0')
|
|
106
|
+
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
|
|
98
107
|
end
|
|
99
108
|
end
|
|
100
109
|
|
|
@@ -107,12 +116,12 @@ module Ransack
|
|
|
107
116
|
|
|
108
117
|
it 'passes true values to scopes' do
|
|
109
118
|
s = Person.ransack('over_age' => 1)
|
|
110
|
-
expect(s.result.to_sql).to (include 'age > 1')
|
|
119
|
+
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '1'} : 'age > 1')
|
|
111
120
|
end
|
|
112
121
|
|
|
113
122
|
it 'passes false values to scopes' do
|
|
114
123
|
s = Person.ransack('over_age' => 0)
|
|
115
|
-
expect(s.result.to_sql).to (include 'age > 0')
|
|
124
|
+
expect(s.result.to_sql).to (include rails7_and_mysql ? %q{age > '0'} : 'age > 0')
|
|
116
125
|
end
|
|
117
126
|
end
|
|
118
127
|
|
|
@@ -122,12 +131,32 @@ module Ransack
|
|
|
122
131
|
expect { Person.ransack('') }.to_not raise_error
|
|
123
132
|
end
|
|
124
133
|
|
|
134
|
+
it 'raises exception if ransack! called with unknown condition' do
|
|
135
|
+
expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error(ArgumentError)
|
|
136
|
+
end
|
|
137
|
+
|
|
125
138
|
it 'does not modify the parameters' do
|
|
126
139
|
params = { name_eq: '' }
|
|
127
140
|
expect { Person.ransack(params) }.not_to change { params }
|
|
128
141
|
end
|
|
129
142
|
end
|
|
130
143
|
|
|
144
|
+
context 'has_one through associations' do
|
|
145
|
+
let(:address) { Address.create!(city: 'Boston') }
|
|
146
|
+
let(:org) { Organization.create!(name: 'Testorg', address: address) }
|
|
147
|
+
let!(:employee) { Employee.create!(name: 'Ernie', organization: org) }
|
|
148
|
+
|
|
149
|
+
it 'works when has_one through association is first' do
|
|
150
|
+
s = Employee.ransack(address_city_eq: 'Boston', organization_name_eq: 'Testorg')
|
|
151
|
+
expect(s.result.to_a).to include(employee)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'works when has_one through association is last' do
|
|
155
|
+
s = Employee.ransack(organization_name_eq: 'Testorg', address_city_eq: 'Boston')
|
|
156
|
+
expect(s.result.to_a).to include(employee)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
131
160
|
context 'negative conditions on HABTM associations' do
|
|
132
161
|
let(:medieval) { Tag.create!(name: 'Medieval') }
|
|
133
162
|
let(:fantasy) { Tag.create!(name: 'Fantasy') }
|
|
@@ -143,14 +172,12 @@ module Ransack
|
|
|
143
172
|
it 'removes redundant joins from top query' do
|
|
144
173
|
s = Article.ransack(tags_name_not_eq: "Fantasy")
|
|
145
174
|
sql = s.result.to_sql
|
|
146
|
-
|
|
147
175
|
expect(sql).to_not include('LEFT OUTER JOIN')
|
|
148
176
|
end
|
|
149
177
|
|
|
150
178
|
it 'handles != for single values' do
|
|
151
179
|
s = Article.ransack(tags_name_not_eq: "Fantasy")
|
|
152
180
|
articles = s.result.to_a
|
|
153
|
-
|
|
154
181
|
expect(articles).to include marco
|
|
155
182
|
expect(articles).to_not include arthur
|
|
156
183
|
end
|
|
@@ -267,10 +294,12 @@ module Ransack
|
|
|
267
294
|
# end
|
|
268
295
|
|
|
269
296
|
it 'creates ransack attributes' do
|
|
297
|
+
person = Person.create!(name: 'Aric Smith')
|
|
298
|
+
|
|
270
299
|
s = Person.ransack(reversed_name_eq: 'htimS cirA')
|
|
271
300
|
expect(s.result.size).to eq(1)
|
|
272
301
|
|
|
273
|
-
expect(s.result.first).to eq
|
|
302
|
+
expect(s.result.first).to eq person
|
|
274
303
|
end
|
|
275
304
|
|
|
276
305
|
it 'can be accessed through associations' do
|
|
@@ -310,7 +339,11 @@ module Ransack
|
|
|
310
339
|
end
|
|
311
340
|
|
|
312
341
|
it 'should function correctly with a multi-parameter attribute' do
|
|
313
|
-
::ActiveRecord::
|
|
342
|
+
if ::ActiveRecord::VERSION::MAJOR >= 7
|
|
343
|
+
::ActiveRecord.default_timezone = :utc
|
|
344
|
+
else
|
|
345
|
+
::ActiveRecord::Base.default_timezone = :utc
|
|
346
|
+
end
|
|
314
347
|
Time.zone = 'UTC'
|
|
315
348
|
|
|
316
349
|
date = Date.current
|
|
@@ -460,9 +493,9 @@ module Ransack
|
|
|
460
493
|
Comment.create(article: Article.create(title: 'Avenger'), person: Person.create(salary: 100_000)),
|
|
461
494
|
Comment.create(article: Article.create(title: 'Avenge'), person: Person.create(salary: 50_000)),
|
|
462
495
|
]
|
|
463
|
-
expect(Comment.ransack(article_title_cont: 'aven',s: 'person_salary desc').result).to eq(comments)
|
|
496
|
+
expect(Comment.ransack(article_title_cont: 'aven', s: 'person_salary desc').result).to eq(comments)
|
|
464
497
|
expect(Comment.joins(:person).ransack(s: 'persons_salarydesc', article_title_cont: 'aven').result).to eq(comments)
|
|
465
|
-
expect(Comment.joins(:person).ransack(article_title_cont: 'aven',s: 'persons_salary desc').result).to eq(comments)
|
|
498
|
+
expect(Comment.joins(:person).ransack(article_title_cont: 'aven', s: 'persons_salary desc').result).to eq(comments)
|
|
466
499
|
end
|
|
467
500
|
|
|
468
501
|
it 'allows sort by `only_sort` field' do
|
|
@@ -541,7 +574,6 @@ module Ransack
|
|
|
541
574
|
)
|
|
542
575
|
end
|
|
543
576
|
|
|
544
|
-
|
|
545
577
|
it 'should allow passing ransacker arguments to a ransacker' do
|
|
546
578
|
s = Person.ransack(
|
|
547
579
|
c: [{
|
|
@@ -641,6 +673,37 @@ module Ransack
|
|
|
641
673
|
it { should_not include 'only_sort' }
|
|
642
674
|
it { should include 'only_admin' }
|
|
643
675
|
end
|
|
676
|
+
|
|
677
|
+
context 'when not defined in model, nor in ApplicationRecord' do
|
|
678
|
+
subject { Article.ransackable_attributes }
|
|
679
|
+
|
|
680
|
+
it "raises a helpful error" do
|
|
681
|
+
without_application_record_method(:ransackable_attributes) do
|
|
682
|
+
expect { subject }.to raise_error(RuntimeError, /Ransack needs Article attributes explicitly allowlisted/)
|
|
683
|
+
end
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
context 'when defined only in model by delegating to super' do
|
|
688
|
+
subject { Article.ransackable_attributes }
|
|
689
|
+
|
|
690
|
+
around do |example|
|
|
691
|
+
Article.singleton_class.define_method(:ransackable_attributes) do
|
|
692
|
+
super(nil) - super(nil)
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
example.run
|
|
696
|
+
ensure
|
|
697
|
+
Article.singleton_class.remove_method(:ransackable_attributes)
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
it "returns the allowlist in the model, and warns" do
|
|
701
|
+
without_application_record_method(:ransackable_attributes) do
|
|
702
|
+
expect { subject }.to output(/Ransack's builtin `ransackable_attributes` method is deprecated/).to_stderr
|
|
703
|
+
expect(subject).to be_empty
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
end
|
|
644
707
|
end
|
|
645
708
|
|
|
646
709
|
describe '#ransortable_attributes' do
|
|
@@ -673,6 +736,37 @@ module Ransack
|
|
|
673
736
|
it { should include 'parent' }
|
|
674
737
|
it { should include 'children' }
|
|
675
738
|
it { should include 'articles' }
|
|
739
|
+
|
|
740
|
+
context 'when not defined in model, nor in ApplicationRecord' do
|
|
741
|
+
subject { Article.ransackable_associations }
|
|
742
|
+
|
|
743
|
+
it "raises a helpful error" do
|
|
744
|
+
without_application_record_method(:ransackable_associations) do
|
|
745
|
+
expect { subject }.to raise_error(RuntimeError, /Ransack needs Article associations explicitly allowlisted/)
|
|
746
|
+
end
|
|
747
|
+
end
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
context 'when defined only in model by delegating to super' do
|
|
751
|
+
subject { Article.ransackable_associations }
|
|
752
|
+
|
|
753
|
+
around do |example|
|
|
754
|
+
Article.singleton_class.define_method(:ransackable_associations) do
|
|
755
|
+
super(nil) - super(nil)
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
example.run
|
|
759
|
+
ensure
|
|
760
|
+
Article.singleton_class.remove_method(:ransackable_associations)
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
it "returns the allowlist in the model, and warns" do
|
|
764
|
+
without_application_record_method(:ransackable_associations) do
|
|
765
|
+
expect { subject }.to output(/Ransack's builtin `ransackable_associations` method is deprecated/).to_stderr
|
|
766
|
+
expect(subject).to be_empty
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
end
|
|
676
770
|
end
|
|
677
771
|
|
|
678
772
|
describe '#ransackable_scopes' do
|
|
@@ -687,6 +781,21 @@ module Ransack
|
|
|
687
781
|
it { should eq [] }
|
|
688
782
|
end
|
|
689
783
|
|
|
784
|
+
private
|
|
785
|
+
|
|
786
|
+
def without_application_record_method(method)
|
|
787
|
+
ApplicationRecord.singleton_class.alias_method :"original_#{method}", :"#{method}"
|
|
788
|
+
ApplicationRecord.singleton_class.remove_method :"#{method}"
|
|
789
|
+
|
|
790
|
+
yield
|
|
791
|
+
ensure
|
|
792
|
+
ApplicationRecord.singleton_class.alias_method :"#{method}", :"original_#{method}"
|
|
793
|
+
ApplicationRecord.singleton_class.remove_method :"original_#{method}"
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
def rails7_and_mysql
|
|
797
|
+
::ActiveRecord::VERSION::MAJOR >= 7 && ENV['DB'] == 'mysql'
|
|
798
|
+
end
|
|
690
799
|
end
|
|
691
800
|
end
|
|
692
801
|
end
|
|
@@ -9,7 +9,6 @@ module Ransack
|
|
|
9
9
|
describe Context do
|
|
10
10
|
subject { Context.new(Person) }
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
it 'has an Active Record alias tracker method' do
|
|
14
13
|
expect(subject.alias_tracker)
|
|
15
14
|
.to be_an ::ActiveRecord::Associations::AliasTracker
|
|
@@ -79,6 +78,25 @@ module Ransack
|
|
|
79
78
|
expect(constraint.right.relation.name).to eql 'people'
|
|
80
79
|
expect(constraint.right.name).to eql 'id'
|
|
81
80
|
end
|
|
81
|
+
|
|
82
|
+
it 'build correlated subquery for multiple conditions (default scope)' do
|
|
83
|
+
search = Search.new(Person, { comments_body_not_eq: 'some_title' })
|
|
84
|
+
|
|
85
|
+
# Was
|
|
86
|
+
# SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
|
|
87
|
+
# SELECT "comments"."disabled" FROM "comments"
|
|
88
|
+
# WHERE "comments"."disabled" = "people"."id"
|
|
89
|
+
# AND NOT ("comments"."body" != 'some_title')
|
|
90
|
+
# ) ORDER BY "people"."id" DESC
|
|
91
|
+
# Should Be
|
|
92
|
+
# SELECT "people".* FROM "people" WHERE "people"."id" NOT IN (
|
|
93
|
+
# SELECT "comments"."person_id" FROM "comments"
|
|
94
|
+
# WHERE "comments"."person_id" = "people"."id"
|
|
95
|
+
# AND NOT ("comments"."body" != 'some_title')
|
|
96
|
+
# ) ORDER BY "people"."id" DESC
|
|
97
|
+
|
|
98
|
+
expect(search.result.to_sql).to match /.comments.\..person_id. = .people.\..id./
|
|
99
|
+
end
|
|
82
100
|
end
|
|
83
101
|
|
|
84
102
|
describe 'sharing context across searches' do
|
|
@@ -91,23 +109,6 @@ module Ransack
|
|
|
91
109
|
context: shared_context)
|
|
92
110
|
end
|
|
93
111
|
|
|
94
|
-
describe '#join_associations', if: AR_version <= '4.0' do
|
|
95
|
-
it 'returns dependent join associations for all searches run
|
|
96
|
-
against the context' do
|
|
97
|
-
parents, children = shared_context.join_associations
|
|
98
|
-
|
|
99
|
-
expect(children.aliased_table_name).to eq "children_people"
|
|
100
|
-
expect(parents.aliased_table_name).to eq "parents_people"
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
it 'can be rejoined to execute a valid query' do
|
|
104
|
-
parents, children = shared_context.join_associations
|
|
105
|
-
|
|
106
|
-
expect { Person.joins(parents).joins(children).to_a }
|
|
107
|
-
.to_not raise_error
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
112
|
describe '#join_sources' do
|
|
112
113
|
it 'returns dependent arel join nodes for all searches run against
|
|
113
114
|
the context' do
|