ransack 2.4.2 → 3.2.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/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 +16 -40
- data/.nojekyll +0 -0
- data/CHANGELOG.md +157 -11
- data/CONTRIBUTING.md +8 -7
- data/Gemfile +2 -2
- data/README.md +44 -972
- 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 +284 -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 +38 -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 +8436 -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/polyamorous.rb +1 -0
- data/lib/ransack/adapters/active_record/base.rb +1 -3
- data/lib/ransack/adapters/active_record/context.rb +24 -51
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +2 -9
- data/lib/ransack/configuration.rb +16 -2
- data/lib/ransack/constants.rb +0 -3
- data/lib/ransack/helpers/form_helper.rb +11 -3
- data/lib/ransack/locale/sv.yml +70 -0
- data/lib/ransack/nodes/sort.rb +2 -2
- data/lib/ransack/nodes/value.rb +1 -1
- data/lib/ransack/search.rb +4 -3
- data/lib/ransack/translate.rb +1 -1
- data/lib/ransack/version.rb +1 -1
- 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 +28 -11
- data/spec/ransack/configuration_spec.rb +14 -0
- data/spec/ransack/helpers/form_helper_spec.rb +57 -2
- data/spec/ransack/nodes/condition_spec.rb +13 -0
- data/spec/ransack/nodes/value_spec.rb +115 -0
- data/spec/ransack/search_spec.rb +140 -27
- data/spec/support/schema.rb +49 -0
- metadata +82 -29
- 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/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
|
@@ -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 '▲'
|
|
@@ -469,8 +469,7 @@ module Ransack
|
|
|
469
469
|
it { should match /exist\=existing/ }
|
|
470
470
|
end
|
|
471
471
|
|
|
472
|
-
context 'using a real ActionController::Parameter object'
|
|
473
|
-
if: ::ActiveRecord::VERSION::MAJOR > 3 do
|
|
472
|
+
context 'using a real ActionController::Parameter object' do
|
|
474
473
|
|
|
475
474
|
describe 'with symbol q:, #sort_link should include search params' do
|
|
476
475
|
subject { @controller.view_context.sort_link(Person.ransack, :name) }
|
|
@@ -727,6 +726,62 @@ module Ransack
|
|
|
727
726
|
it { should match /Block label ▼/ }
|
|
728
727
|
end
|
|
729
728
|
|
|
729
|
+
describe '#sort_link with class option' do
|
|
730
|
+
subject { @controller.view_context
|
|
731
|
+
.sort_link(
|
|
732
|
+
[:main_app, Person.ransack(sorts: ['name desc'])],
|
|
733
|
+
:name,
|
|
734
|
+
class: 'people', controller: 'people'
|
|
735
|
+
)
|
|
736
|
+
}
|
|
737
|
+
it { should match /class="sort_link desc people"/ }
|
|
738
|
+
it { should_not match /people\?class=people/ }
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
describe '#sort_link with class option workaround' do
|
|
742
|
+
it "generates a correct link and prints a deprecation" do
|
|
743
|
+
expect do
|
|
744
|
+
link = @controller.view_context
|
|
745
|
+
.sort_link(
|
|
746
|
+
[:main_app, Person.ransack(sorts: ['name desc'])],
|
|
747
|
+
:name,
|
|
748
|
+
'name',
|
|
749
|
+
{ controller: 'people' },
|
|
750
|
+
class: 'people'
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
expect(link).to match(/class="sort_link desc people"/)
|
|
754
|
+
expect(link).not_to match(/people\?class=people/)
|
|
755
|
+
end.to output(
|
|
756
|
+
/Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one\. \(called at #{Regexp.escape(__FILE__)}:/
|
|
757
|
+
).to_stderr
|
|
758
|
+
end
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
describe '#sort_link with data option' do
|
|
762
|
+
subject { @controller.view_context
|
|
763
|
+
.sort_link(
|
|
764
|
+
[:main_app, Person.ransack(sorts: ['name desc'])],
|
|
765
|
+
:name,
|
|
766
|
+
data: { turbo_action: :advance }, controller: 'people'
|
|
767
|
+
)
|
|
768
|
+
}
|
|
769
|
+
it { should match /data-turbo-action="advance"/ }
|
|
770
|
+
it { should_not match /people\?data%5Bturbo_action%5D=advance/ }
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
describe "#sort_link with host option" do
|
|
774
|
+
subject { @controller.view_context
|
|
775
|
+
.sort_link(
|
|
776
|
+
[:main_app, Person.ransack(sorts: ['name desc'])],
|
|
777
|
+
:name,
|
|
778
|
+
host: 'foo', controller: 'people'
|
|
779
|
+
)
|
|
780
|
+
}
|
|
781
|
+
it { should match /href="\/people\?q/ }
|
|
782
|
+
it { should_not match /href=".*foo/ }
|
|
783
|
+
end
|
|
784
|
+
|
|
730
785
|
describe '#search_form_for with default format' do
|
|
731
786
|
subject { @controller.view_context
|
|
732
787
|
.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 {
|
|
@@ -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
|
data/spec/ransack/search_spec.rb
CHANGED
|
@@ -20,10 +20,42 @@ module Ransack
|
|
|
20
20
|
Search.new(Person, name_eq: 'foobar')
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
context 'whitespace stripping' do
|
|
24
|
+
context 'when whitespace_strip option is true' do
|
|
25
|
+
before do
|
|
26
|
+
Ransack.configure { |c| c.strip_whitespace = true }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'strips leading & trailing whitespace before building' do
|
|
30
|
+
expect_any_instance_of(Search).to receive(:build)
|
|
31
|
+
.with({ 'name_eq' => 'foobar' })
|
|
32
|
+
Search.new(Person, name_eq: ' foobar ')
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'when whitespace_strip option is false' do
|
|
37
|
+
before do
|
|
38
|
+
Ransack.configure { |c| c.strip_whitespace = false }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'doesn\'t strip leading & trailing whitespace before building' do
|
|
42
|
+
expect_any_instance_of(Search).to receive(:build)
|
|
43
|
+
.with({ 'name_eq' => ' foobar ' })
|
|
44
|
+
Search.new(Person, name_eq: ' foobar ')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'strips leading & trailing whitespace when strip_whitespace search parameter is true' do
|
|
49
|
+
expect_any_instance_of(Search).to receive(:build)
|
|
50
|
+
.with({ 'name_eq' => 'foobar' })
|
|
51
|
+
Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: true })
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'doesn\'t strip leading & trailing whitespace when strip_whitespace search parameter is false' do
|
|
55
|
+
expect_any_instance_of(Search).to receive(:build)
|
|
56
|
+
.with({ 'name_eq' => ' foobar ' })
|
|
57
|
+
Search.new(Person, { name_eq: ' foobar ' }, { strip_whitespace: false })
|
|
58
|
+
end
|
|
27
59
|
end
|
|
28
60
|
|
|
29
61
|
it 'removes empty suffixed conditions before building' do
|
|
@@ -280,6 +312,29 @@ module Ransack
|
|
|
280
312
|
expect { Search.new(Person, params) }.not_to change { params }
|
|
281
313
|
end
|
|
282
314
|
|
|
315
|
+
context "ransackable_scope" do
|
|
316
|
+
around(:each) do |example|
|
|
317
|
+
Person.define_singleton_method(:name_eq) do |name|
|
|
318
|
+
self.where(name: name)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
begin
|
|
322
|
+
example.run
|
|
323
|
+
ensure
|
|
324
|
+
Person.singleton_class.undef_method :name_eq
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
it "is prioritized over base predicates" do
|
|
329
|
+
allow(Person).to receive(:ransackable_scopes)
|
|
330
|
+
.and_return(Person.ransackable_scopes + [:name_eq])
|
|
331
|
+
|
|
332
|
+
s = Search.new(Person, name_eq: "Johny")
|
|
333
|
+
expect(s.instance_variable_get(:@scope_args)["name_eq"]).to eq("Johny")
|
|
334
|
+
expect(s.base[:name_eq]).to be_nil
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
283
338
|
end
|
|
284
339
|
|
|
285
340
|
describe '#result' do
|
|
@@ -300,8 +355,6 @@ module Ransack
|
|
|
300
355
|
end
|
|
301
356
|
|
|
302
357
|
it 'use appropriate table alias' do
|
|
303
|
-
skip "Rails 6 regressed here, but it's fixed in 6-0-stable since https://github.com/rails/rails/commit/f9ba52477ca288e7effa5f6794ae3df3f4e982bc" if ENV["RAILS"] == "v6.0.3"
|
|
304
|
-
|
|
305
358
|
s = Search.new(Person, {
|
|
306
359
|
name_eq: "person_name_query",
|
|
307
360
|
articles_title_eq: "person_article_title_query",
|
|
@@ -451,82 +504,109 @@ module Ransack
|
|
|
451
504
|
expect(sort.dir).to eq 'asc'
|
|
452
505
|
end
|
|
453
506
|
|
|
454
|
-
it 'creates sorts based on
|
|
455
|
-
@s.sorts =
|
|
507
|
+
it 'creates sorts based on a single alias/direction' do
|
|
508
|
+
@s.sorts = 'daddy desc'
|
|
509
|
+
expect(@s.sorts.size).to eq(1)
|
|
510
|
+
sort = @s.sorts.first
|
|
511
|
+
expect(sort).to be_a Nodes::Sort
|
|
512
|
+
expect(sort.name).to eq 'parent_name'
|
|
513
|
+
expect(sort.dir).to eq 'desc'
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
it 'creates sorts based on a single alias and uppercase direction' do
|
|
517
|
+
@s.sorts = 'daddy DESC'
|
|
518
|
+
expect(@s.sorts.size).to eq(1)
|
|
519
|
+
sort = @s.sorts.first
|
|
520
|
+
expect(sort).to be_a Nodes::Sort
|
|
521
|
+
expect(sort.name).to eq 'parent_name'
|
|
522
|
+
expect(sort.dir).to eq 'desc'
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
it 'creates sorts based on a single alias and without direction' do
|
|
526
|
+
@s.sorts = 'daddy'
|
|
527
|
+
expect(@s.sorts.size).to eq(1)
|
|
528
|
+
sort = @s.sorts.first
|
|
529
|
+
expect(sort).to be_a Nodes::Sort
|
|
530
|
+
expect(sort.name).to eq 'parent_name'
|
|
531
|
+
expect(sort.dir).to eq 'asc'
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
it 'creates sorts based on attributes, alias and directions in array format' do
|
|
535
|
+
@s.sorts = ['id desc', { name: 'daddy', dir: 'asc' }]
|
|
456
536
|
expect(@s.sorts.size).to eq(2)
|
|
457
537
|
sort1, sort2 = @s.sorts
|
|
458
538
|
expect(sort1).to be_a Nodes::Sort
|
|
459
539
|
expect(sort1.name).to eq 'id'
|
|
460
540
|
expect(sort1.dir).to eq 'desc'
|
|
461
541
|
expect(sort2).to be_a Nodes::Sort
|
|
462
|
-
expect(sort2.name).to eq '
|
|
542
|
+
expect(sort2.name).to eq 'parent_name'
|
|
463
543
|
expect(sort2.dir).to eq 'asc'
|
|
464
544
|
end
|
|
465
545
|
|
|
466
|
-
it 'creates sorts based on
|
|
467
|
-
@s.sorts = ['id DESC', { name: '
|
|
546
|
+
it 'creates sorts based on attributes, alias and uppercase directions in array format' do
|
|
547
|
+
@s.sorts = ['id DESC', { name: 'daddy', dir: 'ASC' }]
|
|
468
548
|
expect(@s.sorts.size).to eq(2)
|
|
469
549
|
sort1, sort2 = @s.sorts
|
|
470
550
|
expect(sort1).to be_a Nodes::Sort
|
|
471
551
|
expect(sort1.name).to eq 'id'
|
|
472
552
|
expect(sort1.dir).to eq 'desc'
|
|
473
553
|
expect(sort2).to be_a Nodes::Sort
|
|
474
|
-
expect(sort2.name).to eq '
|
|
554
|
+
expect(sort2.name).to eq 'parent_name'
|
|
475
555
|
expect(sort2.dir).to eq 'asc'
|
|
476
556
|
end
|
|
477
557
|
|
|
478
|
-
it 'creates sorts based on
|
|
558
|
+
it 'creates sorts based on attributes, alias and different directions
|
|
479
559
|
in array format' do
|
|
480
|
-
@s.sorts = ['id DESC', { name: '
|
|
560
|
+
@s.sorts = ['id DESC', { name: 'daddy', dir: nil }]
|
|
481
561
|
expect(@s.sorts.size).to eq(2)
|
|
482
562
|
sort1, sort2 = @s.sorts
|
|
483
563
|
expect(sort1).to be_a Nodes::Sort
|
|
484
564
|
expect(sort1.name).to eq 'id'
|
|
485
565
|
expect(sort1.dir).to eq 'desc'
|
|
486
566
|
expect(sort2).to be_a Nodes::Sort
|
|
487
|
-
expect(sort2.name).to eq '
|
|
567
|
+
expect(sort2.name).to eq 'parent_name'
|
|
488
568
|
expect(sort2.dir).to eq 'asc'
|
|
489
569
|
end
|
|
490
570
|
|
|
491
|
-
it 'creates sorts based on
|
|
571
|
+
it 'creates sorts based on attributes, alias and directions in hash format' do
|
|
492
572
|
@s.sorts = {
|
|
493
573
|
'0' => { name: 'id', dir: 'desc' },
|
|
494
|
-
'1' => { name: '
|
|
574
|
+
'1' => { name: 'daddy', dir: 'asc' }
|
|
495
575
|
}
|
|
496
576
|
expect(@s.sorts.size).to eq(2)
|
|
497
577
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
|
498
578
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
|
499
|
-
|
|
579
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
|
500
580
|
expect(id_sort.dir).to eq 'desc'
|
|
501
|
-
expect(
|
|
581
|
+
expect(daddy_sort.dir).to eq 'asc'
|
|
502
582
|
end
|
|
503
583
|
|
|
504
|
-
it 'creates sorts based on
|
|
584
|
+
it 'creates sorts based on attributes, alias and uppercase directions
|
|
505
585
|
in hash format' do
|
|
506
586
|
@s.sorts = {
|
|
507
587
|
'0' => { name: 'id', dir: 'DESC' },
|
|
508
|
-
'1' => { name: '
|
|
588
|
+
'1' => { name: 'daddy', dir: 'ASC' }
|
|
509
589
|
}
|
|
510
590
|
expect(@s.sorts.size).to eq(2)
|
|
511
591
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
|
512
592
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
|
513
|
-
|
|
593
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
|
514
594
|
expect(id_sort.dir).to eq 'desc'
|
|
515
|
-
expect(
|
|
595
|
+
expect(daddy_sort.dir).to eq 'asc'
|
|
516
596
|
end
|
|
517
597
|
|
|
518
|
-
it 'creates sorts based on
|
|
598
|
+
it 'creates sorts based on attributes, alias and different directions
|
|
519
599
|
in hash format' do
|
|
520
600
|
@s.sorts = {
|
|
521
601
|
'0' => { name: 'id', dir: 'DESC' },
|
|
522
|
-
'1' => { name: '
|
|
602
|
+
'1' => { name: 'daddy', dir: nil }
|
|
523
603
|
}
|
|
524
604
|
expect(@s.sorts.size).to eq(2)
|
|
525
605
|
expect(@s.sorts).to be_all { |s| Nodes::Sort === s }
|
|
526
606
|
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
|
527
|
-
|
|
607
|
+
daddy_sort = @s.sorts.detect { |s| s.name == 'parent_name' }
|
|
528
608
|
expect(id_sort.dir).to eq 'desc'
|
|
529
|
-
expect(
|
|
609
|
+
expect(daddy_sort.dir).to eq 'asc'
|
|
530
610
|
end
|
|
531
611
|
|
|
532
612
|
it 'overrides existing sort' do
|
|
@@ -554,6 +634,39 @@ module Ransack
|
|
|
554
634
|
|
|
555
635
|
Ransack.options = default
|
|
556
636
|
end
|
|
637
|
+
|
|
638
|
+
it "PG's sort option with double name", if: ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" do
|
|
639
|
+
default = Ransack.options.clone
|
|
640
|
+
|
|
641
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
642
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC"
|
|
643
|
+
|
|
644
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_first }
|
|
645
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
646
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
|
|
647
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
|
648
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
|
|
649
|
+
|
|
650
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_last }
|
|
651
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
652
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
|
|
653
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
|
654
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
|
|
655
|
+
|
|
656
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_first }
|
|
657
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
658
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS FIRST"
|
|
659
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
|
660
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS FIRST"
|
|
661
|
+
|
|
662
|
+
Ransack.configure { |c| c.postgres_fields_sort_option = :nulls_always_last }
|
|
663
|
+
s = Search.new(Person, s: 'doubled_name asc')
|
|
664
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" ASC NULLS LAST"
|
|
665
|
+
s = Search.new(Person, s: 'doubled_name desc')
|
|
666
|
+
expect(s.result.to_sql).to eq "SELECT \"people\".* FROM \"people\" ORDER BY \"people\".\"name\" || \"people\".\"name\" DESC NULLS LAST"
|
|
667
|
+
|
|
668
|
+
Ransack.options = default
|
|
669
|
+
end
|
|
557
670
|
end
|
|
558
671
|
|
|
559
672
|
describe '#method_missing' do
|
data/spec/support/schema.rb
CHANGED
|
@@ -138,6 +138,45 @@ class Article < ActiveRecord::Base
|
|
|
138
138
|
alias_attribute :content, :body
|
|
139
139
|
|
|
140
140
|
default_scope { where("'default_scope' = 'default_scope'") }
|
|
141
|
+
scope :latest_comment_cont, lambda { |msg|
|
|
142
|
+
join = <<-SQL
|
|
143
|
+
(LEFT OUTER JOIN (
|
|
144
|
+
SELECT
|
|
145
|
+
comments.*,
|
|
146
|
+
row_number() OVER (PARTITION BY comments.article_id ORDER BY comments.id DESC) AS rownum
|
|
147
|
+
FROM comments
|
|
148
|
+
) AS latest_comment
|
|
149
|
+
ON latest_comment.article_id = article.id
|
|
150
|
+
AND latest_comment.rownum = 1
|
|
151
|
+
)
|
|
152
|
+
SQL
|
|
153
|
+
.squish
|
|
154
|
+
|
|
155
|
+
joins(join).where("latest_comment.body ILIKE ?", "%#{msg}%")
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
ransacker :title_type, formatter: lambda { |tuples|
|
|
159
|
+
title, type = JSON.parse(tuples)
|
|
160
|
+
Arel::Nodes::Grouping.new(
|
|
161
|
+
[
|
|
162
|
+
Arel::Nodes.build_quoted(title),
|
|
163
|
+
Arel::Nodes.build_quoted(type)
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
} do |_parent|
|
|
167
|
+
articles = Article.arel_table
|
|
168
|
+
Arel::Nodes::Grouping.new(
|
|
169
|
+
%i[title type].map do |field|
|
|
170
|
+
Arel::Nodes::NamedFunction.new(
|
|
171
|
+
'COALESCE',
|
|
172
|
+
[
|
|
173
|
+
Arel::Nodes::NamedFunction.new('TRIM', [articles[field]]),
|
|
174
|
+
Arel::Nodes.build_quoted('')
|
|
175
|
+
]
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
)
|
|
179
|
+
end
|
|
141
180
|
end
|
|
142
181
|
|
|
143
182
|
class StoryArticle < Article
|
|
@@ -176,6 +215,11 @@ class Note < ActiveRecord::Base
|
|
|
176
215
|
belongs_to :notable, polymorphic: true
|
|
177
216
|
end
|
|
178
217
|
|
|
218
|
+
class Account < ActiveRecord::Base
|
|
219
|
+
belongs_to :agent_account, class_name: "Account"
|
|
220
|
+
belongs_to :trade_account, class_name: "Account"
|
|
221
|
+
end
|
|
222
|
+
|
|
179
223
|
module Schema
|
|
180
224
|
def self.create
|
|
181
225
|
ActiveRecord::Migration.verbose = false
|
|
@@ -234,6 +278,11 @@ module Schema
|
|
|
234
278
|
t.integer :target_person_id
|
|
235
279
|
t.integer :article_id
|
|
236
280
|
end
|
|
281
|
+
|
|
282
|
+
create_table :accounts, force: true do |t|
|
|
283
|
+
t.belongs_to :agent_account
|
|
284
|
+
t.belongs_to :trade_account
|
|
285
|
+
end
|
|
237
286
|
end
|
|
238
287
|
|
|
239
288
|
10.times do
|