pursuit 0.4.5 → 1.0.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/rubygem.yaml +46 -0
- data/Gemfile +14 -13
- data/Gemfile.lock +14 -13
- data/README.md +210 -27
- data/bin/console +10 -0
- data/lib/pursuit/aggregate_modifier_not_found.rb +20 -0
- data/lib/pursuit/aggregate_modifier_required.rb +20 -0
- data/lib/pursuit/aggregate_modifiers_not_available.rb +13 -0
- data/lib/pursuit/attribute_not_found.rb +20 -0
- data/lib/pursuit/constants.rb +1 -1
- data/lib/pursuit/error.rb +7 -0
- data/lib/pursuit/predicate_parser.rb +181 -0
- data/lib/pursuit/predicate_search.rb +83 -0
- data/lib/pursuit/predicate_transform.rb +231 -0
- data/lib/pursuit/query_error.rb +7 -0
- data/lib/pursuit/simple_search.rb +64 -0
- data/lib/pursuit/term_parser.rb +44 -0
- data/lib/pursuit/term_search.rb +69 -0
- data/lib/pursuit/term_transform.rb +35 -0
- data/lib/pursuit.rb +18 -4
- data/pursuit.gemspec +4 -3
- data/spec/internal/app/models/application_record.rb +5 -0
- data/spec/internal/app/models/product.rb +25 -9
- data/spec/internal/app/models/product_category.rb +23 -1
- data/spec/internal/app/models/product_variation.rb +26 -1
- data/spec/lib/pursuit/predicate_parser_spec.rb +1604 -0
- data/spec/lib/pursuit/predicate_search_spec.rb +80 -0
- data/spec/lib/pursuit/predicate_transform_spec.rb +624 -0
- data/spec/lib/pursuit/simple_search_spec.rb +59 -0
- data/spec/lib/pursuit/term_parser_spec.rb +271 -0
- data/spec/lib/pursuit/term_search_spec.rb +71 -0
- data/spec/lib/pursuit/term_transform_spec.rb +105 -0
- metadata +47 -25
- data/.travis.yml +0 -26
- data/lib/pursuit/dsl.rb +0 -28
- data/lib/pursuit/railtie.rb +0 -13
- data/lib/pursuit/search.rb +0 -172
- data/lib/pursuit/search_options.rb +0 -86
- data/lib/pursuit/search_term_parser.rb +0 -46
- data/spec/lib/pursuit/dsl_spec.rb +0 -22
- data/spec/lib/pursuit/search_options_spec.rb +0 -146
- data/spec/lib/pursuit/search_spec.rb +0 -516
- data/spec/lib/pursuit/search_term_parser_spec.rb +0 -34
- data/travis/gemfiles/5.2.gemfile +0 -8
- data/travis/gemfiles/6.0.gemfile +0 -8
- data/travis/gemfiles/6.1.gemfile +0 -8
- data/travis/gemfiles/7.0.gemfile +0 -8
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Pursuit::PredicateSearch do
|
4
|
+
subject(:predicate_search) do
|
5
|
+
described_class.new(
|
6
|
+
Product.left_outer_joins(:variations).group(:id),
|
7
|
+
permit_aggregate_modifiers: true
|
8
|
+
) do
|
9
|
+
permit_attribute :title
|
10
|
+
permit_attribute :variation, ProductVariation.arel_table[:id]
|
11
|
+
permit_attribute :variation_title, ProductVariation.arel_table[:title]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#initialize' do
|
16
|
+
it 'is expected to set #relation eq `relation`' do
|
17
|
+
expect(predicate_search).to have_attributes(relation: Product.left_outer_joins(:variations).group(:id))
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'is expected to set #permit_aggregate_modifiers eq `permit_aggregate_modifiers`' do
|
21
|
+
expect(predicate_search).to have_attributes(permit_aggregate_modifiers: true)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'is expected to evaluate the passed block' do
|
25
|
+
expect(predicate_search.permitted_attributes).to be_present
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#parser' do
|
30
|
+
subject(:parser) { predicate_search.parser }
|
31
|
+
|
32
|
+
it { is_expected.to be_a(Pursuit::PredicateParser) }
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#transform' do
|
36
|
+
subject(:transform) { predicate_search.transform }
|
37
|
+
|
38
|
+
it { is_expected.to be_a(Pursuit::PredicateTransform) }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#permit_attribute' do
|
42
|
+
subject(:permit_attribute) do
|
43
|
+
predicate_search.permit_attribute(:variation_currency, ProductVariation.arel_table[:currency])
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'is expected to add the attribute to #permitted_attributes' do
|
47
|
+
permit_attribute
|
48
|
+
expect(predicate_search.permitted_attributes).to match(
|
49
|
+
hash_including(
|
50
|
+
variation_currency: ProductVariation.arel_table[:currency]
|
51
|
+
)
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#parse' do
|
57
|
+
subject(:parse) { predicate_search.parse('title ~ Shirt & #variation > 0') }
|
58
|
+
|
59
|
+
it 'is expected to equal a Hash containing the ARel nodes' do
|
60
|
+
expect(parse).to eq(
|
61
|
+
{
|
62
|
+
where: Product.arel_table[:title].matches('%Shirt%'),
|
63
|
+
having: ProductVariation.arel_table[:id].count.gt(0)
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#apply' do
|
70
|
+
subject(:apply) { predicate_search.apply('title ~ Shirt') }
|
71
|
+
|
72
|
+
it 'is expected to equal #relation with predicate clauses applied' do
|
73
|
+
expect(apply).to eq(
|
74
|
+
Product.left_outer_joins(:variations).group(:id).where(
|
75
|
+
Product.arel_table[:title].matches('%Shirt%')
|
76
|
+
)
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,624 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Pursuit::PredicateTransform do
|
4
|
+
subject(:transform) { described_class.new }
|
5
|
+
|
6
|
+
describe '#apply' do
|
7
|
+
subject(:apply) do
|
8
|
+
transform.apply(
|
9
|
+
tree,
|
10
|
+
permitted_attributes: permitted_attributes,
|
11
|
+
permit_aggregate_modifiers: permit_aggregate_modifiers
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:permitted_attributes) do
|
16
|
+
{
|
17
|
+
title: Product.arel_table[:title],
|
18
|
+
created_at: Product.arel_table[:created_at],
|
19
|
+
updated_at: Product.arel_table[:updated_at],
|
20
|
+
variations: ProductVariation.arel_table[Arel.star],
|
21
|
+
variation_title: ProductVariation.arel_table[:title],
|
22
|
+
variation_currency: ProductVariation.arel_table[:currency],
|
23
|
+
variation_amount: ProductVariation.arel_table[:amount]
|
24
|
+
}.with_indifferent_access
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:permit_aggregate_modifiers) { true }
|
28
|
+
|
29
|
+
context 'when passed a truthy tree' do
|
30
|
+
let(:tree) do
|
31
|
+
{ truthy: 'true' }
|
32
|
+
end
|
33
|
+
|
34
|
+
it { is_expected.to be(true) }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when passed a falsey tree' do
|
38
|
+
let(:tree) do
|
39
|
+
{ falsey: 'false' }
|
40
|
+
end
|
41
|
+
|
42
|
+
it { is_expected.to be(false) }
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when passed an unsigned integer tree' do
|
46
|
+
let(:tree) do
|
47
|
+
{ integer: '3' }
|
48
|
+
end
|
49
|
+
|
50
|
+
it { is_expected.to be_an(Integer) }
|
51
|
+
|
52
|
+
it 'is expected to equal the correct integer value' do
|
53
|
+
expect(apply).to eq(3)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when passed a signed integer tree' do
|
58
|
+
let(:tree) do
|
59
|
+
{ integer: '-3' }
|
60
|
+
end
|
61
|
+
|
62
|
+
it { is_expected.to be_an(Integer) }
|
63
|
+
|
64
|
+
it 'is expected to equal the correct integer value' do
|
65
|
+
expect(apply).to eq(-3)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when passed an unsigned decimal tree' do
|
70
|
+
let(:tree) do
|
71
|
+
{ decimal: '3.14' }
|
72
|
+
end
|
73
|
+
|
74
|
+
it { is_expected.to be_a(BigDecimal) }
|
75
|
+
|
76
|
+
it 'is expected to equal the correct decimal value' do
|
77
|
+
expect(apply).to eq(3.14)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when passed a signed decimal tree' do
|
82
|
+
let(:tree) do
|
83
|
+
{ decimal: '-3.14' }
|
84
|
+
end
|
85
|
+
|
86
|
+
it { is_expected.to be_a(BigDecimal) }
|
87
|
+
|
88
|
+
it 'is expected to equal the correct decimal value' do
|
89
|
+
expect(apply).to eq(-3.14)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when passed an empty double quoted string' do
|
94
|
+
let(:tree) do
|
95
|
+
{ string_double_quotes: [] }
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'is expected to equal an empty string' do
|
99
|
+
expect(apply).to eq('')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when passed a double quoted string' do
|
104
|
+
let(:tree) do
|
105
|
+
{ string_double_quotes: 'Hello \\"World\\"' }
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'is expected to equal the correct string' do
|
109
|
+
expect(apply).to eq('Hello "World"')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when passed an empty single quoted string' do
|
114
|
+
let(:tree) do
|
115
|
+
{ string_single_quotes: [] }
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'is expected to equal an empty string' do
|
119
|
+
expect(apply).to eq('')
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'when passed a single quoted string' do
|
124
|
+
let(:tree) do
|
125
|
+
{ string_single_quotes: "Hello \\'World\\'" }
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'is expected to equal the correct string' do
|
129
|
+
expect(apply).to eq("Hello 'World'")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'when passed an unquoted string' do
|
134
|
+
let(:tree) do
|
135
|
+
{ string_no_quotes: 'hello_world' }
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'is expected to equal the correct string' do
|
139
|
+
expect(apply).to eq('hello_world')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'when passed a comparison with the "=" comparator' do
|
144
|
+
let(:tree) do
|
145
|
+
{
|
146
|
+
attribute: { string_no_quotes: 'title' },
|
147
|
+
comparator: '=',
|
148
|
+
value: { string_no_quotes: 'Shirt' }
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'is expected to equal the correct ARel node' do
|
153
|
+
expect(apply).to eq(Product.arel_table[:title].eq('Shirt'))
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'when the value is blank' do
|
157
|
+
let(:tree) do
|
158
|
+
{
|
159
|
+
attribute: { string_no_quotes: 'title' },
|
160
|
+
comparator: '=',
|
161
|
+
value: { string_single_quotes: [] }
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'is expected to equal the correct ARel node' do
|
166
|
+
expect(apply).to eq(
|
167
|
+
Product.arel_table[:title].eq(nil).or(
|
168
|
+
Product.arel_table[:title].matches_regexp('^\s*$')
|
169
|
+
)
|
170
|
+
)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'when passed a comparison with the "!=" comparator' do
|
176
|
+
let(:tree) do
|
177
|
+
{
|
178
|
+
attribute: { string_no_quotes: 'title' },
|
179
|
+
comparator: '!=',
|
180
|
+
value: { string_no_quotes: 'Shirt' }
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'is expected to equal the correct ARel node' do
|
185
|
+
expect(apply).to eq(Product.arel_table[:title].not_eq('Shirt'))
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'when the value is blank' do
|
189
|
+
let(:tree) do
|
190
|
+
{
|
191
|
+
attribute: { string_no_quotes: 'title' },
|
192
|
+
comparator: '!=',
|
193
|
+
value: { string_single_quotes: [] }
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'is expected to equal the correct ARel node' do
|
198
|
+
expect(apply).to eq(
|
199
|
+
Product.arel_table[:title].not_eq(nil).and(
|
200
|
+
Product.arel_table[:title].does_not_match_regexp('^\s*$')
|
201
|
+
)
|
202
|
+
)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'when passed a comparison with the "<" comparator' do
|
208
|
+
let(:tree) do
|
209
|
+
{
|
210
|
+
attribute: { string_no_quotes: 'created_at' },
|
211
|
+
comparator: '<',
|
212
|
+
value: { string_no_quotes: '1970-01-01' }
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'is expected to equal the correct ARel node' do
|
217
|
+
expect(apply).to eq(Product.arel_table[:created_at].lt('1970-01-01'))
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'when passed a comparison with the ">" comparator' do
|
222
|
+
let(:tree) do
|
223
|
+
{
|
224
|
+
attribute: { string_no_quotes: 'created_at' },
|
225
|
+
comparator: '>',
|
226
|
+
value: { string_no_quotes: '1970-01-01' }
|
227
|
+
}
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'is expected to equal the correct ARel node' do
|
231
|
+
expect(apply).to eq(Product.arel_table[:created_at].gt('1970-01-01'))
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context 'when passed a comparison with the "<=" comparator' do
|
236
|
+
let(:tree) do
|
237
|
+
{
|
238
|
+
attribute: { string_no_quotes: 'created_at' },
|
239
|
+
comparator: '<=',
|
240
|
+
value: { string_no_quotes: '1970-01-01' }
|
241
|
+
}
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'is expected to equal the correct ARel node' do
|
245
|
+
expect(apply).to eq(Product.arel_table[:created_at].lteq('1970-01-01'))
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'when passed a comparison with the ">=" comparator' do
|
250
|
+
let(:tree) do
|
251
|
+
{
|
252
|
+
attribute: { string_no_quotes: 'created_at' },
|
253
|
+
comparator: '>=',
|
254
|
+
value: { string_no_quotes: '1970-01-01' }
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'is expected to equal the correct ARel node' do
|
259
|
+
expect(apply).to eq(Product.arel_table[:created_at].gteq('1970-01-01'))
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context 'when passed a comparison with the "~" comparator' do
|
264
|
+
let(:tree) do
|
265
|
+
{
|
266
|
+
attribute: { string_no_quotes: 'title' },
|
267
|
+
comparator: '~',
|
268
|
+
value: { string_no_quotes: 'Shirt' }
|
269
|
+
}
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'is expected to equal the correct ARel node' do
|
273
|
+
expect(apply).to eq(Product.arel_table[:title].matches('%Shirt%'))
|
274
|
+
end
|
275
|
+
|
276
|
+
context 'when the value is blank' do
|
277
|
+
let(:tree) do
|
278
|
+
{
|
279
|
+
attribute: { string_no_quotes: 'title' },
|
280
|
+
comparator: '~',
|
281
|
+
value: { string_single_quotes: [] }
|
282
|
+
}
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'is expected to equal the correct ARel node' do
|
286
|
+
expect(apply).to eq(Product.arel_table[:title].matches('%'))
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
context 'when passed a comparison with the "!~" comparator' do
|
292
|
+
let(:tree) do
|
293
|
+
{
|
294
|
+
attribute: { string_no_quotes: 'title' },
|
295
|
+
comparator: '!~',
|
296
|
+
value: { string_no_quotes: 'Shirt' }
|
297
|
+
}
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'is expected to equal the correct ARel node' do
|
301
|
+
expect(apply).to eq(Product.arel_table[:title].does_not_match('%Shirt%'))
|
302
|
+
end
|
303
|
+
|
304
|
+
context 'when the value is blank' do
|
305
|
+
let(:tree) do
|
306
|
+
{
|
307
|
+
attribute: { string_no_quotes: 'title' },
|
308
|
+
comparator: '!~',
|
309
|
+
value: { string_single_quotes: [] }
|
310
|
+
}
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'is expected to equal the correct ARel node' do
|
314
|
+
expect(apply).to eq(Product.arel_table[:title].does_not_match('%'))
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
context 'when passed a comparison with an unpermitted attribute' do
|
320
|
+
let(:tree) do
|
321
|
+
{
|
322
|
+
attribute: { string_no_quotes: 'id' },
|
323
|
+
comparator: '=',
|
324
|
+
value: { integer: '123' }
|
325
|
+
}
|
326
|
+
end
|
327
|
+
|
328
|
+
it { expect { apply }.to raise_exception(Pursuit::AttributeNotFound) }
|
329
|
+
end
|
330
|
+
|
331
|
+
context 'when passed a comparison with an attribute representing "*"' do
|
332
|
+
let(:tree) do
|
333
|
+
{
|
334
|
+
attribute: { string_no_quotes: 'variations' },
|
335
|
+
comparator: '=',
|
336
|
+
value: { integer: '123' }
|
337
|
+
}
|
338
|
+
end
|
339
|
+
|
340
|
+
it { expect { apply }.to raise_exception(Pursuit::AggregateModifierRequired) }
|
341
|
+
end
|
342
|
+
|
343
|
+
context 'when passed an aggregate comparison with the "=" comparator' do
|
344
|
+
let(:tree) do
|
345
|
+
{
|
346
|
+
aggregate_modifier: '*',
|
347
|
+
attribute: { string_no_quotes: 'variation_amount' },
|
348
|
+
comparator: '=',
|
349
|
+
value: { integer: '5000' }
|
350
|
+
}
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'is expected to equal the correct ARel node' do
|
354
|
+
expect(apply).to eq(ProductVariation.arel_table[:amount].sum.eq(5000))
|
355
|
+
end
|
356
|
+
|
357
|
+
context 'when the value is blank' do
|
358
|
+
let(:tree) do
|
359
|
+
{
|
360
|
+
aggregate_modifier: '*',
|
361
|
+
attribute: { string_no_quotes: 'variation_amount' },
|
362
|
+
comparator: '=',
|
363
|
+
value: { string_single_quotes: [] }
|
364
|
+
}
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'is expected to equal the correct ARel node' do
|
368
|
+
expect(apply).to eq(
|
369
|
+
ProductVariation.arel_table[:amount].sum.eq(nil).or(
|
370
|
+
ProductVariation.arel_table[:amount].sum.matches_regexp('^\s*$')
|
371
|
+
)
|
372
|
+
)
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
context 'when passed an aggregate comparison with the "!=" comparator' do
|
378
|
+
let(:tree) do
|
379
|
+
{
|
380
|
+
aggregate_modifier: '*',
|
381
|
+
attribute: { string_no_quotes: 'variation_amount' },
|
382
|
+
comparator: '!=',
|
383
|
+
value: { integer: '5000' }
|
384
|
+
}
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'is expected to equal the correct ARel node' do
|
388
|
+
expect(apply).to eq(ProductVariation.arel_table[:amount].sum.not_eq(5000))
|
389
|
+
end
|
390
|
+
|
391
|
+
context 'when the value is blank' do
|
392
|
+
let(:tree) do
|
393
|
+
{
|
394
|
+
aggregate_modifier: '*',
|
395
|
+
attribute: { string_no_quotes: 'variation_amount' },
|
396
|
+
comparator: '!=',
|
397
|
+
value: { string_single_quotes: [] }
|
398
|
+
}
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'is expected to equal the correct ARel node' do
|
402
|
+
expect(apply).to eq(
|
403
|
+
ProductVariation.arel_table[:amount].sum.not_eq(nil).and(
|
404
|
+
ProductVariation.arel_table[:amount].sum.does_not_match_regexp('^\s*$')
|
405
|
+
)
|
406
|
+
)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
context 'when passed an aggregate comparison with the "<" comparator' do
|
412
|
+
let(:tree) do
|
413
|
+
{
|
414
|
+
aggregate_modifier: '-',
|
415
|
+
attribute: { string_no_quotes: 'variation_amount' },
|
416
|
+
comparator: '<',
|
417
|
+
value: { integer: '1000' }
|
418
|
+
}
|
419
|
+
end
|
420
|
+
|
421
|
+
it 'is expected to equal the correct ARel node' do
|
422
|
+
expect(apply).to eq(ProductVariation.arel_table[:amount].minimum.lt(1000))
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context 'when passed an aggregate comparison with the ">" comparator' do
|
427
|
+
let(:tree) do
|
428
|
+
{
|
429
|
+
aggregate_modifier: '+',
|
430
|
+
attribute: { string_no_quotes: 'variation_amount' },
|
431
|
+
comparator: '>',
|
432
|
+
value: { integer: '1000' }
|
433
|
+
}
|
434
|
+
end
|
435
|
+
|
436
|
+
it 'is expected to equal the correct ARel node' do
|
437
|
+
expect(apply).to eq(ProductVariation.arel_table[:amount].maximum.gt(1000))
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
context 'when passed an aggregate comparison with the "<=" comparator' do
|
442
|
+
let(:tree) do
|
443
|
+
{
|
444
|
+
aggregate_modifier: '-',
|
445
|
+
attribute: { string_no_quotes: 'variation_amount' },
|
446
|
+
comparator: '<=',
|
447
|
+
value: { integer: '1000' }
|
448
|
+
}
|
449
|
+
end
|
450
|
+
|
451
|
+
it 'is expected to equal the correct ARel node' do
|
452
|
+
expect(apply).to eq(ProductVariation.arel_table[:amount].minimum.lteq(1000))
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
context 'when passed an aggregate comparison with the ">=" comparator' do
|
457
|
+
let(:tree) do
|
458
|
+
{
|
459
|
+
aggregate_modifier: '+',
|
460
|
+
attribute: { string_no_quotes: 'variation_amount' },
|
461
|
+
comparator: '>=',
|
462
|
+
value: { integer: '1000' }
|
463
|
+
}
|
464
|
+
end
|
465
|
+
|
466
|
+
it 'is expected to equal the correct ARel node' do
|
467
|
+
expect(apply).to eq(ProductVariation.arel_table[:amount].maximum.gteq(1000))
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
context 'when passed an aggregate comparison with the "~" comparator' do
|
472
|
+
let(:tree) do
|
473
|
+
{
|
474
|
+
aggregate_modifier: '~',
|
475
|
+
attribute: { string_no_quotes: 'variation_currency' },
|
476
|
+
comparator: '~',
|
477
|
+
value: { string_no_quotes: 'USD' }
|
478
|
+
}
|
479
|
+
end
|
480
|
+
|
481
|
+
it 'is expected to equal the correct ARel node' do
|
482
|
+
expect(apply).to eq(ProductVariation.arel_table[:currency].average.matches('%USD%'))
|
483
|
+
end
|
484
|
+
|
485
|
+
context 'when the value is blank' do
|
486
|
+
let(:tree) do
|
487
|
+
{
|
488
|
+
aggregate_modifier: '~',
|
489
|
+
attribute: { string_no_quotes: 'variation_currency' },
|
490
|
+
comparator: '~',
|
491
|
+
value: { string_single_quotes: [] }
|
492
|
+
}
|
493
|
+
end
|
494
|
+
|
495
|
+
it 'is expected to equal the correct ARel node' do
|
496
|
+
expect(apply).to eq(ProductVariation.arel_table[:currency].average.matches('%'))
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
context 'when passed an aggregate comparison with the "!~" comparator' do
|
502
|
+
let(:tree) do
|
503
|
+
{
|
504
|
+
aggregate_modifier: '~',
|
505
|
+
attribute: { string_no_quotes: 'variation_currency' },
|
506
|
+
comparator: '!~',
|
507
|
+
value: { string_no_quotes: 'USD' }
|
508
|
+
}
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'is expected to equal the correct ARel node' do
|
512
|
+
expect(apply).to eq(ProductVariation.arel_table[:currency].average.does_not_match('%USD%'))
|
513
|
+
end
|
514
|
+
|
515
|
+
context 'when the value is blank' do
|
516
|
+
let(:tree) do
|
517
|
+
{
|
518
|
+
aggregate_modifier: '~',
|
519
|
+
attribute: { string_no_quotes: 'variation_currency' },
|
520
|
+
comparator: '!~',
|
521
|
+
value: { string_single_quotes: [] }
|
522
|
+
}
|
523
|
+
end
|
524
|
+
|
525
|
+
it 'is expected to equal the correct ARel node' do
|
526
|
+
expect(apply).to eq(ProductVariation.arel_table[:currency].average.does_not_match('%'))
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
context 'when passed an aggregate comparison with an unpermitted attribute' do
|
532
|
+
let(:tree) do
|
533
|
+
{
|
534
|
+
aggregate_modifier: '#',
|
535
|
+
attribute: { string_no_quotes: 'variation_id' },
|
536
|
+
comparator: '>',
|
537
|
+
value: { integer: '0' }
|
538
|
+
}
|
539
|
+
end
|
540
|
+
|
541
|
+
it { expect { apply }.to raise_exception(Pursuit::AttributeNotFound) }
|
542
|
+
end
|
543
|
+
|
544
|
+
context 'when passed an aggregate comparison with an unknown aggregate modifier' do
|
545
|
+
let(:tree) do
|
546
|
+
{
|
547
|
+
aggregate_modifier: '!',
|
548
|
+
attribute: { string_no_quotes: 'variations' },
|
549
|
+
comparator: '>',
|
550
|
+
value: { integer: '0' }
|
551
|
+
}
|
552
|
+
end
|
553
|
+
|
554
|
+
it { expect { apply }.to raise_exception(Pursuit::AggregateModifierNotFound) }
|
555
|
+
end
|
556
|
+
|
557
|
+
context 'when passed an aggregate comparison with aggregate modifiers disabled' do
|
558
|
+
let(:permit_aggregate_modifiers) { false }
|
559
|
+
|
560
|
+
let(:tree) do
|
561
|
+
{
|
562
|
+
aggregate_modifier: '#',
|
563
|
+
attribute: { string_no_quotes: 'variations' },
|
564
|
+
comparator: '>',
|
565
|
+
value: { integer: '0' }
|
566
|
+
}
|
567
|
+
end
|
568
|
+
|
569
|
+
it { expect { apply }.to raise_exception(Pursuit::AggregateModifiersNotAvailable) }
|
570
|
+
end
|
571
|
+
|
572
|
+
context 'when passed a join using the "|" joiner' do
|
573
|
+
let(:tree) do
|
574
|
+
{
|
575
|
+
left: {
|
576
|
+
attribute: { string_no_quotes: 'title' },
|
577
|
+
comparator: '=',
|
578
|
+
value: { string_no_quotes: 'Shirt' }
|
579
|
+
},
|
580
|
+
joiner: '|',
|
581
|
+
right: {
|
582
|
+
attribute: { string_no_quotes: 'title' },
|
583
|
+
comparator: '=',
|
584
|
+
value: { string_no_quotes: 'Jumper' }
|
585
|
+
}
|
586
|
+
}
|
587
|
+
end
|
588
|
+
|
589
|
+
it 'is expected to equal the correct ARel node' do
|
590
|
+
expect(apply).to eq(
|
591
|
+
Product.arel_table[:title].eq('Shirt').or(
|
592
|
+
Product.arel_table[:title].eq('Jumper')
|
593
|
+
)
|
594
|
+
)
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
context 'when passed a join using the "&" joiner' do
|
599
|
+
let(:tree) do
|
600
|
+
{
|
601
|
+
left: {
|
602
|
+
attribute: { string_no_quotes: 'title' },
|
603
|
+
comparator: '~',
|
604
|
+
value: { string_no_quotes: 'Shirt' }
|
605
|
+
},
|
606
|
+
joiner: '&',
|
607
|
+
right: {
|
608
|
+
attribute: { string_no_quotes: 'title' },
|
609
|
+
comparator: '~',
|
610
|
+
value: { string_no_quotes: 'Green' }
|
611
|
+
}
|
612
|
+
}
|
613
|
+
end
|
614
|
+
|
615
|
+
it 'is expected to equal the correct ARel node' do
|
616
|
+
expect(apply).to eq(
|
617
|
+
Product.arel_table[:title].matches('%Shirt%').and(
|
618
|
+
Product.arel_table[:title].matches('%Green%')
|
619
|
+
)
|
620
|
+
)
|
621
|
+
end
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|