ransack 2.4.2 → 4.0.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/workflows/codeql.yml +72 -0
- data/.github/workflows/cronjob.yml +6 -9
- data/.github/workflows/deploy.yml +35 -0
- data/.github/workflows/rubocop.yml +1 -1
- data/.github/workflows/test-deploy.yml +29 -0
- data/.github/workflows/test.yml +22 -48
- data/.nojekyll +0 -0
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +208 -11
- data/CONTRIBUTING.md +41 -18
- data/Gemfile +10 -10
- data/README.md +44 -977
- data/bug_report_templates/test-ransacker-arel-present-predicate.rb +4 -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 +79 -0
- data/docs/docs/getting-started/using-predicates.md +282 -0
- data/docs/docs/going-further/_category_.json +4 -0
- data/docs/docs/going-further/acts-as-taggable-on.md +114 -0
- data/docs/docs/going-further/associations.md +70 -0
- data/docs/docs/going-further/custom-predicates.md +52 -0
- data/docs/docs/going-further/documentation.md +43 -0
- data/docs/docs/going-further/exporting-to-csv.md +49 -0
- data/docs/docs/going-further/external-guides.md +57 -0
- data/docs/docs/going-further/form-customisation.md +63 -0
- data/docs/docs/going-further/i18n.md +53 -0
- data/docs/docs/going-further/merging-searches.md +41 -0
- data/docs/docs/going-further/other-notes.md +428 -0
- data/docs/docs/going-further/polymorphic-search.md +40 -0
- data/docs/docs/going-further/ransackers.md +331 -0
- data/docs/docs/going-further/release_process.md +36 -0
- data/docs/docs/going-further/saving-queries.md +82 -0
- data/docs/docs/going-further/searching-postgres.md +57 -0
- data/docs/docs/going-further/wiki-contributors.md +82 -0
- data/docs/docs/intro.md +99 -0
- data/docs/docusaurus.config.js +120 -0
- data/docs/package.json +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 +8790 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +0 -4
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +0 -1
- data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +11 -1
- data/lib/polyamorous/activerecord_7.1_ruby_2/join_association.rb +1 -0
- data/lib/polyamorous/activerecord_7.1_ruby_2/join_dependency.rb +1 -0
- data/lib/polyamorous/activerecord_7.1_ruby_2/reflection.rb +1 -0
- data/lib/ransack/adapters/active_record/base.rb +79 -10
- data/lib/ransack/adapters/active_record/context.rb +24 -51
- data/lib/ransack/configuration.rb +39 -12
- data/lib/ransack/constants.rb +125 -3
- data/lib/ransack/context.rb +34 -5
- data/lib/ransack/helpers/form_builder.rb +3 -3
- data/lib/ransack/helpers/form_helper.rb +14 -5
- data/lib/ransack/locale/sv.yml +70 -0
- 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/sort.rb +2 -2
- data/lib/ransack/nodes/value.rb +2 -2
- data/lib/ransack/predicate.rb +1 -1
- data/lib/ransack/ransacker.rb +1 -1
- data/lib/ransack/search.rb +13 -7
- data/lib/ransack/translate.rb +3 -3
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +38 -2
- data/lib/ransack.rb +3 -6
- data/ransack.gemspec +5 -5
- data/spec/helpers/polyamorous_helper.rb +2 -8
- data/spec/polyamorous/activerecord_compatibility_spec.rb +15 -0
- data/spec/polyamorous/join_association_spec.rb +1 -6
- data/spec/polyamorous/join_dependency_spec.rb +0 -16
- data/spec/ransack/adapters/active_record/base_spec.rb +101 -11
- data/spec/ransack/configuration_spec.rb +23 -9
- data/spec/ransack/helpers/form_builder_spec.rb +8 -8
- data/spec/ransack/helpers/form_helper_spec.rb +93 -4
- data/spec/ransack/nodes/condition_spec.rb +37 -0
- data/spec/ransack/nodes/value_spec.rb +115 -0
- data/spec/ransack/predicate_spec.rb +36 -1
- data/spec/ransack/search_spec.rb +140 -27
- data/spec/ransack/translate_spec.rb +1 -1
- data/spec/support/schema.rb +75 -9
- metadata +83 -37
- data/docs/release_process.md +0 -20
- data/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +0 -24
- data/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +0 -79
- data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +0 -11
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +0 -1
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +0 -80
- data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.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 -68
- 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/docs/{img → docs/going-further/img}/create_release.png +0 -0
- /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/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_association.rb +0 -0
- /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/join_dependency.rb +0 -0
- /data/lib/polyamorous/{activerecord_6.2_ruby_2 → activerecord_7.0_ruby_2}/reflection.rb +0 -0
- /data/lib/ransack/{adapters/active_record.rb → active_record.rb} +0 -0
|
@@ -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
|
|
|
@@ -123,7 +132,7 @@ module Ransack
|
|
|
123
132
|
end
|
|
124
133
|
|
|
125
134
|
it 'raises exception if ransack! called with unknown condition' do
|
|
126
|
-
expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error
|
|
135
|
+
expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error(ArgumentError)
|
|
127
136
|
end
|
|
128
137
|
|
|
129
138
|
it 'does not modify the parameters' do
|
|
@@ -314,7 +323,11 @@ module Ransack
|
|
|
314
323
|
end
|
|
315
324
|
|
|
316
325
|
it 'should function correctly with a multi-parameter attribute' do
|
|
317
|
-
::ActiveRecord::
|
|
326
|
+
if ::ActiveRecord::VERSION::MAJOR >= 7
|
|
327
|
+
::ActiveRecord.default_timezone = :utc
|
|
328
|
+
else
|
|
329
|
+
::ActiveRecord::Base.default_timezone = :utc
|
|
330
|
+
end
|
|
318
331
|
Time.zone = 'UTC'
|
|
319
332
|
|
|
320
333
|
date = Date.current
|
|
@@ -644,6 +657,37 @@ module Ransack
|
|
|
644
657
|
it { should_not include 'only_sort' }
|
|
645
658
|
it { should include 'only_admin' }
|
|
646
659
|
end
|
|
660
|
+
|
|
661
|
+
context 'when not defined in model, nor in ApplicationRecord' do
|
|
662
|
+
subject { Article.ransackable_attributes }
|
|
663
|
+
|
|
664
|
+
it "raises a helpful error" do
|
|
665
|
+
without_application_record_method(:ransackable_attributes) do
|
|
666
|
+
expect { subject }.to raise_error(RuntimeError, /Ransack needs Article attributes explicitly allowlisted/)
|
|
667
|
+
end
|
|
668
|
+
end
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
context 'when defined only in model by delegating to super' do
|
|
672
|
+
subject { Article.ransackable_attributes }
|
|
673
|
+
|
|
674
|
+
around do |example|
|
|
675
|
+
Article.singleton_class.define_method(:ransackable_attributes) do
|
|
676
|
+
super(nil) - super(nil)
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
example.run
|
|
680
|
+
ensure
|
|
681
|
+
Article.singleton_class.remove_method(:ransackable_attributes)
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
it "returns the allowlist in the model, and warns" do
|
|
685
|
+
without_application_record_method(:ransackable_attributes) do
|
|
686
|
+
expect { subject }.to output(/Ransack's builtin `ransackable_attributes` method is deprecated/).to_stderr
|
|
687
|
+
expect(subject).to be_empty
|
|
688
|
+
end
|
|
689
|
+
end
|
|
690
|
+
end
|
|
647
691
|
end
|
|
648
692
|
|
|
649
693
|
describe '#ransortable_attributes' do
|
|
@@ -676,6 +720,37 @@ module Ransack
|
|
|
676
720
|
it { should include 'parent' }
|
|
677
721
|
it { should include 'children' }
|
|
678
722
|
it { should include 'articles' }
|
|
723
|
+
|
|
724
|
+
context 'when not defined in model, nor in ApplicationRecord' do
|
|
725
|
+
subject { Article.ransackable_associations }
|
|
726
|
+
|
|
727
|
+
it "raises a helpful error" do
|
|
728
|
+
without_application_record_method(:ransackable_associations) do
|
|
729
|
+
expect { subject }.to raise_error(RuntimeError, /Ransack needs Article associations explicitly allowlisted/)
|
|
730
|
+
end
|
|
731
|
+
end
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
context 'when defined only in model by delegating to super' do
|
|
735
|
+
subject { Article.ransackable_associations }
|
|
736
|
+
|
|
737
|
+
around do |example|
|
|
738
|
+
Article.singleton_class.define_method(:ransackable_associations) do
|
|
739
|
+
super(nil) - super(nil)
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
example.run
|
|
743
|
+
ensure
|
|
744
|
+
Article.singleton_class.remove_method(:ransackable_associations)
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
it "returns the allowlist in the model, and warns" do
|
|
748
|
+
without_application_record_method(:ransackable_associations) do
|
|
749
|
+
expect { subject }.to output(/Ransack's builtin `ransackable_associations` method is deprecated/).to_stderr
|
|
750
|
+
expect(subject).to be_empty
|
|
751
|
+
end
|
|
752
|
+
end
|
|
753
|
+
end
|
|
679
754
|
end
|
|
680
755
|
|
|
681
756
|
describe '#ransackable_scopes' do
|
|
@@ -690,6 +765,21 @@ module Ransack
|
|
|
690
765
|
it { should eq [] }
|
|
691
766
|
end
|
|
692
767
|
|
|
768
|
+
private
|
|
769
|
+
|
|
770
|
+
def without_application_record_method(method)
|
|
771
|
+
ApplicationRecord.singleton_class.alias_method :"original_#{method}", :"#{method}"
|
|
772
|
+
ApplicationRecord.singleton_class.remove_method :"#{method}"
|
|
773
|
+
|
|
774
|
+
yield
|
|
775
|
+
ensure
|
|
776
|
+
ApplicationRecord.singleton_class.alias_method :"#{method}", :"original_#{method}"
|
|
777
|
+
ApplicationRecord.singleton_class.remove_method :"original_#{method}"
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
def rails7_and_mysql
|
|
781
|
+
::ActiveRecord::VERSION::MAJOR >= 7 && ENV['DB'] == 'mysql'
|
|
782
|
+
end
|
|
693
783
|
end
|
|
694
784
|
end
|
|
695
785
|
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)
|
|
@@ -45,6 +45,20 @@ module Ransack
|
|
|
45
45
|
Ransack.options = default
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
it 'should have default value for strip_whitespace' do
|
|
49
|
+
expect(Ransack.options[:strip_whitespace]).to eq true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'changes default search key parameter' do
|
|
53
|
+
default = Ransack.options.clone
|
|
54
|
+
|
|
55
|
+
Ransack.configure { |c| c.strip_whitespace = false }
|
|
56
|
+
|
|
57
|
+
expect(Ransack.options[:strip_whitespace]).to eq false
|
|
58
|
+
|
|
59
|
+
Ransack.options = default
|
|
60
|
+
end
|
|
61
|
+
|
|
48
62
|
it 'should have default values for arrows' do
|
|
49
63
|
expect(Ransack.options[:up_arrow]).to eq '▼'
|
|
50
64
|
expect(Ransack.options[:down_arrow]).to eq '▲'
|
|
@@ -124,8 +138,8 @@ module Ransack
|
|
|
124
138
|
Ransack.configure do |config|
|
|
125
139
|
config.add_predicate(
|
|
126
140
|
:test_array_predicate,
|
|
127
|
-
:
|
|
128
|
-
:
|
|
141
|
+
wants_array: true,
|
|
142
|
+
compounds: true
|
|
129
143
|
)
|
|
130
144
|
end
|
|
131
145
|
|
|
@@ -139,11 +153,11 @@ module Ransack
|
|
|
139
153
|
Ransack.configure do |config|
|
|
140
154
|
config.add_predicate(
|
|
141
155
|
:test_in_predicate,
|
|
142
|
-
:
|
|
156
|
+
arel_predicate: 'in'
|
|
143
157
|
)
|
|
144
158
|
config.add_predicate(
|
|
145
159
|
:test_not_in_predicate,
|
|
146
|
-
:
|
|
160
|
+
arel_predicate: 'not_in'
|
|
147
161
|
)
|
|
148
162
|
end
|
|
149
163
|
|
|
@@ -157,13 +171,13 @@ module Ransack
|
|
|
157
171
|
Ransack.configure do |config|
|
|
158
172
|
config.add_predicate(
|
|
159
173
|
:test_in_predicate_no_array,
|
|
160
|
-
:
|
|
161
|
-
:
|
|
174
|
+
arel_predicate: 'in',
|
|
175
|
+
wants_array: false
|
|
162
176
|
)
|
|
163
177
|
config.add_predicate(
|
|
164
178
|
:test_not_in_predicate_no_array,
|
|
165
|
-
:
|
|
166
|
-
:
|
|
179
|
+
arel_predicate: 'not_in',
|
|
180
|
+
wants_array: false
|
|
167
181
|
)
|
|
168
182
|
end
|
|
169
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(
|
|
@@ -469,13 +495,12 @@ module Ransack
|
|
|
469
495
|
it { should match /exist\=existing/ }
|
|
470
496
|
end
|
|
471
497
|
|
|
472
|
-
context 'using a real ActionController::Parameter object'
|
|
473
|
-
if: ::ActiveRecord::VERSION::MAJOR > 3 do
|
|
498
|
+
context 'using a real ActionController::Parameter object' do
|
|
474
499
|
|
|
475
500
|
describe 'with symbol q:, #sort_link should include search params' do
|
|
476
501
|
subject { @controller.view_context.sort_link(Person.ransack, :name) }
|
|
477
502
|
let(:params) { ActionController::Parameters.new(
|
|
478
|
-
{ :
|
|
503
|
+
{ q: { name_eq: 'TEST' }, controller: 'people' }
|
|
479
504
|
) }
|
|
480
505
|
before { @controller.instance_variable_set(:@params, params) }
|
|
481
506
|
|
|
@@ -490,7 +515,7 @@ module Ransack
|
|
|
490
515
|
describe 'with symbol q:, #sort_url should include search params' do
|
|
491
516
|
subject { @controller.view_context.sort_url(Person.ransack, :name) }
|
|
492
517
|
let(:params) { ActionController::Parameters.new(
|
|
493
|
-
{ :
|
|
518
|
+
{ q: { name_eq: 'TEST' }, controller: 'people' }
|
|
494
519
|
) }
|
|
495
520
|
before { @controller.instance_variable_set(:@params, params) }
|
|
496
521
|
|
|
@@ -727,6 +752,70 @@ module Ransack
|
|
|
727
752
|
it { should match /Block label ▼/ }
|
|
728
753
|
end
|
|
729
754
|
|
|
755
|
+
describe '#sort_link with class option' do
|
|
756
|
+
subject { @controller.view_context
|
|
757
|
+
.sort_link(
|
|
758
|
+
[:main_app, Person.ransack(sorts: ['name desc'])],
|
|
759
|
+
:name,
|
|
760
|
+
class: 'people', controller: 'people'
|
|
761
|
+
)
|
|
762
|
+
}
|
|
763
|
+
it { should match /class="sort_link desc people"/ }
|
|
764
|
+
it { should_not match /people\?class=people/ }
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
describe '#sort_link with class option workaround' do
|
|
768
|
+
it "generates a correct link and prints a deprecation" do
|
|
769
|
+
expect do
|
|
770
|
+
link = @controller.view_context
|
|
771
|
+
.sort_link(
|
|
772
|
+
[:main_app, Person.ransack(sorts: ['name desc'])],
|
|
773
|
+
:name,
|
|
774
|
+
'name',
|
|
775
|
+
{ controller: 'people' },
|
|
776
|
+
class: 'people'
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
expect(link).to match(/class="sort_link desc people"/)
|
|
780
|
+
expect(link).not_to match(/people\?class=people/)
|
|
781
|
+
end.to output(
|
|
782
|
+
/Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one\. \(called at #{Regexp.escape(__FILE__)}:/
|
|
783
|
+
).to_stderr
|
|
784
|
+
end
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
describe '#sort_link with data option' do
|
|
788
|
+
subject { @controller.view_context
|
|
789
|
+
.sort_link(
|
|
790
|
+
[:main_app, Person.ransack(sorts: ['name desc'])],
|
|
791
|
+
:name,
|
|
792
|
+
data: { turbo_action: :advance }, controller: 'people'
|
|
793
|
+
)
|
|
794
|
+
}
|
|
795
|
+
it { should match /data-turbo-action="advance"/ }
|
|
796
|
+
it { should_not match /people\?data%5Bturbo_action%5D=advance/ }
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
describe "#sort_link with host option" do
|
|
800
|
+
subject { @controller.view_context
|
|
801
|
+
.sort_link(
|
|
802
|
+
[:main_app, Person.ransack(sorts: ['name desc'])],
|
|
803
|
+
:name,
|
|
804
|
+
host: 'foo', controller: 'people'
|
|
805
|
+
)
|
|
806
|
+
}
|
|
807
|
+
it { should match /href="\/people\?q/ }
|
|
808
|
+
it { should_not match /href=".*foo/ }
|
|
809
|
+
end
|
|
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
|
+
|
|
730
819
|
describe '#search_form_for with default format' do
|
|
731
820
|
subject { @controller.view_context
|
|
732
821
|
.search_form_for(Person.ransack) {} }
|
|
@@ -3,6 +3,19 @@ require 'spec_helper'
|
|
|
3
3
|
module Ransack
|
|
4
4
|
module Nodes
|
|
5
5
|
describe Condition do
|
|
6
|
+
context 'bug report #1245' do
|
|
7
|
+
it 'preserves tuple behavior' do
|
|
8
|
+
ransack_hash = {
|
|
9
|
+
m: 'and',
|
|
10
|
+
g: [
|
|
11
|
+
{ title_type_in: ['["title 1", ""]'] }
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
sql = Article.ransack(ransack_hash).result.to_sql
|
|
16
|
+
expect(sql).to include("IN (('title 1', ''))")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
6
19
|
|
|
7
20
|
context 'with an alias' do
|
|
8
21
|
subject {
|
|
@@ -61,6 +74,30 @@ module Ransack
|
|
|
61
74
|
specify { expect(subject).to be_nil }
|
|
62
75
|
end
|
|
63
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
|
|
64
101
|
end
|
|
65
102
|
end
|
|
66
103
|
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Ransack
|
|
4
|
+
module Nodes
|
|
5
|
+
describe Value do
|
|
6
|
+
let(:context) { Context.for(Person) }
|
|
7
|
+
|
|
8
|
+
subject do
|
|
9
|
+
Value.new(context, raw_value)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context "with a date value" do
|
|
13
|
+
let(:raw_value) { "2022-05-23" }
|
|
14
|
+
|
|
15
|
+
[:date].each do |type|
|
|
16
|
+
it "should cast #{type} correctly" do
|
|
17
|
+
result = subject.cast(type)
|
|
18
|
+
|
|
19
|
+
expect(result).to be_a_kind_of(Date)
|
|
20
|
+
expect(result).to eq(Date.parse(raw_value))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "with a timestamp value" do
|
|
26
|
+
let(:raw_value) { "2022-05-23 10:40:02 -0400" }
|
|
27
|
+
|
|
28
|
+
[:datetime, :timestamp, :time, :timestamptz].each do |type|
|
|
29
|
+
it "should cast #{type} correctly" do
|
|
30
|
+
result = subject.cast(type)
|
|
31
|
+
|
|
32
|
+
expect(result).to be_a_kind_of(Time)
|
|
33
|
+
expect(result).to eq(Time.zone.parse(raw_value))
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Constants::TRUE_VALUES.each do |value|
|
|
39
|
+
context "with a true boolean value (#{value})" do
|
|
40
|
+
let(:raw_value) { value.to_s }
|
|
41
|
+
|
|
42
|
+
it "should cast boolean correctly" do
|
|
43
|
+
result = subject.cast(:boolean)
|
|
44
|
+
expect(result).to eq(true)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Constants::FALSE_VALUES.each do |value|
|
|
50
|
+
context "with a false boolean value (#{value})" do
|
|
51
|
+
let(:raw_value) { value.to_s }
|
|
52
|
+
|
|
53
|
+
it "should cast boolean correctly" do
|
|
54
|
+
result = subject.cast(:boolean)
|
|
55
|
+
|
|
56
|
+
expect(result).to eq(false)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
["12", "101.5"].each do |value|
|
|
62
|
+
context "with an integer value (#{value})" do
|
|
63
|
+
let(:raw_value) { value }
|
|
64
|
+
|
|
65
|
+
it "should cast #{value} to integer correctly" do
|
|
66
|
+
result = subject.cast(:integer)
|
|
67
|
+
|
|
68
|
+
expect(result).to be_an(Integer)
|
|
69
|
+
expect(result).to eq(value.to_i)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
["12", "101.5"].each do |value|
|
|
75
|
+
context "with a float value (#{value})" do
|
|
76
|
+
let(:raw_value) { value }
|
|
77
|
+
|
|
78
|
+
it "should cast #{value} to float correctly" do
|
|
79
|
+
result = subject.cast(:float)
|
|
80
|
+
|
|
81
|
+
expect(result).to be_an(Float)
|
|
82
|
+
expect(result).to eq(value.to_f)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
["12", "101.5"].each do |value|
|
|
88
|
+
context "with a decimal value (#{value})" do
|
|
89
|
+
let(:raw_value) { value }
|
|
90
|
+
|
|
91
|
+
it "should cast #{value} to decimal correctly" do
|
|
92
|
+
result = subject.cast(:decimal)
|
|
93
|
+
|
|
94
|
+
expect(result).to be_a(BigDecimal)
|
|
95
|
+
expect(result).to eq(value.to_d)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
["12", "101.513"].each do |value|
|
|
101
|
+
context "with a money value (#{value})" do
|
|
102
|
+
let(:raw_value) { value }
|
|
103
|
+
|
|
104
|
+
it "should cast #{value} to money correctly" do
|
|
105
|
+
result = subject.cast(:money)
|
|
106
|
+
|
|
107
|
+
expect(result).to be_a(String)
|
|
108
|
+
expect(result).to eq(value.to_f.to_s)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|