ransack 1.7.0 → 2.4.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 +5 -5
- data/.github/FUNDING.yml +3 -0
- data/.github/SECURITY.md +12 -0
- data/.github/workflows/test.yml +120 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +463 -27
- data/CONTRIBUTING.md +52 -22
- data/Gemfile +24 -24
- data/README.md +453 -126
- data/Rakefile +6 -25
- data/lib/polyamorous/activerecord_5.2_ruby_2/join_association.rb +24 -0
- data/lib/polyamorous/activerecord_5.2_ruby_2/join_dependency.rb +79 -0
- data/lib/polyamorous/activerecord_5.2_ruby_2/reflection.rb +11 -0
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_association.rb +1 -0
- data/lib/polyamorous/activerecord_6.0_ruby_2/join_dependency.rb +80 -0
- data/lib/polyamorous/activerecord_6.0_ruby_2/reflection.rb +1 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_association.rb +74 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/join_dependency.rb +93 -0
- data/lib/polyamorous/activerecord_6.1_ruby_2/reflection.rb +1 -0
- data/lib/polyamorous/join.rb +70 -0
- data/lib/polyamorous/polyamorous.rb +24 -0
- data/lib/polyamorous/swapping_reflection_class.rb +11 -0
- data/lib/polyamorous/tree_node.rb +7 -0
- data/lib/ransack/adapters/active_record/base.rb +27 -2
- data/lib/ransack/adapters/active_record/context.rb +213 -139
- data/lib/ransack/adapters/active_record/ransack/constants.rb +70 -55
- data/lib/ransack/adapters/active_record/ransack/context.rb +10 -18
- data/lib/ransack/adapters/active_record/ransack/nodes/condition.rb +42 -32
- data/lib/ransack/adapters/active_record/ransack/translate.rb +1 -5
- data/lib/ransack/adapters/active_record/ransack/visitor.rb +23 -0
- data/lib/ransack/adapters/active_record.rb +11 -10
- data/lib/ransack/adapters.rb +45 -23
- data/lib/ransack/configuration.rb +107 -4
- data/lib/ransack/constants.rb +13 -26
- data/lib/ransack/context.rb +45 -33
- data/lib/ransack/helpers/form_builder.rb +21 -12
- data/lib/ransack/helpers/form_helper.rb +75 -70
- data/lib/ransack/locale/ar.yml +70 -0
- data/lib/ransack/locale/az.yml +70 -0
- data/lib/ransack/locale/bg.yml +70 -0
- data/lib/ransack/locale/ca.yml +70 -0
- data/lib/ransack/locale/da.yml +70 -0
- data/lib/ransack/locale/el.yml +70 -0
- data/lib/ransack/locale/es.yml +22 -22
- data/lib/ransack/locale/fa.yml +70 -0
- data/lib/ransack/locale/fi.yml +71 -0
- data/lib/ransack/locale/id.yml +70 -0
- data/lib/ransack/locale/it.yml +70 -0
- data/lib/ransack/locale/ja.yml +70 -0
- data/lib/ransack/locale/nl.yml +4 -4
- data/lib/ransack/locale/pt-BR.yml +70 -0
- data/lib/ransack/locale/ru.yml +70 -0
- data/lib/ransack/locale/sk.yml +70 -0
- data/lib/ransack/locale/tr.yml +70 -0
- data/lib/ransack/locale/{zh.yml → zh-CN.yml} +13 -13
- data/lib/ransack/locale/zh-TW.yml +70 -0
- data/lib/ransack/nodes/attribute.rb +5 -2
- data/lib/ransack/nodes/bindable.rb +18 -6
- data/lib/ransack/nodes/condition.rb +85 -28
- data/lib/ransack/nodes/grouping.rb +17 -11
- data/lib/ransack/nodes/sort.rb +9 -5
- data/lib/ransack/nodes/value.rb +74 -68
- data/lib/ransack/nodes.rb +1 -1
- data/lib/ransack/predicate.rb +17 -20
- data/lib/ransack/search.rb +17 -8
- data/lib/ransack/translate.rb +115 -115
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +1 -12
- data/lib/ransack.rb +9 -9
- data/logo/ransack-h.png +0 -0
- data/logo/ransack-h.svg +34 -0
- data/logo/ransack-v.png +0 -0
- data/logo/ransack-v.svg +34 -0
- data/logo/ransack.png +0 -0
- data/logo/ransack.svg +21 -0
- data/ransack.gemspec +7 -24
- data/spec/console.rb +4 -0
- data/spec/helpers/polyamorous_helper.rb +19 -0
- data/spec/polyamorous/join_association_spec.rb +35 -0
- data/spec/polyamorous/join_dependency_spec.rb +97 -0
- data/spec/polyamorous/join_spec.rb +19 -0
- data/spec/ransack/adapters/active_record/base_spec.rb +370 -75
- data/spec/ransack/adapters/active_record/context_spec.rb +72 -34
- data/spec/ransack/configuration_spec.rb +97 -14
- data/spec/ransack/helpers/form_builder_spec.rb +2 -11
- data/spec/ransack/helpers/form_helper_spec.rb +481 -113
- data/spec/ransack/nodes/condition_spec.rb +24 -0
- data/spec/ransack/nodes/grouping_spec.rb +56 -0
- data/spec/ransack/predicate_spec.rb +79 -5
- data/spec/ransack/search_spec.rb +207 -81
- data/spec/spec_helper.rb +8 -0
- data/spec/support/schema.rb +100 -42
- metadata +57 -184
- data/.travis.yml +0 -69
- data/lib/ransack/adapters/active_record/3.0/compat.rb +0 -179
- data/lib/ransack/adapters/active_record/3.0/context.rb +0 -201
- data/lib/ransack/adapters/active_record/3.1/context.rb +0 -215
- data/lib/ransack/adapters/active_record/3.2/context.rb +0 -44
- data/lib/ransack/adapters/active_record/compat.rb +0 -14
- data/lib/ransack/adapters/mongoid/3.2/.gitkeep +0 -0
- data/lib/ransack/adapters/mongoid/attributes/attribute.rb +0 -37
- data/lib/ransack/adapters/mongoid/attributes/order_predications.rb +0 -17
- data/lib/ransack/adapters/mongoid/attributes/predications.rb +0 -141
- data/lib/ransack/adapters/mongoid/base.rb +0 -130
- data/lib/ransack/adapters/mongoid/context.rb +0 -208
- data/lib/ransack/adapters/mongoid/inquiry_hash.rb +0 -23
- data/lib/ransack/adapters/mongoid/ransack/constants.rb +0 -88
- data/lib/ransack/adapters/mongoid/ransack/context.rb +0 -60
- data/lib/ransack/adapters/mongoid/ransack/nodes/condition.rb +0 -27
- data/lib/ransack/adapters/mongoid/ransack/translate.rb +0 -13
- data/lib/ransack/adapters/mongoid/ransack/visitor.rb +0 -24
- data/lib/ransack/adapters/mongoid/table.rb +0 -35
- data/lib/ransack/adapters/mongoid.rb +0 -13
- data/spec/mongoid/adapters/mongoid/base_spec.rb +0 -276
- data/spec/mongoid/adapters/mongoid/context_spec.rb +0 -56
- data/spec/mongoid/configuration_spec.rb +0 -102
- data/spec/mongoid/dependencies_spec.rb +0 -8
- data/spec/mongoid/helpers/ransack_helper.rb +0 -11
- data/spec/mongoid/nodes/condition_spec.rb +0 -34
- data/spec/mongoid/nodes/grouping_spec.rb +0 -13
- data/spec/mongoid/predicate_spec.rb +0 -155
- data/spec/mongoid/search_spec.rb +0 -446
- data/spec/mongoid/support/mongoid.yml +0 -6
- data/spec/mongoid/support/schema.rb +0 -128
- data/spec/mongoid/translate_spec.rb +0 -14
- data/spec/mongoid_spec_helper.rb +0 -59
- data/spec/ransack/dependencies_spec.rb +0 -12
|
@@ -18,73 +18,240 @@ module Ransack
|
|
|
18
18
|
expect(subject.object).to be_an ::ActiveRecord::Relation
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
context "multiple database connection" do
|
|
22
|
+
it "does not raise error" do
|
|
23
|
+
expect { Person.ransack(name_cont: "test") }.not_to raise_error
|
|
24
|
+
expect { SubDB::OperationHistory.ransack(people_id_eq: 1) }.not_to raise_error
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
21
28
|
context 'with scopes' do
|
|
22
29
|
before do
|
|
23
|
-
Person
|
|
30
|
+
allow(Person)
|
|
31
|
+
.to receive(:ransackable_scopes)
|
|
32
|
+
.and_return([:active, :over_age, :of_age])
|
|
24
33
|
end
|
|
25
34
|
|
|
26
|
-
it
|
|
35
|
+
it 'applies true scopes' do
|
|
27
36
|
s = Person.ransack('active' => true)
|
|
28
37
|
expect(s.result.to_sql).to (include 'active = 1')
|
|
29
38
|
end
|
|
30
39
|
|
|
31
|
-
it
|
|
40
|
+
it 'applies stringy true scopes' do
|
|
32
41
|
s = Person.ransack('active' => 'true')
|
|
33
42
|
expect(s.result.to_sql).to (include 'active = 1')
|
|
34
43
|
end
|
|
35
44
|
|
|
36
|
-
it
|
|
45
|
+
it 'applies stringy boolean scopes with true value in an array' do
|
|
37
46
|
s = Person.ransack('of_age' => ['true'])
|
|
38
47
|
expect(s.result.to_sql).to (include 'age >= 18')
|
|
39
48
|
end
|
|
40
49
|
|
|
41
|
-
it
|
|
50
|
+
it 'applies stringy boolean scopes with false value in an array' do
|
|
42
51
|
s = Person.ransack('of_age' => ['false'])
|
|
43
52
|
expect(s.result.to_sql).to (include 'age < 18')
|
|
44
53
|
end
|
|
45
54
|
|
|
46
|
-
it
|
|
55
|
+
it 'ignores unlisted scopes' do
|
|
47
56
|
s = Person.ransack('restricted' => true)
|
|
48
57
|
expect(s.result.to_sql).to_not (include 'restricted')
|
|
49
58
|
end
|
|
50
59
|
|
|
51
|
-
it
|
|
60
|
+
it 'ignores false scopes' do
|
|
52
61
|
s = Person.ransack('active' => false)
|
|
53
62
|
expect(s.result.to_sql).not_to (include 'active')
|
|
54
63
|
end
|
|
55
64
|
|
|
56
|
-
it
|
|
65
|
+
it 'ignores stringy false scopes' do
|
|
57
66
|
s = Person.ransack('active' => 'false')
|
|
58
67
|
expect(s.result.to_sql).to_not (include 'active')
|
|
59
68
|
end
|
|
60
69
|
|
|
61
|
-
it
|
|
70
|
+
it 'passes values to scopes' do
|
|
62
71
|
s = Person.ransack('over_age' => 18)
|
|
63
72
|
expect(s.result.to_sql).to (include 'age > 18')
|
|
64
73
|
end
|
|
65
74
|
|
|
66
|
-
it
|
|
75
|
+
it 'chains scopes' do
|
|
67
76
|
s = Person.ransack('over_age' => 18, 'active' => true)
|
|
68
77
|
expect(s.result.to_sql).to (include 'age > 18')
|
|
69
78
|
expect(s.result.to_sql).to (include 'active = 1')
|
|
70
79
|
end
|
|
80
|
+
|
|
81
|
+
context "with sanitize_custom_scope_booleans set to false" do
|
|
82
|
+
before(:all) do
|
|
83
|
+
Ransack.configure { |c| c.sanitize_custom_scope_booleans = false }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
after(:all) do
|
|
87
|
+
Ransack.configure { |c| c.sanitize_custom_scope_booleans = true }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'passes true values to scopes' do
|
|
91
|
+
s = Person.ransack('over_age' => 1)
|
|
92
|
+
expect(s.result.to_sql).to (include 'age > 1')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'passes false values to scopes' do
|
|
96
|
+
s = Person.ransack('over_age' => 0)
|
|
97
|
+
expect(s.result.to_sql).to (include 'age > 0')
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context "with ransackable_scopes_skip_sanitize_args enabled for scope" do
|
|
102
|
+
before do
|
|
103
|
+
allow(Person)
|
|
104
|
+
.to receive(:ransackable_scopes_skip_sanitize_args)
|
|
105
|
+
.and_return([:over_age])
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'passes true values to scopes' do
|
|
109
|
+
s = Person.ransack('over_age' => 1)
|
|
110
|
+
expect(s.result.to_sql).to (include 'age > 1')
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'passes false values to scopes' do
|
|
114
|
+
s = Person.ransack('over_age' => 0)
|
|
115
|
+
expect(s.result.to_sql).to (include 'age > 0')
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
71
119
|
end
|
|
72
120
|
|
|
73
121
|
it 'does not raise exception for string :params argument' do
|
|
74
122
|
expect { Person.ransack('') }.to_not raise_error
|
|
75
123
|
end
|
|
76
124
|
|
|
125
|
+
it 'raises exception if ransack! called with unknown condition' do
|
|
126
|
+
expect { Person.ransack!(unknown_attr_eq: 'Ernie') }.to raise_error
|
|
127
|
+
end
|
|
128
|
+
|
|
77
129
|
it 'does not modify the parameters' do
|
|
78
|
-
params = { :
|
|
130
|
+
params = { name_eq: '' }
|
|
79
131
|
expect { Person.ransack(params) }.not_to change { params }
|
|
80
132
|
end
|
|
81
133
|
end
|
|
82
134
|
|
|
135
|
+
context 'negative conditions on HABTM associations' do
|
|
136
|
+
let(:medieval) { Tag.create!(name: 'Medieval') }
|
|
137
|
+
let(:fantasy) { Tag.create!(name: 'Fantasy') }
|
|
138
|
+
let(:arthur) { Article.create!(title: 'King Arthur') }
|
|
139
|
+
let(:marco) { Article.create!(title: 'Marco Polo') }
|
|
140
|
+
|
|
141
|
+
before do
|
|
142
|
+
marco.tags << medieval
|
|
143
|
+
arthur.tags << medieval
|
|
144
|
+
arthur.tags << fantasy
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'removes redundant joins from top query' do
|
|
148
|
+
s = Article.ransack(tags_name_not_eq: "Fantasy")
|
|
149
|
+
sql = s.result.to_sql
|
|
150
|
+
expect(sql).to_not include('LEFT OUTER JOIN')
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'handles != for single values' do
|
|
154
|
+
s = Article.ransack(tags_name_not_eq: "Fantasy")
|
|
155
|
+
articles = s.result.to_a
|
|
156
|
+
expect(articles).to include marco
|
|
157
|
+
expect(articles).to_not include arthur
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'handles NOT IN for multiple attributes' do
|
|
161
|
+
s = Article.ransack(tags_name_not_in: ["Fantasy", "Scifi"])
|
|
162
|
+
articles = s.result.to_a
|
|
163
|
+
|
|
164
|
+
expect(articles).to include marco
|
|
165
|
+
expect(articles).to_not include arthur
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
context 'negative conditions on self-referenced associations' do
|
|
170
|
+
let(:pop) { Person.create!(name: 'Grandpa') }
|
|
171
|
+
let(:dad) { Person.create!(name: 'Father') }
|
|
172
|
+
let(:mom) { Person.create!(name: 'Mother') }
|
|
173
|
+
let(:son) { Person.create!(name: 'Grandchild') }
|
|
174
|
+
|
|
175
|
+
before do
|
|
176
|
+
son.parent = dad
|
|
177
|
+
dad.parent = pop
|
|
178
|
+
dad.children << son
|
|
179
|
+
mom.children << son
|
|
180
|
+
pop.children << dad
|
|
181
|
+
son.save! && dad.save! && mom.save! && pop.save!
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'handles multiple associations and aliases' do
|
|
185
|
+
s = Person.ransack(
|
|
186
|
+
c: {
|
|
187
|
+
'0' => { a: ['name'], p: 'not_eq', v: ['Father'] },
|
|
188
|
+
'1' => {
|
|
189
|
+
a: ['children_name', 'parent_name'],
|
|
190
|
+
p: 'not_eq', v: ['Father'], m: 'or'
|
|
191
|
+
},
|
|
192
|
+
'2' => { a: ['children_salary'], p: 'eq', v: [nil] }
|
|
193
|
+
})
|
|
194
|
+
people = s.result
|
|
195
|
+
|
|
196
|
+
expect(people.to_a).to include son
|
|
197
|
+
expect(people.to_a).to include mom
|
|
198
|
+
expect(people.to_a).to_not include dad # rule '0': 'name'
|
|
199
|
+
expect(people.to_a).to_not include pop # rule '1': 'children_name'
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
describe '#ransack_alias' do
|
|
204
|
+
it 'translates an alias to the correct attributes' do
|
|
205
|
+
p = Person.create!(name: 'Meatloaf', email: 'babies@example.com')
|
|
206
|
+
|
|
207
|
+
s = Person.ransack(term_cont: 'atlo')
|
|
208
|
+
expect(s.result.to_a).to eq [p]
|
|
209
|
+
|
|
210
|
+
s = Person.ransack(term_cont: 'babi')
|
|
211
|
+
expect(s.result.to_a).to eq [p]
|
|
212
|
+
|
|
213
|
+
s = Person.ransack(term_cont: 'nomatch')
|
|
214
|
+
expect(s.result.to_a).to eq []
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
it 'also works with associations' do
|
|
218
|
+
dad = Person.create!(name: 'Birdman')
|
|
219
|
+
son = Person.create!(name: 'Weezy', parent: dad)
|
|
220
|
+
|
|
221
|
+
s = Person.ransack(daddy_eq: 'Birdman')
|
|
222
|
+
expect(s.result.to_a).to eq [son]
|
|
223
|
+
|
|
224
|
+
s = Person.ransack(daddy_eq: 'Drake')
|
|
225
|
+
expect(s.result.to_a).to eq []
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it 'makes aliases available to subclasses' do
|
|
229
|
+
yngwie = Musician.create!(name: 'Yngwie Malmsteen')
|
|
230
|
+
|
|
231
|
+
musicians = Musician.ransack(term_cont: 'ngw').result
|
|
232
|
+
expect(musicians).to eq([yngwie])
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'handles naming collisions gracefully' do
|
|
236
|
+
frank = Person.create!(name: 'Frank Stallone')
|
|
237
|
+
|
|
238
|
+
people = Person.ransack(term_cont: 'allon').result
|
|
239
|
+
expect(people).to eq([frank])
|
|
240
|
+
|
|
241
|
+
Class.new(Article) do
|
|
242
|
+
ransack_alias :term, :title
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
people = Person.ransack(term_cont: 'allon').result
|
|
246
|
+
expect(people).to eq([frank])
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
83
250
|
describe '#ransacker' do
|
|
84
251
|
# For infix tests
|
|
85
252
|
def self.sane_adapter?
|
|
86
253
|
case ::ActiveRecord::Base.connection.adapter_name
|
|
87
|
-
when
|
|
254
|
+
when 'SQLite3', 'PostgreSQL'
|
|
88
255
|
true
|
|
89
256
|
else
|
|
90
257
|
false
|
|
@@ -102,90 +269,128 @@ module Ransack
|
|
|
102
269
|
# end
|
|
103
270
|
|
|
104
271
|
it 'creates ransack attributes' do
|
|
105
|
-
|
|
272
|
+
person = Person.create!(name: 'Aric Smith')
|
|
273
|
+
|
|
274
|
+
s = Person.ransack(reversed_name_eq: 'htimS cirA')
|
|
106
275
|
expect(s.result.size).to eq(1)
|
|
107
276
|
|
|
108
|
-
expect(s.result.first).to eq
|
|
277
|
+
expect(s.result.first).to eq person
|
|
109
278
|
end
|
|
110
279
|
|
|
111
280
|
it 'can be accessed through associations' do
|
|
112
|
-
s = Person.ransack(:
|
|
281
|
+
s = Person.ransack(children_reversed_name_eq: 'htimS cirA')
|
|
113
282
|
expect(s.result.to_sql).to match(
|
|
114
283
|
/#{quote_table_name("children_people")}.#{
|
|
115
284
|
quote_column_name("name")} = 'Aric Smith'/
|
|
116
285
|
)
|
|
117
286
|
end
|
|
118
287
|
|
|
119
|
-
it 'allows an
|
|
120
|
-
s = Person.ransack(:
|
|
288
|
+
it 'allows an attribute to be an InfixOperation' do
|
|
289
|
+
s = Person.ransack(doubled_name_eq: 'Aric SmithAric Smith')
|
|
121
290
|
expect(s.result.first).to eq Person.where(name: 'Aric Smith').first
|
|
122
291
|
end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
|
|
123
292
|
|
|
124
|
-
it
|
|
125
|
-
s = Person.ransack(:
|
|
293
|
+
it 'does not break #count if using InfixOperations' do
|
|
294
|
+
s = Person.ransack(doubled_name_eq: 'Aric SmithAric Smith')
|
|
126
295
|
expect(s.result.count).to eq 1
|
|
127
296
|
end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
|
|
128
297
|
|
|
129
|
-
it
|
|
130
|
-
s = Person.ransack(:
|
|
298
|
+
it 'should remove empty key value pairs from the params hash' do
|
|
299
|
+
s = Person.ransack(children_reversed_name_eq: '')
|
|
131
300
|
expect(s.result.to_sql).not_to match /LEFT OUTER JOIN/
|
|
132
301
|
end
|
|
133
302
|
|
|
134
|
-
it
|
|
135
|
-
s = Person.ransack(:
|
|
303
|
+
it 'should keep proper key value pairs in the params hash' do
|
|
304
|
+
s = Person.ransack(children_reversed_name_eq: 'Testing')
|
|
136
305
|
expect(s.result.to_sql).to match /LEFT OUTER JOIN/
|
|
137
306
|
end
|
|
138
307
|
|
|
139
|
-
it
|
|
308
|
+
it 'should function correctly when nil is passed in' do
|
|
140
309
|
s = Person.ransack(nil)
|
|
141
310
|
end
|
|
142
311
|
|
|
143
|
-
it
|
|
312
|
+
it 'should function correctly when a blank string is passed in' do
|
|
144
313
|
s = Person.ransack('')
|
|
145
314
|
end
|
|
146
315
|
|
|
147
|
-
it
|
|
316
|
+
it 'should function correctly with a multi-parameter attribute' do
|
|
148
317
|
::ActiveRecord::Base.default_timezone = :utc
|
|
149
318
|
Time.zone = 'UTC'
|
|
150
319
|
|
|
151
320
|
date = Date.current
|
|
152
321
|
s = Person.ransack(
|
|
153
|
-
{
|
|
154
|
-
|
|
155
|
-
|
|
322
|
+
{ 'created_at_gteq(1i)' => date.year,
|
|
323
|
+
'created_at_gteq(2i)' => date.month,
|
|
324
|
+
'created_at_gteq(3i)' => date.day
|
|
156
325
|
}
|
|
157
326
|
)
|
|
158
327
|
expect(s.result.to_sql).to match />=/
|
|
159
328
|
expect(s.result.to_sql).to match date.to_s
|
|
160
329
|
end
|
|
161
330
|
|
|
162
|
-
it
|
|
163
|
-
s = Person.ransack(:
|
|
331
|
+
it 'should function correctly when using fields with dots in them' do
|
|
332
|
+
s = Person.ransack(email_cont: 'example.com')
|
|
164
333
|
expect(s.result.exists?).to be true
|
|
165
334
|
end
|
|
166
335
|
|
|
167
|
-
it
|
|
168
|
-
p = Person.create!(:
|
|
169
|
-
s = Person.ransack(:
|
|
336
|
+
it 'should function correctly when using fields with % in them' do
|
|
337
|
+
p = Person.create!(name: '110%-er')
|
|
338
|
+
s = Person.ransack(name_cont: '10%')
|
|
170
339
|
expect(s.result.to_a).to eq [p]
|
|
171
340
|
end
|
|
172
341
|
|
|
173
|
-
it
|
|
174
|
-
p = Person.create!(:
|
|
175
|
-
s = Person.ransack(:
|
|
342
|
+
it 'should function correctly when using fields with backslashes in them' do
|
|
343
|
+
p = Person.create!(name: "\\WINNER\\")
|
|
344
|
+
s = Person.ransack(name_cont: "\\WINNER\\")
|
|
176
345
|
expect(s.result.to_a).to eq [p]
|
|
177
346
|
end
|
|
178
347
|
|
|
179
|
-
context
|
|
180
|
-
|
|
181
|
-
|
|
348
|
+
context 'searching by underscores' do
|
|
349
|
+
# when escaping is supported right in LIKE expression without adding extra expressions
|
|
350
|
+
def self.simple_escaping?
|
|
351
|
+
case ::ActiveRecord::Base.connection.adapter_name
|
|
352
|
+
when 'Mysql2', 'PostgreSQL'
|
|
353
|
+
true
|
|
354
|
+
else
|
|
355
|
+
false
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
it 'should search correctly if matches exist' do
|
|
360
|
+
p = Person.create!(name: 'name_with_underscore')
|
|
361
|
+
s = Person.ransack(name_cont: 'name_')
|
|
362
|
+
expect(s.result.to_a).to eq [p]
|
|
363
|
+
end if simple_escaping?
|
|
364
|
+
|
|
365
|
+
it 'should return empty result if no matches' do
|
|
366
|
+
Person.create!(name: 'name_with_underscore')
|
|
367
|
+
s = Person.ransack(name_cont: 'n_')
|
|
368
|
+
expect(s.result.to_a).to eq []
|
|
369
|
+
end if simple_escaping?
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
context 'searching on an `in` predicate with a ransacker' do
|
|
373
|
+
it 'should function correctly when passing an array of ids' do
|
|
374
|
+
s = Person.ransack(array_people_ids_in: true)
|
|
182
375
|
expect(s.result.count).to be > 0
|
|
376
|
+
|
|
377
|
+
s = Person.ransack(array_where_people_ids_in: [1, '2', 3])
|
|
378
|
+
expect(s.result.count).to be 3
|
|
379
|
+
expect(s.result.map(&:id)).to eq [3, 2, 1]
|
|
183
380
|
end
|
|
184
381
|
|
|
185
|
-
it
|
|
186
|
-
Person.
|
|
187
|
-
|
|
382
|
+
it 'should function correctly when passing an array of strings' do
|
|
383
|
+
a, b = Person.select(:id).order(:id).limit(2).map { |a| a.id.to_s }
|
|
384
|
+
|
|
385
|
+
Person.create!(name: a)
|
|
386
|
+
s = Person.ransack(array_people_names_in: true)
|
|
188
387
|
expect(s.result.count).to be > 0
|
|
388
|
+
s = Person.ransack(array_where_people_names_in: a)
|
|
389
|
+
expect(s.result.count).to be 1
|
|
390
|
+
|
|
391
|
+
Person.create!(name: b)
|
|
392
|
+
s = Person.ransack(array_where_people_names_in: [a, b])
|
|
393
|
+
expect(s.result.count).to be 2
|
|
189
394
|
end
|
|
190
395
|
|
|
191
396
|
it 'should function correctly with an Arel SqlLiteral' do
|
|
@@ -196,49 +401,77 @@ module Ransack
|
|
|
196
401
|
end
|
|
197
402
|
end
|
|
198
403
|
|
|
199
|
-
context
|
|
200
|
-
it
|
|
404
|
+
context 'search on an `in` predicate with an array' do
|
|
405
|
+
it 'should function correctly when passing an array of ids' do
|
|
201
406
|
array = Person.all.map(&:id)
|
|
202
407
|
s = Person.ransack(id_in: array)
|
|
203
408
|
expect(s.result.count).to eq array.size
|
|
204
409
|
end
|
|
205
410
|
end
|
|
206
411
|
|
|
207
|
-
it
|
|
208
|
-
p = Person.create!(:
|
|
412
|
+
it 'should work correctly when an attribute name ends with _start' do
|
|
413
|
+
p = Person.create!(new_start: 'Bar and foo', name: 'Xiang')
|
|
209
414
|
|
|
210
|
-
s = Person.ransack(:
|
|
415
|
+
s = Person.ransack(new_start_end: ' and foo')
|
|
211
416
|
expect(s.result.to_a).to eq [p]
|
|
212
417
|
|
|
213
|
-
s = Person.ransack(:
|
|
418
|
+
s = Person.ransack(name_or_new_start_start: 'Xia')
|
|
214
419
|
expect(s.result.to_a).to eq [p]
|
|
215
420
|
|
|
216
|
-
s = Person.ransack(:
|
|
421
|
+
s = Person.ransack(new_start_or_name_end: 'iang')
|
|
217
422
|
expect(s.result.to_a).to eq [p]
|
|
218
423
|
end
|
|
219
424
|
|
|
220
|
-
it
|
|
221
|
-
p = Person.create!(:
|
|
425
|
+
it 'should work correctly when an attribute name ends with _end' do
|
|
426
|
+
p = Person.create!(stop_end: 'Foo and bar', name: 'Marianne')
|
|
222
427
|
|
|
223
|
-
s = Person.ransack(:
|
|
428
|
+
s = Person.ransack(stop_end_start: 'Foo and')
|
|
224
429
|
expect(s.result.to_a).to eq [p]
|
|
225
430
|
|
|
226
|
-
s = Person.ransack(:
|
|
431
|
+
s = Person.ransack(stop_end_or_name_end: 'anne')
|
|
227
432
|
expect(s.result.to_a).to eq [p]
|
|
228
433
|
|
|
229
|
-
s = Person.ransack(:
|
|
434
|
+
s = Person.ransack(name_or_stop_end_end: ' bar')
|
|
230
435
|
expect(s.result.to_a).to eq [p]
|
|
231
436
|
end
|
|
232
437
|
|
|
233
|
-
it
|
|
234
|
-
p = Person.create!(:
|
|
235
|
-
s = Person.ransack(:
|
|
438
|
+
it 'should work correctly when an attribute name has `and` in it' do
|
|
439
|
+
p = Person.create!(terms_and_conditions: true)
|
|
440
|
+
s = Person.ransack(terms_and_conditions_eq: true)
|
|
236
441
|
expect(s.result.to_a).to eq [p]
|
|
237
442
|
end
|
|
238
443
|
|
|
239
|
-
|
|
444
|
+
context 'attribute aliased column names',
|
|
445
|
+
if: Ransack::SUPPORTS_ATTRIBUTE_ALIAS do
|
|
446
|
+
it 'should be translated to original column name' do
|
|
447
|
+
s = Person.ransack(full_name_eq: 'Nicolas Cage')
|
|
448
|
+
expect(s.result.to_sql).to match(
|
|
449
|
+
/WHERE #{quote_table_name("people")}.#{quote_column_name("name")}/
|
|
450
|
+
)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
it 'should translate on associations' do
|
|
454
|
+
s = Person.ransack(articles_content_cont: 'Nicolas Cage')
|
|
455
|
+
expect(s.result.to_sql).to match(
|
|
456
|
+
/#{quote_table_name("articles")}.#{
|
|
457
|
+
quote_column_name("body")} I?LIKE '%Nicolas Cage%'/
|
|
458
|
+
)
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
it 'sorts with different join variants' do
|
|
463
|
+
comments = [
|
|
464
|
+
Comment.create(article: Article.create(title: 'Avenger'), person: Person.create(salary: 100_000)),
|
|
465
|
+
Comment.create(article: Article.create(title: 'Avenge'), person: Person.create(salary: 50_000)),
|
|
466
|
+
]
|
|
467
|
+
expect(Comment.ransack(article_title_cont: 'aven',s: 'person_salary desc').result).to eq(comments)
|
|
468
|
+
expect(Comment.joins(:person).ransack(s: 'persons_salarydesc', article_title_cont: 'aven').result).to eq(comments)
|
|
469
|
+
expect(Comment.joins(:person).ransack(article_title_cont: 'aven',s: 'persons_salary desc').result).to eq(comments)
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
it 'allows sort by `only_sort` field' do
|
|
240
473
|
s = Person.ransack(
|
|
241
|
-
|
|
474
|
+
's' => { '0' => { 'dir' => 'asc', 'name' => 'only_sort' } }
|
|
242
475
|
)
|
|
243
476
|
expect(s.result.to_sql).to match(
|
|
244
477
|
/ORDER BY #{quote_table_name("people")}.#{
|
|
@@ -246,9 +479,9 @@ module Ransack
|
|
|
246
479
|
)
|
|
247
480
|
end
|
|
248
481
|
|
|
249
|
-
it
|
|
482
|
+
it 'does not sort by `only_search` field' do
|
|
250
483
|
s = Person.ransack(
|
|
251
|
-
|
|
484
|
+
's' => { '0' => { 'dir' => 'asc', 'name' => 'only_search' } }
|
|
252
485
|
)
|
|
253
486
|
expect(s.result.to_sql).not_to match(
|
|
254
487
|
/ORDER BY #{quote_table_name("people")}.#{
|
|
@@ -256,25 +489,25 @@ module Ransack
|
|
|
256
489
|
)
|
|
257
490
|
end
|
|
258
491
|
|
|
259
|
-
it 'allows search by
|
|
260
|
-
s = Person.ransack(:
|
|
492
|
+
it 'allows search by `only_search` field' do
|
|
493
|
+
s = Person.ransack(only_search_eq: 'htimS cirA')
|
|
261
494
|
expect(s.result.to_sql).to match(
|
|
262
495
|
/WHERE #{quote_table_name("people")}.#{
|
|
263
496
|
quote_column_name("only_search")} = 'htimS cirA'/
|
|
264
497
|
)
|
|
265
498
|
end
|
|
266
499
|
|
|
267
|
-
it
|
|
268
|
-
s = Person.ransack(:
|
|
500
|
+
it 'cannot be searched by `only_sort`' do
|
|
501
|
+
s = Person.ransack(only_sort_eq: 'htimS cirA')
|
|
269
502
|
expect(s.result.to_sql).not_to match(
|
|
270
503
|
/WHERE #{quote_table_name("people")}.#{
|
|
271
504
|
quote_column_name("only_sort")} = 'htimS cirA'/
|
|
272
505
|
)
|
|
273
506
|
end
|
|
274
507
|
|
|
275
|
-
it 'allows sort by
|
|
508
|
+
it 'allows sort by `only_admin` field, if auth_object: :admin' do
|
|
276
509
|
s = Person.ransack(
|
|
277
|
-
{
|
|
510
|
+
{ 's' => { '0' => { 'dir' => 'asc', 'name' => 'only_admin' } } },
|
|
278
511
|
{ auth_object: :admin }
|
|
279
512
|
)
|
|
280
513
|
expect(s.result.to_sql).to match(
|
|
@@ -283,9 +516,9 @@ module Ransack
|
|
|
283
516
|
)
|
|
284
517
|
end
|
|
285
518
|
|
|
286
|
-
it
|
|
519
|
+
it 'does not sort by `only_admin` field, if auth_object: nil' do
|
|
287
520
|
s = Person.ransack(
|
|
288
|
-
|
|
521
|
+
's' => { '0' => { 'dir' => 'asc', 'name' => 'only_admin' } }
|
|
289
522
|
)
|
|
290
523
|
expect(s.result.to_sql).not_to match(
|
|
291
524
|
/ORDER BY #{quote_table_name("people")}.#{
|
|
@@ -293,10 +526,10 @@ module Ransack
|
|
|
293
526
|
)
|
|
294
527
|
end
|
|
295
528
|
|
|
296
|
-
it 'allows search by
|
|
529
|
+
it 'allows search by `only_admin` field, if auth_object: :admin' do
|
|
297
530
|
s = Person.ransack(
|
|
298
|
-
{ :
|
|
299
|
-
{ :
|
|
531
|
+
{ only_admin_eq: 'htimS cirA' },
|
|
532
|
+
{ auth_object: :admin }
|
|
300
533
|
)
|
|
301
534
|
expect(s.result.to_sql).to match(
|
|
302
535
|
/WHERE #{quote_table_name("people")}.#{
|
|
@@ -304,14 +537,15 @@ module Ransack
|
|
|
304
537
|
)
|
|
305
538
|
end
|
|
306
539
|
|
|
307
|
-
it
|
|
308
|
-
s = Person.ransack(:
|
|
540
|
+
it 'cannot be searched by `only_admin`, if auth_object: nil' do
|
|
541
|
+
s = Person.ransack(only_admin_eq: 'htimS cirA')
|
|
309
542
|
expect(s.result.to_sql).not_to match(
|
|
310
543
|
/WHERE #{quote_table_name("people")}.#{
|
|
311
544
|
quote_column_name("only_admin")} = 'htimS cirA'/
|
|
312
545
|
)
|
|
313
546
|
end
|
|
314
547
|
|
|
548
|
+
|
|
315
549
|
it 'should allow passing ransacker arguments to a ransacker' do
|
|
316
550
|
s = Person.ransack(
|
|
317
551
|
c: [{
|
|
@@ -332,6 +566,56 @@ module Ransack
|
|
|
332
566
|
)
|
|
333
567
|
expect { s.result.first }.to_not raise_error
|
|
334
568
|
end
|
|
569
|
+
|
|
570
|
+
it 'should allow sort passing arguments to a ransacker' do
|
|
571
|
+
s = Person.ransack(
|
|
572
|
+
s: {
|
|
573
|
+
'0' => {
|
|
574
|
+
name: 'with_arguments', dir: 'desc', ransacker_args: [2, 6]
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
)
|
|
578
|
+
expect(s.result.to_sql).to match(
|
|
579
|
+
/ORDER BY \(SELECT MAX\(articles.title\) FROM articles/
|
|
580
|
+
)
|
|
581
|
+
expect(s.result.to_sql).to match(
|
|
582
|
+
/WHERE articles.person_id = people.id AND LENGTH\(articles.body\)/
|
|
583
|
+
)
|
|
584
|
+
expect(s.result.to_sql).to match(
|
|
585
|
+
/BETWEEN 2 AND 6 GROUP BY articles.person_id \) DESC/
|
|
586
|
+
)
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
context 'case insensitive sorting' do
|
|
590
|
+
it 'allows sort by desc' do
|
|
591
|
+
search = Person.ransack(sorts: ['name_case_insensitive desc'])
|
|
592
|
+
expect(search.result.to_sql).to match /ORDER BY LOWER(.*) DESC/
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
it 'allows sort by asc' do
|
|
596
|
+
search = Person.ransack(sorts: ['name_case_insensitive asc'])
|
|
597
|
+
expect(search.result.to_sql).to match /ORDER BY LOWER(.*) ASC/
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
context 'regular sorting' do
|
|
602
|
+
it 'allows sort by desc' do
|
|
603
|
+
search = Person.ransack(sorts: ['name desc'])
|
|
604
|
+
expect(search.result.to_sql).to match /ORDER BY .* DESC/
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
it 'allows sort by asc' do
|
|
608
|
+
search = Person.ransack(sorts: ['name asc'])
|
|
609
|
+
expect(search.result.to_sql).to match /ORDER BY .* ASC/
|
|
610
|
+
end
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
context 'sorting by a scope' do
|
|
614
|
+
it 'applies the correct scope' do
|
|
615
|
+
search = Person.ransack(sorts: ['reverse_name asc'])
|
|
616
|
+
expect(search.result.to_sql).to include("ORDER BY REVERSE(name) ASC")
|
|
617
|
+
end
|
|
618
|
+
end
|
|
335
619
|
end
|
|
336
620
|
|
|
337
621
|
describe '#ransackable_attributes' do
|
|
@@ -341,9 +625,14 @@ module Ransack
|
|
|
341
625
|
it { should include 'name' }
|
|
342
626
|
it { should include 'reversed_name' }
|
|
343
627
|
it { should include 'doubled_name' }
|
|
628
|
+
it { should include 'term' }
|
|
344
629
|
it { should include 'only_search' }
|
|
345
630
|
it { should_not include 'only_sort' }
|
|
346
631
|
it { should_not include 'only_admin' }
|
|
632
|
+
|
|
633
|
+
if Ransack::SUPPORTS_ATTRIBUTE_ALIAS
|
|
634
|
+
it { should include 'full_name' }
|
|
635
|
+
end
|
|
347
636
|
end
|
|
348
637
|
|
|
349
638
|
context 'with auth_object :admin' do
|
|
@@ -396,6 +685,12 @@ module Ransack
|
|
|
396
685
|
it { should eq [] }
|
|
397
686
|
end
|
|
398
687
|
|
|
688
|
+
describe '#ransackable_scopes_skip_sanitize_args' do
|
|
689
|
+
subject { Person.ransackable_scopes_skip_sanitize_args }
|
|
690
|
+
|
|
691
|
+
it { should eq [] }
|
|
692
|
+
end
|
|
693
|
+
|
|
399
694
|
end
|
|
400
695
|
end
|
|
401
696
|
end
|