ransack 3.2.1 → 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/workflows/codeql.yml +72 -0
- data/.github/workflows/cronjob.yml +6 -9
- data/.github/workflows/deploy.yml +1 -1
- data/.github/workflows/rubocop.yml +2 -2
- data/.github/workflows/test-deploy.yml +1 -1
- data/.github/workflows/test.yml +23 -22
- data/.rubocop.yml +7 -1
- data/CHANGELOG.md +86 -0
- data/CONTRIBUTING.md +40 -21
- data/Gemfile +10 -10
- data/README.md +4 -9
- data/bug_report_templates/test-ransack-scope-and-column-same-name.rb +1 -1
- data/bug_report_templates/test-ransacker-arel-present-predicate.rb +5 -1
- data/docs/docs/getting-started/advanced-mode.md +1 -1
- data/docs/docs/getting-started/search-matches.md +1 -1
- data/docs/docs/getting-started/simple-mode.md +6 -2
- data/docs/docs/getting-started/sorting.md +45 -53
- data/docs/docs/going-further/acts-as-taggable-on.md +4 -4
- data/docs/docs/going-further/form-customisation.md +1 -1
- data/docs/docs/going-further/i18n.md +3 -3
- data/docs/docs/going-further/other-notes.md +2 -2
- data/docs/docs/going-further/polymorphic-search.md +6 -0
- data/docs/docs/going-further/saving-queries.md +1 -1
- data/docs/docs/going-further/searching-postgres.md +1 -1
- data/docs/package.json +7 -3
- data/docs/yarn.lock +2371 -1928
- data/lib/polyamorous/{activerecord_6.1_ruby_2 → activerecord}/join_dependency.rb +14 -4
- data/lib/polyamorous/polyamorous.rb +3 -4
- data/lib/ransack/adapters/active_record/base.rb +78 -7
- data/lib/ransack/adapters/active_record/context.rb +2 -1
- data/lib/ransack/configuration.rb +25 -12
- data/lib/ransack/constants.rb +125 -0
- data/lib/ransack/context.rb +34 -5
- data/lib/ransack/helpers/form_builder.rb +3 -3
- data/lib/ransack/helpers/form_helper.rb +3 -2
- data/lib/ransack/nodes/attribute.rb +2 -2
- data/lib/ransack/nodes/condition.rb +80 -7
- data/lib/ransack/nodes/grouping.rb +3 -3
- data/lib/ransack/nodes/node.rb +1 -1
- data/lib/ransack/nodes/value.rb +1 -1
- data/lib/ransack/predicate.rb +1 -1
- data/lib/ransack/ransacker.rb +1 -1
- data/lib/ransack/search.rb +9 -4
- data/lib/ransack/translate.rb +2 -2
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +38 -2
- data/lib/ransack.rb +3 -6
- data/ransack.gemspec +1 -1
- data/spec/ransack/adapters/active_record/base_spec.rb +89 -0
- data/spec/ransack/configuration_spec.rb +9 -9
- data/spec/ransack/helpers/form_builder_spec.rb +8 -8
- data/spec/ransack/helpers/form_helper_spec.rb +36 -2
- data/spec/ransack/nodes/condition_spec.rb +24 -0
- data/spec/ransack/predicate_spec.rb +36 -1
- data/spec/ransack/translate_spec.rb +1 -1
- data/spec/support/schema.rb +55 -10
- metadata +9 -22
- data/lib/polyamorous/activerecord_7.0_ruby_2/join_association.rb +0 -1
- data/lib/polyamorous/activerecord_7.0_ruby_2/join_dependency.rb +0 -1
- data/lib/polyamorous/activerecord_7.0_ruby_2/reflection.rb +0 -1
- data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +0 -1
- data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +0 -1
- data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +0 -1
- data/lib/polyamorous.rb +0 -1
- data/lib/ransack/adapters/active_record/ransack/constants.rb +0 -128
- data/lib/ransack/adapters/active_record/ransack/context.rb +0 -56
- 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/lib/polyamorous/{activerecord_6.1_ruby_2 → activerecord}/join_association.rb +0 -0
- /data/lib/polyamorous/{activerecord_6.1_ruby_2 → activerecord}/reflection.rb +0 -0
- /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
data/lib/ransack/translate.rb
CHANGED
@@ -66,7 +66,7 @@ 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 = { :
|
69
|
+
options = { count: 1, default: defaults }
|
70
70
|
I18n.translate(defaults.shift, **options)
|
71
71
|
end
|
72
72
|
|
@@ -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,10 +1,7 @@
|
|
1
1
|
require 'active_support/core_ext'
|
2
2
|
require 'ransack/configuration'
|
3
|
-
require 'ransack/adapters'
|
4
3
|
require 'polyamorous/polyamorous'
|
5
4
|
|
6
|
-
Ransack::Adapters.object_mapper.require_constants
|
7
|
-
|
8
5
|
module Ransack
|
9
6
|
extend Configuration
|
10
7
|
class UntraversableAssociationError < StandardError; end
|
@@ -12,7 +9,7 @@ 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
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.homepage = "https://github.com/activerecord-hackery/ransack"
|
13
13
|
s.summary = %q{Object-based searching for Active Record.}
|
14
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.}
|
15
|
-
s.required_ruby_version = '>=
|
15
|
+
s.required_ruby_version = '>= 3.0'
|
16
16
|
s.license = 'MIT'
|
17
17
|
|
18
18
|
s.add_dependency 'activerecord', '>= 6.1.5'
|
@@ -141,6 +141,22 @@ module Ransack
|
|
141
141
|
end
|
142
142
|
end
|
143
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
|
+
|
144
160
|
context 'negative conditions on HABTM associations' do
|
145
161
|
let(:medieval) { Tag.create!(name: 'Medieval') }
|
146
162
|
let(:fantasy) { Tag.create!(name: 'Fantasy') }
|
@@ -657,6 +673,37 @@ module Ransack
|
|
657
673
|
it { should_not include 'only_sort' }
|
658
674
|
it { should include 'only_admin' }
|
659
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
|
660
707
|
end
|
661
708
|
|
662
709
|
describe '#ransortable_attributes' do
|
@@ -689,6 +736,37 @@ module Ransack
|
|
689
736
|
it { should include 'parent' }
|
690
737
|
it { should include 'children' }
|
691
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
|
692
770
|
end
|
693
771
|
|
694
772
|
describe '#ransackable_scopes' do
|
@@ -704,6 +782,17 @@ module Ransack
|
|
704
782
|
end
|
705
783
|
|
706
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
|
+
|
707
796
|
def rails7_and_mysql
|
708
797
|
::ActiveRecord::VERSION::MAJOR >= 7 && ENV['DB'] == 'mysql'
|
709
798
|
end
|
@@ -20,7 +20,7 @@ module Ransack
|
|
20
20
|
Ransack.configure do |config|
|
21
21
|
config.add_predicate(
|
22
22
|
:test_predicate_without_compound,
|
23
|
-
:
|
23
|
+
compounds: false
|
24
24
|
)
|
25
25
|
end
|
26
26
|
expect(Ransack.predicates)
|
@@ -138,8 +138,8 @@ module Ransack
|
|
138
138
|
Ransack.configure do |config|
|
139
139
|
config.add_predicate(
|
140
140
|
:test_array_predicate,
|
141
|
-
:
|
142
|
-
:
|
141
|
+
wants_array: true,
|
142
|
+
compounds: true
|
143
143
|
)
|
144
144
|
end
|
145
145
|
|
@@ -153,11 +153,11 @@ module Ransack
|
|
153
153
|
Ransack.configure do |config|
|
154
154
|
config.add_predicate(
|
155
155
|
:test_in_predicate,
|
156
|
-
:
|
156
|
+
arel_predicate: 'in'
|
157
157
|
)
|
158
158
|
config.add_predicate(
|
159
159
|
:test_not_in_predicate,
|
160
|
-
:
|
160
|
+
arel_predicate: 'not_in'
|
161
161
|
)
|
162
162
|
end
|
163
163
|
|
@@ -171,13 +171,13 @@ module Ransack
|
|
171
171
|
Ransack.configure do |config|
|
172
172
|
config.add_predicate(
|
173
173
|
:test_in_predicate_no_array,
|
174
|
-
:
|
175
|
-
:
|
174
|
+
arel_predicate: 'in',
|
175
|
+
wants_array: false
|
176
176
|
)
|
177
177
|
config.add_predicate(
|
178
178
|
:test_not_in_predicate_no_array,
|
179
|
-
:
|
180
|
-
:
|
179
|
+
arel_predicate: 'not_in',
|
180
|
+
wants_array: false
|
181
181
|
)
|
182
182
|
end
|
183
183
|
|
@@ -26,7 +26,7 @@ module Ransack
|
|
26
26
|
# @s.created_at_eq = date_values # This works in Rails 4.x but not 3.x
|
27
27
|
@s.created_at_eq = [2011, 1, 2, 3, 4, 5] # so we have to do this
|
28
28
|
html = @f.datetime_select(
|
29
|
-
:created_at_eq, :
|
29
|
+
:created_at_eq, use_month_numbers: true, include_seconds: true
|
30
30
|
)
|
31
31
|
date_values.each { |val| expect(html).to include date_select_html(val) }
|
32
32
|
end
|
@@ -70,13 +70,13 @@ module Ransack
|
|
70
70
|
|
71
71
|
describe '#sort_link' do
|
72
72
|
it 'sort_link for ransack attribute' do
|
73
|
-
sort_link = @f.sort_link :name, :
|
73
|
+
sort_link = @f.sort_link :name, controller: 'people'
|
74
74
|
expect(sort_link).to match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
|
75
75
|
expect(sort_link).to match /sort_link/
|
76
76
|
expect(sort_link).to match /Full Name<\/a>/
|
77
77
|
end
|
78
78
|
it 'sort_link for common attribute' do
|
79
|
-
sort_link = @f.sort_link :id, :
|
79
|
+
sort_link = @f.sort_link :id, controller: 'people'
|
80
80
|
expect(sort_link).to match /id<\/a>/
|
81
81
|
end
|
82
82
|
end
|
@@ -99,14 +99,14 @@ module Ransack
|
|
99
99
|
it 'returns ransackable attributes for associations with :associations' do
|
100
100
|
attributes = Person.ransackable_attributes +
|
101
101
|
Article.ransackable_attributes.map { |a| "articles_#{a}" }
|
102
|
-
html = @f.attribute_select(:
|
102
|
+
html = @f.attribute_select(associations: ['articles'])
|
103
103
|
expect(html.split(/\n/).size).to eq(attributes.size)
|
104
104
|
attributes.each do |attribute|
|
105
105
|
expect(html).to match /<option value="#{attribute}">/
|
106
106
|
end
|
107
107
|
end
|
108
108
|
it 'returns option groups for base and associations with :associations' do
|
109
|
-
html = @f.attribute_select(:
|
109
|
+
html = @f.attribute_select(associations: ['articles'])
|
110
110
|
[Person, Article].each do |model|
|
111
111
|
expect(html).to match /<optgroup label="#{model}">/
|
112
112
|
end
|
@@ -121,19 +121,19 @@ module Ransack
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
it 'filters predicates with single-value :only' do
|
124
|
-
html = @f.predicate_select :
|
124
|
+
html = @f.predicate_select only: 'eq'
|
125
125
|
Predicate.names.reject { |k| k =~ /^eq/ }.each do |key|
|
126
126
|
expect(html).not_to match /<option value="#{key}">/
|
127
127
|
end
|
128
128
|
end
|
129
129
|
it 'filters predicates with multi-value :only' do
|
130
|
-
html = @f.predicate_select :
|
130
|
+
html = @f.predicate_select only: [:eq, :lt]
|
131
131
|
Predicate.names.reject { |k| k =~ /^(eq|lt)/ }.each do |key|
|
132
132
|
expect(html).not_to match /<option value="#{key}">/
|
133
133
|
end
|
134
134
|
end
|
135
135
|
it 'excludes compounds when compounds: false' do
|
136
|
-
html = @f.predicate_select :
|
136
|
+
html = @f.predicate_select compounds: false
|
137
137
|
Predicate.names.select { |k| k =~ /_(any|all)$/ }.each do |key|
|
138
138
|
expect(html).not_to match /<option value="#{key}">/
|
139
139
|
end
|
@@ -140,6 +140,32 @@ module Ransack
|
|
140
140
|
}
|
141
141
|
end
|
142
142
|
|
143
|
+
describe '#sort_link works even if search params are a string' do
|
144
|
+
before { @controller.view_context.params[:q] = 'input error' }
|
145
|
+
specify {
|
146
|
+
expect { @controller.view_context
|
147
|
+
.sort_link(
|
148
|
+
Person.ransack({}),
|
149
|
+
:name,
|
150
|
+
controller: 'people'
|
151
|
+
)
|
152
|
+
}.not_to raise_error
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
describe '#sort_url works even if search params are a string' do
|
157
|
+
before { @controller.view_context.params[:q] = 'input error' }
|
158
|
+
specify {
|
159
|
+
expect { @controller.view_context
|
160
|
+
.sort_url(
|
161
|
+
Person.ransack({}),
|
162
|
+
:name,
|
163
|
+
controller: 'people'
|
164
|
+
)
|
165
|
+
}.not_to raise_error
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
143
169
|
describe '#sort_link with search_key defined as a string' do
|
144
170
|
subject { @controller.view_context
|
145
171
|
.sort_link(
|
@@ -474,7 +500,7 @@ module Ransack
|
|
474
500
|
describe 'with symbol q:, #sort_link should include search params' do
|
475
501
|
subject { @controller.view_context.sort_link(Person.ransack, :name) }
|
476
502
|
let(:params) { ActionController::Parameters.new(
|
477
|
-
{ :
|
503
|
+
{ q: { name_eq: 'TEST' }, controller: 'people' }
|
478
504
|
) }
|
479
505
|
before { @controller.instance_variable_set(:@params, params) }
|
480
506
|
|
@@ -489,7 +515,7 @@ module Ransack
|
|
489
515
|
describe 'with symbol q:, #sort_url should include search params' do
|
490
516
|
subject { @controller.view_context.sort_url(Person.ransack, :name) }
|
491
517
|
let(:params) { ActionController::Parameters.new(
|
492
|
-
{ :
|
518
|
+
{ q: { name_eq: 'TEST' }, controller: 'people' }
|
493
519
|
) }
|
494
520
|
before { @controller.instance_variable_set(:@params, params) }
|
495
521
|
|
@@ -782,6 +808,14 @@ module Ransack
|
|
782
808
|
it { should_not match /href=".*foo/ }
|
783
809
|
end
|
784
810
|
|
811
|
+
describe "#sort_link ignores host in params" do
|
812
|
+
before { @controller.view_context.params[:host] = 'other_domain' }
|
813
|
+
subject { @controller.view_context.sort_link(Person.ransack, :name, controller: 'people') }
|
814
|
+
|
815
|
+
it { should match /href="\/people\?q/ }
|
816
|
+
it { should_not match /href=".*other_domain/ }
|
817
|
+
end
|
818
|
+
|
785
819
|
describe '#search_form_for with default format' do
|
786
820
|
subject { @controller.view_context
|
787
821
|
.search_form_for(Person.ransack) {} }
|
@@ -74,6 +74,30 @@ module Ransack
|
|
74
74
|
specify { expect(subject).to be_nil }
|
75
75
|
end
|
76
76
|
end
|
77
|
+
|
78
|
+
context 'with an empty predicate' do
|
79
|
+
subject {
|
80
|
+
Condition.extract(
|
81
|
+
Context.for(Person), 'full_name', Person.first.name
|
82
|
+
)
|
83
|
+
}
|
84
|
+
|
85
|
+
context "when default_predicate = nil" do
|
86
|
+
before do
|
87
|
+
Ransack.configure { |c| c.default_predicate = nil }
|
88
|
+
end
|
89
|
+
|
90
|
+
specify { expect(subject).to be_nil }
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when default_predicate = 'eq'" do
|
94
|
+
before do
|
95
|
+
Ransack.configure { |c| c.default_predicate = 'eq' }
|
96
|
+
end
|
97
|
+
|
98
|
+
specify { expect(subject).to eq Condition.extract(Context.for(Person), 'full_name_eq', Person.first.name) }
|
99
|
+
end
|
100
|
+
end
|
77
101
|
end
|
78
102
|
end
|
79
103
|
end
|
@@ -35,6 +35,13 @@ module Ransack
|
|
35
35
|
@s.awesome_eq = nil
|
36
36
|
expect(@s.result.to_sql).not_to match /WHERE/
|
37
37
|
end
|
38
|
+
|
39
|
+
it 'generates a = condition with a huge integer value' do
|
40
|
+
val = 123456789012345678901
|
41
|
+
@s.salary_eq = val
|
42
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
43
|
+
expect(@s.result.to_sql).to match /#{field} = #{val}/
|
44
|
+
end
|
38
45
|
end
|
39
46
|
|
40
47
|
describe 'lteq' do
|
@@ -56,6 +63,13 @@ module Ransack
|
|
56
63
|
@s.salary_lteq = nil
|
57
64
|
expect(@s.result.to_sql).not_to match /WHERE/
|
58
65
|
end
|
66
|
+
|
67
|
+
it 'generates a <= condition with a huge integer value' do
|
68
|
+
val = 123456789012345678901
|
69
|
+
@s.salary_lteq = val
|
70
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
71
|
+
expect(@s.result.to_sql).to match /#{field} <= #{val}/
|
72
|
+
end
|
59
73
|
end
|
60
74
|
|
61
75
|
describe 'lt' do
|
@@ -77,6 +91,13 @@ module Ransack
|
|
77
91
|
@s.salary_lt = nil
|
78
92
|
expect(@s.result.to_sql).not_to match /WHERE/
|
79
93
|
end
|
94
|
+
|
95
|
+
it 'generates a = condition with a huge integer value' do
|
96
|
+
val = 123456789012345678901
|
97
|
+
@s.salary_lt = val
|
98
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
99
|
+
expect(@s.result.to_sql).to match /#{field} < #{val}/
|
100
|
+
end
|
80
101
|
end
|
81
102
|
|
82
103
|
describe 'gteq' do
|
@@ -98,6 +119,13 @@ module Ransack
|
|
98
119
|
@s.salary_gteq = nil
|
99
120
|
expect(@s.result.to_sql).not_to match /WHERE/
|
100
121
|
end
|
122
|
+
|
123
|
+
it 'generates a >= condition with a huge integer value' do
|
124
|
+
val = 123456789012345678901
|
125
|
+
@s.salary_gteq = val
|
126
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
127
|
+
expect(@s.result.to_sql).to match /#{field} >= #{val}/
|
128
|
+
end
|
101
129
|
end
|
102
130
|
|
103
131
|
describe 'gt' do
|
@@ -119,6 +147,13 @@ module Ransack
|
|
119
147
|
@s.salary_gt = nil
|
120
148
|
expect(@s.result.to_sql).not_to match /WHERE/
|
121
149
|
end
|
150
|
+
|
151
|
+
it 'generates a > condition with a huge integer value' do
|
152
|
+
val = 123456789012345678901
|
153
|
+
@s.salary_gt = val
|
154
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
155
|
+
expect(@s.result.to_sql).to match /#{field} > #{val}/
|
156
|
+
end
|
122
157
|
end
|
123
158
|
|
124
159
|
describe 'cont' do
|
@@ -368,7 +403,7 @@ module Ransack
|
|
368
403
|
expect(@s.result.to_sql).to match /#{field} IS NULL/
|
369
404
|
end
|
370
405
|
|
371
|
-
describe 'with association
|
406
|
+
describe 'with association query' do
|
372
407
|
it 'generates a value IS NOT NULL query' do
|
373
408
|
@s.comments_id_not_null = true
|
374
409
|
sql = @s.result.to_sql
|
@@ -8,7 +8,7 @@ module Ransack
|
|
8
8
|
ar_translation = ::Namespace::Article.human_attribute_name(:title)
|
9
9
|
ransack_translation = Ransack::Translate.attribute(
|
10
10
|
:title,
|
11
|
-
:
|
11
|
+
context: ::Namespace::Article.ransack.context
|
12
12
|
)
|
13
13
|
expect(ransack_translation).to eq ar_translation
|
14
14
|
end
|
data/spec/support/schema.rb
CHANGED
@@ -28,7 +28,24 @@ else
|
|
28
28
|
)
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
# This is just a test app with no sensitive data, so we explicitly allowlist all
|
32
|
+
# attributes and associations for search. In general, end users should
|
33
|
+
# explicitly authorize each model, but this shows a way to configure the
|
34
|
+
# unrestricted default behavior of versions prior to Ransack 4.
|
35
|
+
#
|
36
|
+
class ApplicationRecord < ActiveRecord::Base
|
37
|
+
self.abstract_class = true
|
38
|
+
|
39
|
+
def self.ransackable_attributes(auth_object = nil)
|
40
|
+
authorizable_ransackable_attributes
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.ransackable_associations(auth_object = nil)
|
44
|
+
authorizable_ransackable_associations
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Person < ApplicationRecord
|
32
49
|
default_scope { order(id: :desc) }
|
33
50
|
belongs_to :parent, class_name: 'Person', foreign_key: :parent_id
|
34
51
|
has_many :children, class_name: 'Person', foreign_key: :parent_id
|
@@ -111,9 +128,9 @@ class Person < ActiveRecord::Base
|
|
111
128
|
|
112
129
|
def self.ransackable_attributes(auth_object = nil)
|
113
130
|
if auth_object == :admin
|
114
|
-
|
131
|
+
authorizable_ransackable_attributes - ['only_sort']
|
115
132
|
else
|
116
|
-
|
133
|
+
authorizable_ransackable_attributes - ['only_sort', 'only_admin']
|
117
134
|
end
|
118
135
|
end
|
119
136
|
|
@@ -129,7 +146,7 @@ end
|
|
129
146
|
class Musician < Person
|
130
147
|
end
|
131
148
|
|
132
|
-
class Article <
|
149
|
+
class Article < ApplicationRecord
|
133
150
|
belongs_to :person
|
134
151
|
has_many :comments
|
135
152
|
has_and_belongs_to_many :tags
|
@@ -182,7 +199,7 @@ end
|
|
182
199
|
class StoryArticle < Article
|
183
200
|
end
|
184
201
|
|
185
|
-
class Recommendation <
|
202
|
+
class Recommendation < ApplicationRecord
|
186
203
|
belongs_to :person
|
187
204
|
belongs_to :target_person, class_name: 'Person'
|
188
205
|
belongs_to :article
|
@@ -200,26 +217,40 @@ module Namespace
|
|
200
217
|
end
|
201
218
|
end
|
202
219
|
|
203
|
-
class Comment <
|
220
|
+
class Comment < ApplicationRecord
|
204
221
|
belongs_to :article
|
205
222
|
belongs_to :person
|
206
223
|
|
207
224
|
default_scope { where(disabled: false) }
|
208
225
|
end
|
209
226
|
|
210
|
-
class Tag <
|
227
|
+
class Tag < ApplicationRecord
|
211
228
|
has_and_belongs_to_many :articles
|
212
229
|
end
|
213
230
|
|
214
|
-
class Note <
|
231
|
+
class Note < ApplicationRecord
|
215
232
|
belongs_to :notable, polymorphic: true
|
216
233
|
end
|
217
234
|
|
218
|
-
class Account <
|
235
|
+
class Account < ApplicationRecord
|
219
236
|
belongs_to :agent_account, class_name: "Account"
|
220
237
|
belongs_to :trade_account, class_name: "Account"
|
221
238
|
end
|
222
239
|
|
240
|
+
class Address < ApplicationRecord
|
241
|
+
has_one :organization
|
242
|
+
end
|
243
|
+
|
244
|
+
class Organization < ApplicationRecord
|
245
|
+
belongs_to :address
|
246
|
+
has_many :employees
|
247
|
+
end
|
248
|
+
|
249
|
+
class Employee < ApplicationRecord
|
250
|
+
belongs_to :organization
|
251
|
+
has_one :address, through: :organization
|
252
|
+
end
|
253
|
+
|
223
254
|
module Schema
|
224
255
|
def self.create
|
225
256
|
ActiveRecord::Migration.verbose = false
|
@@ -283,6 +314,20 @@ module Schema
|
|
283
314
|
t.belongs_to :agent_account
|
284
315
|
t.belongs_to :trade_account
|
285
316
|
end
|
317
|
+
|
318
|
+
create_table :addresses, force: true do |t|
|
319
|
+
t.string :city
|
320
|
+
end
|
321
|
+
|
322
|
+
create_table :organizations, force: true do |t|
|
323
|
+
t.string :name
|
324
|
+
t.integer :address_id
|
325
|
+
end
|
326
|
+
|
327
|
+
create_table :employees, force: true do |t|
|
328
|
+
t.string :name
|
329
|
+
t.integer :organization_id
|
330
|
+
end
|
286
331
|
end
|
287
332
|
|
288
333
|
10.times do
|
@@ -308,7 +353,7 @@ module Schema
|
|
308
353
|
end
|
309
354
|
|
310
355
|
module SubDB
|
311
|
-
class Base <
|
356
|
+
class Base < ApplicationRecord
|
312
357
|
self.abstract_class = true
|
313
358
|
establish_connection(
|
314
359
|
adapter: 'sqlite3',
|