ransack 1.4.1 → 1.5.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/CHANGELOG.md +75 -0
- data/CONTRIBUTING.md +7 -7
- data/Gemfile +7 -0
- data/README.md +139 -34
- data/lib/ransack/adapters/active_record/3.0/compat.rb +5 -5
- data/lib/ransack/adapters/active_record/3.0/context.rb +32 -16
- data/lib/ransack/adapters/active_record/3.1/context.rb +31 -16
- data/lib/ransack/adapters/active_record/context.rb +34 -26
- data/lib/ransack/configuration.rb +1 -1
- data/lib/ransack/constants.rb +72 -37
- data/lib/ransack/context.rb +18 -12
- data/lib/ransack/helpers/form_builder.rb +32 -15
- data/lib/ransack/helpers/form_helper.rb +83 -55
- data/lib/ransack/naming.rb +1 -1
- data/lib/ransack/nodes/condition.rb +9 -9
- data/lib/ransack/nodes/grouping.rb +15 -9
- data/lib/ransack/nodes/sort.rb +9 -4
- data/lib/ransack/predicate.rb +10 -10
- data/lib/ransack/search.rb +15 -8
- data/lib/ransack/translate.rb +9 -6
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +8 -2
- data/spec/ransack/adapters/active_record/base_spec.rb +2 -2
- data/spec/ransack/helpers/form_builder_spec.rb +63 -43
- data/spec/ransack/helpers/form_helper_spec.rb +163 -2
- data/spec/ransack/nodes/grouping_spec.rb +44 -1
- data/spec/ransack/predicate_spec.rb +165 -3
- data/spec/ransack/translate_spec.rb +4 -1
- data/spec/support/en.yml +5 -0
- data/spec/support/schema.rb +6 -0
- metadata +3 -3
@@ -51,8 +51,8 @@ module Ransack
|
|
51
51
|
end
|
52
52
|
|
53
53
|
describe '#sort_link with default search_key defined as symbol' do
|
54
|
-
subject { @controller.
|
55
|
-
|
54
|
+
subject { @controller.view_context
|
55
|
+
.sort_link(
|
56
56
|
Person.search(
|
57
57
|
{ :sorts => ['name desc'] }, :search_key => :people_search
|
58
58
|
),
|
@@ -71,6 +71,46 @@ module Ransack
|
|
71
71
|
}
|
72
72
|
end
|
73
73
|
|
74
|
+
describe '#sort_link desc through association table defined as a symbol' do
|
75
|
+
subject { @controller.view_context
|
76
|
+
.sort_link(
|
77
|
+
Person.search({ :sorts => 'comments_body asc' }),
|
78
|
+
:comments_body, :controller => 'people'
|
79
|
+
)
|
80
|
+
}
|
81
|
+
it {
|
82
|
+
should match(
|
83
|
+
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
84
|
+
/people\?q%5Bs%5D=comments.body\+desc/
|
85
|
+
else
|
86
|
+
/people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/
|
87
|
+
end
|
88
|
+
)
|
89
|
+
}
|
90
|
+
it { should match /sort_link asc/ }
|
91
|
+
it { should match /Body ▲/ }
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#sort_link through association table defined as a string' do
|
95
|
+
subject { @controller.view_context
|
96
|
+
.sort_link(
|
97
|
+
Person.search({ :sorts => 'comments.body desc' }),
|
98
|
+
'comments.body', :controller => 'people'
|
99
|
+
)
|
100
|
+
}
|
101
|
+
it {
|
102
|
+
should match(
|
103
|
+
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
104
|
+
/people\?q%5Bs%5D=comments.body\+asc/
|
105
|
+
else
|
106
|
+
/people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/
|
107
|
+
end
|
108
|
+
)
|
109
|
+
}
|
110
|
+
it { should match /sort_link desc/ }
|
111
|
+
it { should match /Comments.body ▼/ }
|
112
|
+
end
|
113
|
+
|
74
114
|
describe '#sort_link works even if search params are a blank string' do
|
75
115
|
before { @controller.view_context.params[:q] = '' }
|
76
116
|
specify {
|
@@ -105,6 +145,127 @@ module Ransack
|
|
105
145
|
}
|
106
146
|
end
|
107
147
|
|
148
|
+
describe '#sort_link with multiple search_keys defined as an array' do
|
149
|
+
subject { @controller.view_context
|
150
|
+
.sort_link(
|
151
|
+
[:main_app, Person.search(:sorts => ['name desc', 'email asc'])],
|
152
|
+
:name, [:name, 'email DESC'],
|
153
|
+
:controller => 'people'
|
154
|
+
)
|
155
|
+
}
|
156
|
+
it {
|
157
|
+
should match(
|
158
|
+
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
|
159
|
+
)
|
160
|
+
}
|
161
|
+
it {
|
162
|
+
should match /sort_link desc/
|
163
|
+
}
|
164
|
+
it {
|
165
|
+
should match /Full Name ▼/
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#sort_link with multiple search_keys should allow a label to be specified' do
|
170
|
+
subject { @controller.view_context
|
171
|
+
.sort_link(
|
172
|
+
[:main_app, Person.search(:sorts => ['name desc', 'email asc'])],
|
173
|
+
:name, [:name, 'email DESC'],
|
174
|
+
'Property Name',
|
175
|
+
:controller => 'people'
|
176
|
+
)
|
177
|
+
}
|
178
|
+
it {
|
179
|
+
should match /Property Name ▼/
|
180
|
+
}
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#sort_link with multiple search_keys should flip multiple fields specified without a direction' do
|
184
|
+
subject { @controller.view_context
|
185
|
+
.sort_link(
|
186
|
+
[:main_app, Person.search(:sorts => ['name desc', 'email asc'])],
|
187
|
+
:name, [:name, :email],
|
188
|
+
:controller => 'people'
|
189
|
+
)
|
190
|
+
}
|
191
|
+
it {
|
192
|
+
should match(
|
193
|
+
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
|
194
|
+
)
|
195
|
+
}
|
196
|
+
it {
|
197
|
+
should match /sort_link desc/
|
198
|
+
}
|
199
|
+
it {
|
200
|
+
should match /Full Name ▼/
|
201
|
+
}
|
202
|
+
end
|
203
|
+
|
204
|
+
describe '#sort_link with multiple search_keys should allow a default_order to be specified' do
|
205
|
+
subject { @controller.view_context
|
206
|
+
.sort_link(
|
207
|
+
[:main_app, Person.search()],
|
208
|
+
:name, [:name, :email],
|
209
|
+
:controller => 'people',
|
210
|
+
:default_order => 'desc'
|
211
|
+
)
|
212
|
+
}
|
213
|
+
it {
|
214
|
+
should match(
|
215
|
+
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
|
216
|
+
)
|
217
|
+
}
|
218
|
+
it {
|
219
|
+
should match /sort_link/
|
220
|
+
}
|
221
|
+
it {
|
222
|
+
should match /Full Name/
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
describe '#sort_link with multiple search_keys should allow multiple default_orders to be specified' do
|
227
|
+
subject { @controller.view_context
|
228
|
+
.sort_link(
|
229
|
+
[:main_app, Person.search()],
|
230
|
+
:name, [:name, :email],
|
231
|
+
:controller => 'people',
|
232
|
+
:default_order => { 'name' => 'desc', :email => 'asc' }
|
233
|
+
)
|
234
|
+
}
|
235
|
+
it {
|
236
|
+
should match(
|
237
|
+
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/
|
238
|
+
)
|
239
|
+
}
|
240
|
+
it {
|
241
|
+
should match /sort_link/
|
242
|
+
}
|
243
|
+
it {
|
244
|
+
should match /Full Name/
|
245
|
+
}
|
246
|
+
end
|
247
|
+
|
248
|
+
describe '#sort_link with multiple search_keys with multiple default_orders should not override a specified order' do
|
249
|
+
subject { @controller.view_context
|
250
|
+
.sort_link(
|
251
|
+
[:main_app, Person.search()],
|
252
|
+
:name, [:name, 'email desc'],
|
253
|
+
:controller => 'people',
|
254
|
+
:default_order => { 'name' => 'desc', :email => 'asc' }
|
255
|
+
)
|
256
|
+
}
|
257
|
+
it {
|
258
|
+
should match(
|
259
|
+
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
|
260
|
+
)
|
261
|
+
}
|
262
|
+
it {
|
263
|
+
should match /sort_link/
|
264
|
+
}
|
265
|
+
it {
|
266
|
+
should match /Full Name/
|
267
|
+
}
|
268
|
+
end
|
108
269
|
context 'view has existing parameters' do
|
109
270
|
before do
|
110
271
|
@controller.view_context.params.merge!({ :exist => 'existing' })
|
@@ -2,12 +2,55 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Ransack
|
4
4
|
module Nodes
|
5
|
+
|
5
6
|
describe Grouping do
|
7
|
+
|
6
8
|
before do
|
7
9
|
@g = 1
|
8
10
|
end
|
9
11
|
|
12
|
+
let(:context) { Context.for(Person) }
|
13
|
+
|
14
|
+
subject { described_class.new(context) }
|
15
|
+
|
16
|
+
describe '#attribute_method?' do
|
17
|
+
context 'for attributes of the context' do
|
18
|
+
it 'is true' do
|
19
|
+
expect(subject.attribute_method?('name')).to be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when the attribute contains '_and_'" do
|
23
|
+
it 'is true' do
|
24
|
+
expect(subject.attribute_method?('terms_and_conditions')).to be_true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when the attribute contains '_or_'" do
|
29
|
+
it 'is true' do
|
30
|
+
expect(subject.attribute_method?('true_or_false')).to be_true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when the attribute ends with '_start'" do
|
35
|
+
it 'is true' do
|
36
|
+
expect(subject.attribute_method?('life_start')).to be_true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "when the attribute ends with '_end'" do
|
41
|
+
it 'is true' do
|
42
|
+
expect(subject.attribute_method?('stop_end')).to be_true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'for unknown attributes' do
|
48
|
+
it 'is false' do
|
49
|
+
expect(subject.attribute_method?('not_an_attribute')).to be_false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
10
53
|
|
11
54
|
end
|
12
55
|
end
|
13
|
-
end
|
56
|
+
end
|
@@ -22,8 +22,7 @@ module Ransack
|
|
22
22
|
describe 'eq' do
|
23
23
|
it 'generates an equality condition for boolean true' do
|
24
24
|
@s.awesome_eq = true
|
25
|
-
field = "#{quote_table_name("people")}.#{
|
26
|
-
quote_column_name("awesome")}"
|
25
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("awesome")}"
|
27
26
|
expect(@s.result.to_sql).to match /#{field} = #{
|
28
27
|
ActiveRecord::Base.connection.quoted_true}/
|
29
28
|
end
|
@@ -41,8 +40,91 @@ module Ransack
|
|
41
40
|
end
|
42
41
|
end
|
43
42
|
|
44
|
-
describe '
|
43
|
+
describe 'lteq' do
|
44
|
+
it 'generates a <= condition with an integer column' do
|
45
|
+
val = 1000
|
46
|
+
@s.salary_lteq = val
|
47
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
48
|
+
expect(@s.result.to_sql).to match /#{field} <= #{val}/
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'generates a <= condition with a string column' do
|
52
|
+
val = 'jane@doe.com'
|
53
|
+
@s.email_lteq = val
|
54
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("email")}"
|
55
|
+
expect(@s.result.to_sql).to match /#{field} <= '#{val}'/
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'does not generate a condition for nil' do
|
59
|
+
@s.salary_lteq = nil
|
60
|
+
expect(@s.result.to_sql).not_to match /WHERE/
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'lt' do
|
65
|
+
it 'generates a < condition with an integer column' do
|
66
|
+
val = 2000
|
67
|
+
@s.salary_lt = val
|
68
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
69
|
+
expect(@s.result.to_sql).to match /#{field} < #{val}/
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'generates a < condition with a string column' do
|
73
|
+
val = 'jane@doe.com'
|
74
|
+
@s.email_lt = val
|
75
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("email")}"
|
76
|
+
expect(@s.result.to_sql).to match /#{field} < '#{val}'/
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'does not generate a condition for nil' do
|
80
|
+
@s.salary_lt = nil
|
81
|
+
expect(@s.result.to_sql).not_to match /WHERE/
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'gteq' do
|
86
|
+
it 'generates a >= condition with an integer column' do
|
87
|
+
val = 300
|
88
|
+
@s.salary_gteq = val
|
89
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
90
|
+
expect(@s.result.to_sql).to match /#{field} >= #{val}/
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'generates a >= condition with a string column' do
|
94
|
+
val = 'jane@doe.com'
|
95
|
+
@s.email_gteq = val
|
96
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("email")}"
|
97
|
+
expect(@s.result.to_sql).to match /#{field} >= '#{val}'/
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'does not generate a condition for nil' do
|
101
|
+
@s.salary_gteq = nil
|
102
|
+
expect(@s.result.to_sql).not_to match /WHERE/
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe 'gt' do
|
107
|
+
it 'generates a > condition with an integer column' do
|
108
|
+
val = 400
|
109
|
+
@s.salary_gt = val
|
110
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("salary")}"
|
111
|
+
expect(@s.result.to_sql).to match /#{field} > #{val}/
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'generates a > condition with a string column' do
|
115
|
+
val = 'jane@doe.com'
|
116
|
+
@s.email_gt = val
|
117
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("email")}"
|
118
|
+
expect(@s.result.to_sql).to match /#{field} > '#{val}'/
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'does not generate a condition for nil' do
|
122
|
+
@s.salary_gt = nil
|
123
|
+
expect(@s.result.to_sql).not_to match /WHERE/
|
124
|
+
end
|
125
|
+
end
|
45
126
|
|
127
|
+
describe 'cont' do
|
46
128
|
it_has_behavior 'wildcard escaping', :name_cont,
|
47
129
|
(if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
48
130
|
/"people"."name" ILIKE '%\\%\\._\\\\%'/
|
@@ -80,6 +162,86 @@ module Ransack
|
|
80
162
|
end
|
81
163
|
end
|
82
164
|
|
165
|
+
describe 'start' do
|
166
|
+
it 'generates a LIKE query with value followed by %' do
|
167
|
+
@s.name_start = 'Er'
|
168
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
169
|
+
expect(@s.result.to_sql).to match /#{field} I?LIKE 'Er%'/
|
170
|
+
end
|
171
|
+
|
172
|
+
it "works with attribute names ending with '_start'" do
|
173
|
+
@s.new_start_start = 'hEy'
|
174
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("new_start")}"
|
175
|
+
expect(@s.result.to_sql).to match /#{field} I?LIKE 'hEy%'/
|
176
|
+
end
|
177
|
+
|
178
|
+
it "works with attribute names ending with '_end'" do
|
179
|
+
@s.stop_end_start = 'begin'
|
180
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("stop_end")}"
|
181
|
+
expect(@s.result.to_sql).to match /#{field} I?LIKE 'begin%'/
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe 'not_start' do
|
186
|
+
it 'generates a NOT LIKE query with value followed by %' do
|
187
|
+
@s.name_not_start = 'Eri'
|
188
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
189
|
+
expect(@s.result.to_sql).to match /#{field} NOT I?LIKE 'Eri%'/
|
190
|
+
end
|
191
|
+
|
192
|
+
it "works with attribute names ending with '_start'" do
|
193
|
+
@s.new_start_not_start = 'hEy'
|
194
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("new_start")}"
|
195
|
+
expect(@s.result.to_sql).to match /#{field} NOT I?LIKE 'hEy%'/
|
196
|
+
end
|
197
|
+
|
198
|
+
it "works with attribute names ending with '_end'" do
|
199
|
+
@s.stop_end_not_start = 'begin'
|
200
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("stop_end")}"
|
201
|
+
expect(@s.result.to_sql).to match /#{field} NOT I?LIKE 'begin%'/
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe 'end' do
|
206
|
+
it 'generates a LIKE query with value preceded by %' do
|
207
|
+
@s.name_end = 'Miller'
|
208
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
209
|
+
expect(@s.result.to_sql).to match /#{field} I?LIKE '%Miller'/
|
210
|
+
end
|
211
|
+
|
212
|
+
it "works with attribute names ending with '_start'" do
|
213
|
+
@s.new_start_end = 'finish'
|
214
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("new_start")}"
|
215
|
+
expect(@s.result.to_sql).to match /#{field} I?LIKE '%finish'/
|
216
|
+
end
|
217
|
+
|
218
|
+
it "works with attribute names ending with '_end'" do
|
219
|
+
@s.stop_end_end = 'Ending'
|
220
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("stop_end")}"
|
221
|
+
expect(@s.result.to_sql).to match /#{field} I?LIKE '%Ending'/
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe 'not_end' do
|
226
|
+
it 'generates a NOT LIKE query with value preceded by %' do
|
227
|
+
@s.name_not_end = 'Miller'
|
228
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
229
|
+
expect(@s.result.to_sql).to match /#{field} NOT I?LIKE '%Miller'/
|
230
|
+
end
|
231
|
+
|
232
|
+
it "works with attribute names ending with '_start'" do
|
233
|
+
@s.new_start_not_end = 'finish'
|
234
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("new_start")}"
|
235
|
+
expect(@s.result.to_sql).to match /#{field} NOT I?LIKE '%finish'/
|
236
|
+
end
|
237
|
+
|
238
|
+
it "works with attribute names ending with '_end'" do
|
239
|
+
@s.stop_end_not_end = 'Ending'
|
240
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("stop_end")}"
|
241
|
+
expect(@s.result.to_sql).to match /#{field} NOT I?LIKE '%Ending'/
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
83
245
|
describe 'true' do
|
84
246
|
it 'generates an equality condition for boolean true' do
|
85
247
|
@s.awesome_true = true
|
@@ -6,7 +6,10 @@ module Ransack
|
|
6
6
|
describe '.attribute' do
|
7
7
|
it 'translate namespaced attribute like AR does' do
|
8
8
|
ar_translation = ::Namespace::Article.human_attribute_name(:title)
|
9
|
-
ransack_translation = Ransack::Translate.attribute(
|
9
|
+
ransack_translation = Ransack::Translate.attribute(
|
10
|
+
:title,
|
11
|
+
:context => ::Namespace::Article.search.context
|
12
|
+
)
|
10
13
|
expect(ransack_translation).to eq ar_translation
|
11
14
|
end
|
12
15
|
end
|