cuprum-collections 0.4.0 → 0.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 +73 -0
- data/README.md +5 -5
- data/lib/cuprum/collections/association.rb +9 -28
- data/lib/cuprum/collections/associations/belongs_to.rb +1 -8
- data/lib/cuprum/collections/associations/has_many.rb +1 -10
- data/lib/cuprum/collections/associations/has_one.rb +1 -10
- data/lib/cuprum/collections/basic/collection.rb +56 -49
- data/lib/cuprum/collections/basic/command.rb +22 -88
- data/lib/cuprum/collections/basic/commands/assign_one.rb +2 -6
- data/lib/cuprum/collections/basic/commands/build_one.rb +1 -4
- data/lib/cuprum/collections/basic/commands/destroy_one.rb +4 -8
- data/lib/cuprum/collections/basic/commands/find_many.rb +4 -24
- data/lib/cuprum/collections/basic/commands/find_matching.rb +5 -21
- data/lib/cuprum/collections/basic/commands/find_one.rb +3 -20
- data/lib/cuprum/collections/basic/commands/insert_one.rb +3 -6
- data/lib/cuprum/collections/basic/commands/update_one.rb +3 -6
- data/lib/cuprum/collections/basic/commands/validate_one.rb +13 -18
- data/lib/cuprum/collections/basic/query.rb +26 -40
- data/lib/cuprum/collections/basic/repository.rb +4 -3
- data/lib/cuprum/collections/basic/scopes/all_scope.rb +25 -0
- data/lib/cuprum/collections/basic/scopes/base.rb +32 -0
- data/lib/cuprum/collections/basic/scopes/builder.rb +39 -0
- data/lib/cuprum/collections/basic/scopes/conjunction_scope.rb +20 -0
- data/lib/cuprum/collections/basic/scopes/criteria_scope.rb +62 -0
- data/lib/cuprum/collections/basic/scopes/disjunction_scope.rb +20 -0
- data/lib/cuprum/collections/basic/scopes/none_scope.rb +33 -0
- data/lib/cuprum/collections/basic/scopes.rb +23 -0
- data/lib/cuprum/collections/basic.rb +1 -0
- data/lib/cuprum/collections/collection.rb +24 -82
- data/lib/cuprum/collections/collection_command.rb +116 -0
- data/lib/cuprum/collections/commands/abstract_find_many.rb +11 -21
- data/lib/cuprum/collections/commands/abstract_find_matching.rb +43 -24
- data/lib/cuprum/collections/commands/abstract_find_one.rb +7 -10
- data/lib/cuprum/collections/commands/associations/find_many.rb +3 -8
- data/lib/cuprum/collections/commands/associations/require_many.rb +5 -5
- data/lib/cuprum/collections/commands/create.rb +3 -3
- data/lib/cuprum/collections/commands/find_one_matching.rb +6 -6
- data/lib/cuprum/collections/commands/query_command.rb +19 -0
- data/lib/cuprum/collections/commands/update.rb +3 -3
- data/lib/cuprum/collections/commands/upsert.rb +10 -10
- data/lib/cuprum/collections/commands.rb +1 -0
- data/lib/cuprum/collections/constraints/ordering.rb +2 -2
- data/lib/cuprum/collections/errors/abstract_find_error.rb +25 -42
- data/lib/cuprum/collections/errors/extra_attributes.rb +3 -3
- data/lib/cuprum/collections/errors/failed_validation.rb +2 -2
- data/lib/cuprum/collections/errors/invalid_parameters.rb +2 -2
- data/lib/cuprum/collections/errors/invalid_query.rb +10 -16
- data/lib/cuprum/collections/errors/missing_default_contract.rb +1 -1
- data/lib/cuprum/collections/errors/unknown_operator.rb +1 -1
- data/lib/cuprum/collections/queries.rb +31 -0
- data/lib/cuprum/collections/query.rb +50 -62
- data/lib/cuprum/collections/relation.rb +5 -383
- data/lib/cuprum/collections/relations/cardinality.rb +66 -0
- data/lib/cuprum/collections/relations/options.rb +18 -0
- data/lib/cuprum/collections/relations/parameters.rb +217 -0
- data/lib/cuprum/collections/relations/primary_keys.rb +23 -0
- data/lib/cuprum/collections/relations/scope.rb +65 -0
- data/lib/cuprum/collections/relations.rb +14 -0
- data/lib/cuprum/collections/repository.rb +5 -5
- data/lib/cuprum/collections/resource.rb +10 -41
- data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +80 -90
- data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +69 -111
- data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +42 -1335
- data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +352 -531
- data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +74 -191
- data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +13 -13
- data/lib/cuprum/collections/rspec/contracts/scope_contracts.rb +1029 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/builder_contracts.rb +856 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/composition_contracts.rb +1430 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/criteria_contracts.rb +2217 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/logical_contracts.rb +297 -0
- data/lib/cuprum/collections/rspec/contracts/scopes.rb +13 -0
- data/lib/cuprum/collections/rspec/contracts.rb +2 -0
- data/lib/cuprum/collections/rspec/deferred/association_examples.rb +2098 -0
- data/lib/cuprum/collections/rspec/deferred/collection_examples.rb +338 -0
- data/lib/cuprum/collections/rspec/deferred/command_examples.rb +160 -0
- data/lib/cuprum/collections/rspec/deferred/commands/assign_one_examples.rb +178 -0
- data/lib/cuprum/collections/rspec/deferred/commands/build_one_examples.rb +94 -0
- data/lib/cuprum/collections/rspec/deferred/commands/destroy_one_examples.rb +118 -0
- data/lib/cuprum/collections/rspec/deferred/commands/find_many_examples.rb +307 -0
- data/lib/cuprum/collections/rspec/deferred/commands/find_matching_examples.rb +143 -0
- data/lib/cuprum/collections/rspec/deferred/commands/find_one_examples.rb +116 -0
- data/lib/cuprum/collections/rspec/deferred/commands/insert_one_examples.rb +103 -0
- data/lib/cuprum/collections/rspec/deferred/commands/update_one_examples.rb +99 -0
- data/lib/cuprum/collections/rspec/deferred/commands/validate_one_examples.rb +117 -0
- data/lib/cuprum/collections/rspec/deferred/commands.rb +8 -0
- data/lib/cuprum/collections/rspec/deferred/relation_examples.rb +1437 -0
- data/lib/cuprum/collections/rspec/deferred/resource_examples.rb +26 -0
- data/lib/cuprum/collections/rspec/deferred.rb +8 -0
- data/lib/cuprum/collections/scope.rb +29 -0
- data/lib/cuprum/collections/scopes/all.rb +51 -0
- data/lib/cuprum/collections/scopes/all_scope.rb +18 -0
- data/lib/cuprum/collections/scopes/base.rb +79 -0
- data/lib/cuprum/collections/scopes/builder.rb +39 -0
- data/lib/cuprum/collections/scopes/building.rb +221 -0
- data/lib/cuprum/collections/scopes/composition.rb +162 -0
- data/lib/cuprum/collections/scopes/conjunction.rb +44 -0
- data/lib/cuprum/collections/scopes/conjunction_scope.rb +12 -0
- data/lib/cuprum/collections/scopes/container.rb +65 -0
- data/lib/cuprum/collections/scopes/criteria/parser.rb +241 -0
- data/lib/cuprum/collections/scopes/criteria.rb +206 -0
- data/lib/cuprum/collections/scopes/criteria_scope.rb +12 -0
- data/lib/cuprum/collections/scopes/disjunction.rb +45 -0
- data/lib/cuprum/collections/scopes/disjunction_scope.rb +12 -0
- data/lib/cuprum/collections/scopes/none.rb +62 -0
- data/lib/cuprum/collections/scopes/none_scope.rb +18 -0
- data/lib/cuprum/collections/scopes.rb +23 -0
- data/lib/cuprum/collections/version.rb +2 -2
- data/lib/cuprum/collections.rb +14 -9
- metadata +61 -15
- data/lib/cuprum/collections/basic/query_builder.rb +0 -69
- data/lib/cuprum/collections/command.rb +0 -26
- data/lib/cuprum/collections/queries/parse.rb +0 -22
- data/lib/cuprum/collections/queries/parse_block.rb +0 -206
- data/lib/cuprum/collections/queries/parse_strategy.rb +0 -91
- data/lib/cuprum/collections/query_builder.rb +0 -61
- data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +0 -484
@@ -0,0 +1,2217 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/rspec/contracts/scopes'
|
4
|
+
require 'cuprum/collections/rspec/contracts/scope_contracts'
|
5
|
+
require 'cuprum/collections/rspec/contracts/scopes/composition_contracts'
|
6
|
+
|
7
|
+
module Cuprum::Collections::RSpec::Contracts::Scopes
|
8
|
+
# Contracts for asserting on criteria scope objects.
|
9
|
+
module CriteriaContracts
|
10
|
+
include Cuprum::Collections::RSpec::Contracts::ScopeContracts
|
11
|
+
include Cuprum::Collections::RSpec::Contracts::Scopes::CompositionContracts
|
12
|
+
|
13
|
+
# Contract validating the behavior of a Criteria scope implementation.
|
14
|
+
module ShouldBeACriteriaScopeContract
|
15
|
+
extend RSpec::SleepingKingStudios::Contract
|
16
|
+
|
17
|
+
# @!method apply(example_group, abstract: false, constructor: true, equality: true, ignore_uninvertible: false)
|
18
|
+
# Adds the contract to the example group.
|
19
|
+
#
|
20
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
21
|
+
# which the contract is applied.
|
22
|
+
# @param abstract [Boolean] if true, the scope is abstract and does not
|
23
|
+
# define a #call implementation. Defaults to false.
|
24
|
+
# @param equality [Boolean] if false, skips the specs for the equality
|
25
|
+
# operator #==. Defaults to true.
|
26
|
+
# @param ignore_uninvertible [Boolean] if true, skips the specs for
|
27
|
+
# uninvertible operators. Defaults to false.
|
28
|
+
contract do |
|
29
|
+
abstract: false,
|
30
|
+
equality: true,
|
31
|
+
constructor: true,
|
32
|
+
ignore_uninvertible: false
|
33
|
+
|
|
34
|
+
shared_context 'when initialized with inverted: true' do
|
35
|
+
let(:constructor_options) { super().merge(inverted: true) }
|
36
|
+
end
|
37
|
+
|
38
|
+
shared_context 'with criteria' do
|
39
|
+
let(:criteria) do
|
40
|
+
operators = Cuprum::Collections::Queries::Operators
|
41
|
+
|
42
|
+
[
|
43
|
+
['title', operators::EQUAL, 'Gideon the Ninth'],
|
44
|
+
['author', operators::EQUAL, 'Tamsyn Muir']
|
45
|
+
]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
let(:criteria) { [] }
|
50
|
+
|
51
|
+
describe '.new' do
|
52
|
+
next unless constructor
|
53
|
+
|
54
|
+
it 'should define the constructor' do
|
55
|
+
expect(described_class)
|
56
|
+
.to be_constructible
|
57
|
+
.with(0).arguments
|
58
|
+
.and_keywords(:criteria, :inverted)
|
59
|
+
.and_any_keywords
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '.build' do
|
64
|
+
let(:value) { { 'title' => 'The Word for World is Forest' } }
|
65
|
+
|
66
|
+
def parse_criteria(*args, &block)
|
67
|
+
return described_class.build(&block).criteria if args.empty?
|
68
|
+
|
69
|
+
described_class.build(args.first, &block).criteria
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should define class method' do
|
73
|
+
expect(described_class)
|
74
|
+
.to respond_to(:build)
|
75
|
+
.with(0..1).arguments
|
76
|
+
.and_a_block
|
77
|
+
end
|
78
|
+
|
79
|
+
it { expect(described_class.build(value)).to be_a described_class }
|
80
|
+
|
81
|
+
include_contract 'should parse criteria'
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '.parse' do
|
85
|
+
def parse_criteria(*args, &block)
|
86
|
+
return described_class.parse(&block) if args.empty?
|
87
|
+
|
88
|
+
described_class.parse(args.first, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should define class method' do
|
92
|
+
expect(described_class)
|
93
|
+
.to respond_to(:parse)
|
94
|
+
.with(0..1).arguments
|
95
|
+
.and_a_block
|
96
|
+
end
|
97
|
+
|
98
|
+
include_contract 'should parse criteria'
|
99
|
+
end
|
100
|
+
|
101
|
+
include_contract 'should be a scope', invertible: true
|
102
|
+
|
103
|
+
include_contract 'should compose scopes for criteria'
|
104
|
+
|
105
|
+
describe '#==' do
|
106
|
+
next unless equality
|
107
|
+
|
108
|
+
describe 'with a scope with the same class' do
|
109
|
+
let(:other_criteria) { [] }
|
110
|
+
let(:other_inverted) { false }
|
111
|
+
let(:other) do
|
112
|
+
described_class.new(
|
113
|
+
criteria: other_criteria,
|
114
|
+
inverted: other_inverted
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'with empty criteria' do
|
119
|
+
let(:other_criteria) { [] }
|
120
|
+
|
121
|
+
it { expect(subject == other).to be true }
|
122
|
+
|
123
|
+
describe 'with inverted: true' do
|
124
|
+
let(:other_inverted) { true }
|
125
|
+
|
126
|
+
it { expect(subject == other).to be false }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'with non-matching criteria' do
|
131
|
+
let(:other_criteria) do
|
132
|
+
operators = Cuprum::Collections::Queries::Operators
|
133
|
+
|
134
|
+
[
|
135
|
+
[
|
136
|
+
'ok',
|
137
|
+
operators::EQUAL,
|
138
|
+
true
|
139
|
+
]
|
140
|
+
]
|
141
|
+
end
|
142
|
+
|
143
|
+
it { expect(subject == other).to be false }
|
144
|
+
|
145
|
+
describe 'with inverted: true' do
|
146
|
+
let(:other_inverted) { true }
|
147
|
+
|
148
|
+
it { expect(subject == other).to be false }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
wrap_context 'with criteria' do
|
153
|
+
describe 'with empty criteria' do
|
154
|
+
let(:other_criteria) { [] }
|
155
|
+
|
156
|
+
it { expect(subject == other).to be false }
|
157
|
+
|
158
|
+
describe 'with inverted: true' do
|
159
|
+
let(:other_inverted) { true }
|
160
|
+
|
161
|
+
it { expect(subject == other).to be false }
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'with non-matching criteria' do
|
166
|
+
let(:other_criteria) do
|
167
|
+
operators = Cuprum::Collections::Queries::Operators
|
168
|
+
|
169
|
+
[
|
170
|
+
[
|
171
|
+
'ok',
|
172
|
+
operators::EQUAL,
|
173
|
+
true
|
174
|
+
]
|
175
|
+
]
|
176
|
+
end
|
177
|
+
|
178
|
+
it { expect(subject == other).to be false }
|
179
|
+
|
180
|
+
describe 'with inverted: true' do
|
181
|
+
let(:other_inverted) { true }
|
182
|
+
|
183
|
+
it { expect(subject == other).to be false }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe 'with matching criteria' do
|
188
|
+
let(:other_criteria) { subject.criteria }
|
189
|
+
|
190
|
+
it { expect(subject == other).to be true }
|
191
|
+
|
192
|
+
describe 'with inverted: true' do
|
193
|
+
let(:other_inverted) { true }
|
194
|
+
|
195
|
+
it { expect(subject == other).to be false }
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
wrap_context 'when initialized with inverted: true' do
|
201
|
+
describe 'with empty criteria' do
|
202
|
+
let(:other_criteria) { [] }
|
203
|
+
|
204
|
+
it { expect(subject == other).to be false }
|
205
|
+
|
206
|
+
describe 'with inverted: true' do
|
207
|
+
let(:other_inverted) { true }
|
208
|
+
|
209
|
+
it { expect(subject == other).to be true }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe 'with non-matching criteria' do
|
214
|
+
let(:other_criteria) do
|
215
|
+
operators = Cuprum::Collections::Queries::Operators
|
216
|
+
|
217
|
+
[
|
218
|
+
[
|
219
|
+
'ok',
|
220
|
+
operators::EQUAL,
|
221
|
+
true
|
222
|
+
]
|
223
|
+
]
|
224
|
+
end
|
225
|
+
|
226
|
+
it { expect(subject == other).to be false }
|
227
|
+
|
228
|
+
describe 'with inverted: true' do
|
229
|
+
let(:other_inverted) { true }
|
230
|
+
|
231
|
+
it { expect(subject == other).to be false }
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
wrap_context 'with criteria' do
|
236
|
+
describe 'with empty criteria' do
|
237
|
+
let(:other_criteria) { [] }
|
238
|
+
|
239
|
+
it { expect(subject == other).to be false }
|
240
|
+
|
241
|
+
describe 'with inverted: true' do
|
242
|
+
let(:other_inverted) { true }
|
243
|
+
|
244
|
+
it { expect(subject == other).to be false }
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe 'with non-matching criteria' do
|
249
|
+
let(:other_criteria) do
|
250
|
+
operators = Cuprum::Collections::Queries::Operators
|
251
|
+
|
252
|
+
[
|
253
|
+
[
|
254
|
+
'ok',
|
255
|
+
operators::EQUAL,
|
256
|
+
true
|
257
|
+
]
|
258
|
+
]
|
259
|
+
end
|
260
|
+
|
261
|
+
it { expect(subject == other).to be false }
|
262
|
+
|
263
|
+
describe 'with inverted: true' do
|
264
|
+
let(:other_inverted) { true }
|
265
|
+
|
266
|
+
it { expect(subject == other).to be false }
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
describe 'with matching criteria' do
|
271
|
+
let(:other_criteria) { subject.criteria }
|
272
|
+
|
273
|
+
it { expect(subject == other).to be false }
|
274
|
+
|
275
|
+
describe 'with inverted: true' do
|
276
|
+
let(:other_inverted) { true }
|
277
|
+
|
278
|
+
it { expect(subject == other).to be true }
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe 'with a scope with the same type' do
|
286
|
+
let(:other_criteria) { [] }
|
287
|
+
let(:other_inverted) { false }
|
288
|
+
let(:other) do
|
289
|
+
described_class.new(
|
290
|
+
criteria: other_criteria,
|
291
|
+
inverted: other_inverted
|
292
|
+
)
|
293
|
+
end
|
294
|
+
|
295
|
+
example_class 'Spec::CustomScope',
|
296
|
+
Cuprum::Collections::Scopes::Base \
|
297
|
+
do |klass|
|
298
|
+
klass.include Cuprum::Collections::Scopes::Criteria
|
299
|
+
end
|
300
|
+
|
301
|
+
describe 'with empty criteria' do
|
302
|
+
let(:other_criteria) { [] }
|
303
|
+
|
304
|
+
it { expect(subject == other).to be true }
|
305
|
+
|
306
|
+
describe 'with inverted: true' do
|
307
|
+
let(:other_inverted) { true }
|
308
|
+
|
309
|
+
it { expect(subject == other).to be false }
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe 'with non-matching criteria' do
|
314
|
+
let(:other_criteria) do
|
315
|
+
operators = Cuprum::Collections::Queries::Operators
|
316
|
+
|
317
|
+
[
|
318
|
+
[
|
319
|
+
'ok',
|
320
|
+
operators::EQUAL,
|
321
|
+
true
|
322
|
+
]
|
323
|
+
]
|
324
|
+
end
|
325
|
+
|
326
|
+
it { expect(subject == other).to be false }
|
327
|
+
|
328
|
+
describe 'with inverted: true' do
|
329
|
+
let(:other_inverted) { true }
|
330
|
+
|
331
|
+
it { expect(subject == other).to be false }
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
wrap_context 'with criteria' do
|
336
|
+
describe 'with empty criteria' do
|
337
|
+
let(:other_criteria) { [] }
|
338
|
+
|
339
|
+
it { expect(subject == other).to be false }
|
340
|
+
|
341
|
+
describe 'with inverted: true' do
|
342
|
+
let(:other_inverted) { true }
|
343
|
+
|
344
|
+
it { expect(subject == other).to be false }
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
describe 'with non-matching criteria' do
|
349
|
+
let(:other_criteria) do
|
350
|
+
operators = Cuprum::Collections::Queries::Operators
|
351
|
+
|
352
|
+
[
|
353
|
+
[
|
354
|
+
'ok',
|
355
|
+
operators::EQUAL,
|
356
|
+
true
|
357
|
+
]
|
358
|
+
]
|
359
|
+
end
|
360
|
+
|
361
|
+
it { expect(subject == other).to be false }
|
362
|
+
|
363
|
+
describe 'with inverted: true' do
|
364
|
+
let(:other_inverted) { true }
|
365
|
+
|
366
|
+
it { expect(subject == other).to be false }
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
describe 'with matching criteria' do
|
371
|
+
let(:other_criteria) { subject.criteria }
|
372
|
+
|
373
|
+
it { expect(subject == other).to be true }
|
374
|
+
|
375
|
+
describe 'with inverted: true' do
|
376
|
+
let(:other_inverted) { true }
|
377
|
+
|
378
|
+
it { expect(subject == other).to be false }
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
wrap_context 'when initialized with inverted: true' do
|
384
|
+
describe 'with empty criteria' do
|
385
|
+
let(:other_criteria) { [] }
|
386
|
+
|
387
|
+
it { expect(subject == other).to be false }
|
388
|
+
|
389
|
+
describe 'with inverted: true' do
|
390
|
+
let(:other_inverted) { true }
|
391
|
+
|
392
|
+
it { expect(subject == other).to be true }
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
describe 'with non-matching criteria' do
|
397
|
+
let(:other_criteria) do
|
398
|
+
operators = Cuprum::Collections::Queries::Operators
|
399
|
+
|
400
|
+
[
|
401
|
+
[
|
402
|
+
'ok',
|
403
|
+
operators::EQUAL,
|
404
|
+
true
|
405
|
+
]
|
406
|
+
]
|
407
|
+
end
|
408
|
+
|
409
|
+
it { expect(subject == other).to be false }
|
410
|
+
|
411
|
+
describe 'with inverted: true' do
|
412
|
+
let(:other_inverted) { true }
|
413
|
+
|
414
|
+
it { expect(subject == other).to be false }
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
wrap_context 'with criteria' do
|
419
|
+
describe 'with empty criteria' do
|
420
|
+
let(:other_criteria) { [] }
|
421
|
+
|
422
|
+
it { expect(subject == other).to be false }
|
423
|
+
|
424
|
+
describe 'with inverted: true' do
|
425
|
+
let(:other_inverted) { true }
|
426
|
+
|
427
|
+
it { expect(subject == other).to be false }
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
describe 'with non-matching criteria' do
|
432
|
+
let(:other_criteria) do
|
433
|
+
operators = Cuprum::Collections::Queries::Operators
|
434
|
+
|
435
|
+
[
|
436
|
+
[
|
437
|
+
'ok',
|
438
|
+
operators::EQUAL,
|
439
|
+
true
|
440
|
+
]
|
441
|
+
]
|
442
|
+
end
|
443
|
+
|
444
|
+
it { expect(subject == other).to be false }
|
445
|
+
|
446
|
+
describe 'with inverted: true' do
|
447
|
+
let(:other_inverted) { true }
|
448
|
+
|
449
|
+
it { expect(subject == other).to be false }
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
describe 'with matching criteria' do
|
454
|
+
let(:other_criteria) { subject.criteria }
|
455
|
+
|
456
|
+
it { expect(subject == other).to be false }
|
457
|
+
|
458
|
+
describe 'with inverted: true' do
|
459
|
+
let(:other_inverted) { true }
|
460
|
+
|
461
|
+
it { expect(subject == other).to be true }
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
describe '#as_json' do
|
470
|
+
let(:expected) do
|
471
|
+
{
|
472
|
+
'criteria' => subject.criteria,
|
473
|
+
'inverted' => subject.inverted?,
|
474
|
+
'type' => subject.type
|
475
|
+
}
|
476
|
+
end
|
477
|
+
|
478
|
+
it { expect(subject.as_json).to be == expected }
|
479
|
+
|
480
|
+
wrap_context 'when initialized with inverted: true' do
|
481
|
+
it { expect(subject.as_json).to be == expected }
|
482
|
+
end
|
483
|
+
|
484
|
+
wrap_context 'with criteria' do
|
485
|
+
it { expect(subject.as_json).to be == expected }
|
486
|
+
|
487
|
+
wrap_context 'when initialized with inverted: true' do
|
488
|
+
it { expect(subject.as_json).to be == expected }
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
describe '#call' do
|
494
|
+
next if abstract
|
495
|
+
|
496
|
+
it { expect(subject).to respond_to(:call) }
|
497
|
+
|
498
|
+
include_contract 'should filter data by criteria'
|
499
|
+
|
500
|
+
context 'when the scope is inverted' do
|
501
|
+
subject { super().invert }
|
502
|
+
|
503
|
+
let(:matching_data) { data - filtered_data }
|
504
|
+
|
505
|
+
include_contract 'should filter data by criteria',
|
506
|
+
ignore_invalid: true,
|
507
|
+
inverted: true
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
describe '#criteria' do
|
512
|
+
include_examples 'should define reader', :criteria, -> { criteria }
|
513
|
+
|
514
|
+
wrap_context 'with criteria' do
|
515
|
+
it { expect(subject.criteria).to be == criteria }
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
describe '#empty?' do
|
520
|
+
include_examples 'should define predicate', :empty?, true
|
521
|
+
|
522
|
+
wrap_context 'with criteria' do
|
523
|
+
it { expect(subject.empty?).to be false }
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
describe '#invert' do
|
528
|
+
shared_examples 'should invert the criteria' do
|
529
|
+
it { expect(copy.criteria).to be == [] }
|
530
|
+
|
531
|
+
context 'when the scope has an equality criterion' do
|
532
|
+
let(:criteria) do
|
533
|
+
described_class.parse({
|
534
|
+
'title' => 'The Word for World is Forest'
|
535
|
+
})
|
536
|
+
end
|
537
|
+
let(:expected) do
|
538
|
+
operators = Cuprum::Collections::Queries::Operators
|
539
|
+
|
540
|
+
[
|
541
|
+
[
|
542
|
+
'title',
|
543
|
+
operators::NOT_EQUAL,
|
544
|
+
'The Word for World is Forest'
|
545
|
+
]
|
546
|
+
]
|
547
|
+
end
|
548
|
+
|
549
|
+
it { expect(copy.criteria).to be == expected }
|
550
|
+
end
|
551
|
+
|
552
|
+
context 'when the scope has a greater than criterion' do
|
553
|
+
let(:criteria) do
|
554
|
+
described_class.parse do |scope|
|
555
|
+
{ 'published_at' => scope.greater_than('1972-03-13') }
|
556
|
+
end
|
557
|
+
end
|
558
|
+
let(:expected) do
|
559
|
+
operators = Cuprum::Collections::Queries::Operators
|
560
|
+
|
561
|
+
[
|
562
|
+
[
|
563
|
+
'published_at',
|
564
|
+
operators::LESS_THAN_OR_EQUAL_TO,
|
565
|
+
'1972-03-13'
|
566
|
+
]
|
567
|
+
]
|
568
|
+
end
|
569
|
+
|
570
|
+
it { expect(copy.criteria).to be == expected }
|
571
|
+
end
|
572
|
+
|
573
|
+
context 'when the scope has a greater than or equal to criterion' do
|
574
|
+
let(:criteria) do
|
575
|
+
described_class.parse do |scope|
|
576
|
+
{
|
577
|
+
'published_at' => scope.greater_than_or_equal_to(
|
578
|
+
'1972-03-13'
|
579
|
+
)
|
580
|
+
}
|
581
|
+
end
|
582
|
+
end
|
583
|
+
let(:expected) do
|
584
|
+
operators = Cuprum::Collections::Queries::Operators
|
585
|
+
|
586
|
+
[
|
587
|
+
[
|
588
|
+
'published_at',
|
589
|
+
operators::LESS_THAN,
|
590
|
+
'1972-03-13'
|
591
|
+
]
|
592
|
+
]
|
593
|
+
end
|
594
|
+
|
595
|
+
it { expect(copy.criteria).to be == expected }
|
596
|
+
end
|
597
|
+
|
598
|
+
context 'when the scope has a less than criterion' do
|
599
|
+
let(:criteria) do
|
600
|
+
described_class.parse do |scope|
|
601
|
+
{ 'published_at' => scope.less_than('1972-03-13') }
|
602
|
+
end
|
603
|
+
end
|
604
|
+
let(:expected) do
|
605
|
+
operators = Cuprum::Collections::Queries::Operators
|
606
|
+
|
607
|
+
[
|
608
|
+
[
|
609
|
+
'published_at',
|
610
|
+
operators::GREATER_THAN_OR_EQUAL_TO,
|
611
|
+
'1972-03-13'
|
612
|
+
]
|
613
|
+
]
|
614
|
+
end
|
615
|
+
|
616
|
+
it { expect(copy.criteria).to be == expected }
|
617
|
+
end
|
618
|
+
|
619
|
+
context 'when the scope has a less than or equal to criterion' do
|
620
|
+
let(:criteria) do
|
621
|
+
described_class.parse do |scope|
|
622
|
+
{
|
623
|
+
'published_at' => scope.less_than_or_equal_to(
|
624
|
+
'1972-03-13'
|
625
|
+
)
|
626
|
+
}
|
627
|
+
end
|
628
|
+
end
|
629
|
+
let(:expected) do
|
630
|
+
operators = Cuprum::Collections::Queries::Operators
|
631
|
+
|
632
|
+
[
|
633
|
+
[
|
634
|
+
'published_at',
|
635
|
+
operators::GREATER_THAN,
|
636
|
+
'1972-03-13'
|
637
|
+
]
|
638
|
+
]
|
639
|
+
end
|
640
|
+
|
641
|
+
it { expect(copy.criteria).to be == expected }
|
642
|
+
end
|
643
|
+
|
644
|
+
context 'when the scope has a not equal criterion' do
|
645
|
+
let(:criteria) do
|
646
|
+
described_class.parse do |scope|
|
647
|
+
{ 'author' => scope.not_equal('J.R.R. Tolkien') }
|
648
|
+
end
|
649
|
+
end
|
650
|
+
let(:expected) do
|
651
|
+
operators = Cuprum::Collections::Queries::Operators
|
652
|
+
|
653
|
+
[
|
654
|
+
[
|
655
|
+
'author',
|
656
|
+
operators::EQUAL,
|
657
|
+
'J.R.R. Tolkien'
|
658
|
+
]
|
659
|
+
]
|
660
|
+
end
|
661
|
+
|
662
|
+
it { expect(copy.criteria).to be == expected }
|
663
|
+
end
|
664
|
+
|
665
|
+
context 'when the scope has a not one of criterion' do
|
666
|
+
let(:criteria) do
|
667
|
+
described_class.parse do |scope|
|
668
|
+
titles = ['The Fellowship Of The Ring', 'The Two Towers']
|
669
|
+
|
670
|
+
{ 'title' => scope.not_one_of(titles) }
|
671
|
+
end
|
672
|
+
end
|
673
|
+
let(:expected) do
|
674
|
+
operators = Cuprum::Collections::Queries::Operators
|
675
|
+
|
676
|
+
[
|
677
|
+
[
|
678
|
+
'title',
|
679
|
+
operators::ONE_OF,
|
680
|
+
['The Fellowship Of The Ring', 'The Two Towers']
|
681
|
+
]
|
682
|
+
]
|
683
|
+
end
|
684
|
+
|
685
|
+
it { expect(copy.criteria).to be == expected }
|
686
|
+
end
|
687
|
+
|
688
|
+
context 'when the scope has a one of criterion' do
|
689
|
+
let(:criteria) do
|
690
|
+
described_class.parse do |scope|
|
691
|
+
titles = ['The Fellowship Of The Ring', 'The Two Towers']
|
692
|
+
|
693
|
+
{ 'title' => scope.one_of(titles) }
|
694
|
+
end
|
695
|
+
end
|
696
|
+
let(:expected) do
|
697
|
+
operators = Cuprum::Collections::Queries::Operators
|
698
|
+
|
699
|
+
[
|
700
|
+
[
|
701
|
+
'title',
|
702
|
+
operators::NOT_ONE_OF,
|
703
|
+
['The Fellowship Of The Ring', 'The Two Towers']
|
704
|
+
]
|
705
|
+
]
|
706
|
+
end
|
707
|
+
|
708
|
+
it { expect(copy.criteria).to be == expected }
|
709
|
+
end
|
710
|
+
|
711
|
+
context 'when the scope has multiple criteria' do
|
712
|
+
let(:criteria) do
|
713
|
+
described_class.parse do |scope|
|
714
|
+
titles = ['The Fellowship Of The Ring', 'The Two Towers']
|
715
|
+
|
716
|
+
{
|
717
|
+
'author' => 'J.R.R. Tolkien',
|
718
|
+
'title' => scope.not_one_of(titles)
|
719
|
+
}
|
720
|
+
end
|
721
|
+
end
|
722
|
+
let(:expected) do
|
723
|
+
operators = Cuprum::Collections::Queries::Operators
|
724
|
+
|
725
|
+
[
|
726
|
+
[
|
727
|
+
'author',
|
728
|
+
operators::NOT_EQUAL,
|
729
|
+
'J.R.R. Tolkien'
|
730
|
+
],
|
731
|
+
[
|
732
|
+
'title',
|
733
|
+
operators::ONE_OF,
|
734
|
+
['The Fellowship Of The Ring', 'The Two Towers']
|
735
|
+
]
|
736
|
+
]
|
737
|
+
end
|
738
|
+
|
739
|
+
it { expect(copy.criteria).to be == expected }
|
740
|
+
end
|
741
|
+
|
742
|
+
context 'when initialized with uninvertible criteria' do
|
743
|
+
next if ignore_uninvertible
|
744
|
+
|
745
|
+
let(:criteria) do
|
746
|
+
[
|
747
|
+
[
|
748
|
+
'title',
|
749
|
+
'random',
|
750
|
+
nil
|
751
|
+
]
|
752
|
+
]
|
753
|
+
end
|
754
|
+
let(:error_class) do
|
755
|
+
Cuprum::Collections::Queries::UninvertibleOperatorException
|
756
|
+
end
|
757
|
+
let(:error_message) { 'uninvertible operator "random"' }
|
758
|
+
|
759
|
+
it 'should raise an exception' do
|
760
|
+
expect { subject.invert }
|
761
|
+
.to raise_error error_class, error_message
|
762
|
+
end
|
763
|
+
end
|
764
|
+
end
|
765
|
+
|
766
|
+
let(:copy) { subject.invert }
|
767
|
+
|
768
|
+
it { expect(copy).to be_a described_class }
|
769
|
+
|
770
|
+
it { expect(copy.inverted?).to be true }
|
771
|
+
|
772
|
+
include_examples 'should invert the criteria'
|
773
|
+
|
774
|
+
wrap_context 'when initialized with inverted: true' do
|
775
|
+
it { expect(copy).to be_a described_class }
|
776
|
+
|
777
|
+
it { expect(copy.inverted?).to be false }
|
778
|
+
|
779
|
+
include_examples 'should invert the criteria'
|
780
|
+
end
|
781
|
+
end
|
782
|
+
|
783
|
+
describe '#inverted?' do
|
784
|
+
include_examples 'should define predicate', :inverted?, false
|
785
|
+
|
786
|
+
wrap_context 'when initialized with inverted: true' do
|
787
|
+
it { expect(subject.inverted?).to be true }
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
describe '#type' do
|
792
|
+
include_examples 'should define reader', :type, :criteria
|
793
|
+
end
|
794
|
+
|
795
|
+
describe '#with_criteria' do
|
796
|
+
let(:new_criteria) { ['author', 'eq', 'Ursula K. LeGuin'] }
|
797
|
+
|
798
|
+
it { expect(subject).to respond_to(:with_criteria).with(1).argument }
|
799
|
+
|
800
|
+
it 'should return a scope' do
|
801
|
+
expect(subject.with_criteria(new_criteria)).to be_a described_class
|
802
|
+
end
|
803
|
+
|
804
|
+
it "should not change the original scope's criteria" do
|
805
|
+
expect { subject.with_criteria(new_criteria) }
|
806
|
+
.not_to change(subject, :criteria)
|
807
|
+
end
|
808
|
+
|
809
|
+
it "should set the copied scope's criteria" do
|
810
|
+
expect(subject.with_criteria(new_criteria).criteria)
|
811
|
+
.to be == new_criteria
|
812
|
+
end
|
813
|
+
|
814
|
+
it 'should return an uninverted scope' do
|
815
|
+
expect(subject.with_criteria(new_criteria).inverted?).to be false
|
816
|
+
end
|
817
|
+
|
818
|
+
wrap_context 'with criteria' do
|
819
|
+
it "should not change the original scope's criteria" do
|
820
|
+
expect { subject.with_criteria(new_criteria) }
|
821
|
+
.not_to change(subject, :criteria)
|
822
|
+
end
|
823
|
+
|
824
|
+
it "should set the copied scope's criteria" do
|
825
|
+
expect(subject.with_criteria(new_criteria).criteria)
|
826
|
+
.to be == new_criteria
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
wrap_context 'when initialized with inverted: true' do
|
831
|
+
it 'should return an inverted scope' do
|
832
|
+
expect(subject.with_criteria(new_criteria).inverted?).to be true
|
833
|
+
end
|
834
|
+
end
|
835
|
+
end
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
# Contract validating the scope filters data based on the criteria.
|
840
|
+
module ShouldFilterDataByCriteriaContract
|
841
|
+
extend RSpec::SleepingKingStudios::Contract
|
842
|
+
|
843
|
+
# @!method apply(example_group, **options)
|
844
|
+
# Adds the contract to the example group.
|
845
|
+
#
|
846
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
847
|
+
# which the contract is applied.
|
848
|
+
# @param options [Hash] additional options for the contract.
|
849
|
+
#
|
850
|
+
# @option options ignore_invalid [Boolean] if true, skips tests for
|
851
|
+
# handling invalid operators. Defaults to false.
|
852
|
+
# @option options inverted [Boolean] if true, filters data that does not
|
853
|
+
# match the criteria. Defaults to false.
|
854
|
+
contract do |ignore_invalid: false, inverted: false|
|
855
|
+
shared_context 'with data' do
|
856
|
+
let(:data) do
|
857
|
+
Cuprum::Collections::RSpec::Fixtures::BOOKS_FIXTURES
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
861
|
+
let(:matching) { data }
|
862
|
+
let(:expected) { inverted ? data - matching : matching }
|
863
|
+
|
864
|
+
context 'when the scope has no criteria' do
|
865
|
+
describe 'with empty data' do
|
866
|
+
let(:data) { [] }
|
867
|
+
|
868
|
+
it { expect(filtered_data).to be == [] }
|
869
|
+
end
|
870
|
+
|
871
|
+
wrap_context 'with data' do
|
872
|
+
let(:matching) { data }
|
873
|
+
|
874
|
+
it { expect(filtered_data).to match_array expected }
|
875
|
+
end
|
876
|
+
end
|
877
|
+
|
878
|
+
context 'when the scope has an equality criterion' do
|
879
|
+
let(:criteria) do
|
880
|
+
described_class.parse({ 'title' => 'The Word for World is Forest' })
|
881
|
+
end
|
882
|
+
|
883
|
+
describe 'with empty data' do
|
884
|
+
let(:data) { [] }
|
885
|
+
|
886
|
+
it { expect(filtered_data).to be == [] }
|
887
|
+
end
|
888
|
+
|
889
|
+
wrap_context 'with data' do
|
890
|
+
let(:matching) do
|
891
|
+
data.select do |item|
|
892
|
+
item['title'] == 'The Word for World is Forest'
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
it { expect(filtered_data).to match_array expected }
|
897
|
+
end
|
898
|
+
end
|
899
|
+
|
900
|
+
context 'when the scope has a greater than criterion' do
|
901
|
+
let(:criteria) do
|
902
|
+
described_class.parse do |scope|
|
903
|
+
{ 'published_at' => scope.greater_than('1972-03-13') }
|
904
|
+
end
|
905
|
+
end
|
906
|
+
|
907
|
+
describe 'with empty data' do
|
908
|
+
let(:data) { [] }
|
909
|
+
|
910
|
+
it { expect(filtered_data).to be == [] }
|
911
|
+
end
|
912
|
+
|
913
|
+
wrap_context 'with data' do
|
914
|
+
let(:matching) do
|
915
|
+
data.select do |item|
|
916
|
+
item['published_at'] > '1972-03-13'
|
917
|
+
end
|
918
|
+
end
|
919
|
+
|
920
|
+
it { expect(filtered_data).to match_array expected }
|
921
|
+
end
|
922
|
+
end
|
923
|
+
|
924
|
+
context 'when the scope has a greater than or equal to criterion' do
|
925
|
+
let(:criteria) do
|
926
|
+
described_class.parse do |scope|
|
927
|
+
{ 'published_at' => scope.greater_than_or_equal_to('1972-03-13') }
|
928
|
+
end
|
929
|
+
end
|
930
|
+
|
931
|
+
describe 'with empty data' do
|
932
|
+
let(:data) { [] }
|
933
|
+
|
934
|
+
it { expect(filtered_data).to be == [] }
|
935
|
+
end
|
936
|
+
|
937
|
+
wrap_context 'with data' do
|
938
|
+
let(:matching) do
|
939
|
+
data.select do |item|
|
940
|
+
item['published_at'] >= '1972-03-13'
|
941
|
+
end
|
942
|
+
end
|
943
|
+
|
944
|
+
it { expect(filtered_data).to match_array expected }
|
945
|
+
end
|
946
|
+
end
|
947
|
+
|
948
|
+
context 'when the scope has a less than criterion' do
|
949
|
+
let(:criteria) do
|
950
|
+
described_class.parse do |scope|
|
951
|
+
{ 'published_at' => scope.less_than('1972-03-13') }
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
describe 'with empty data' do
|
956
|
+
let(:data) { [] }
|
957
|
+
|
958
|
+
it { expect(filtered_data).to be == [] }
|
959
|
+
end
|
960
|
+
|
961
|
+
wrap_context 'with data' do
|
962
|
+
let(:matching) do
|
963
|
+
data.select do |item|
|
964
|
+
item['published_at'] < '1972-03-13'
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
968
|
+
it { expect(filtered_data).to match_array expected }
|
969
|
+
end
|
970
|
+
end
|
971
|
+
|
972
|
+
context 'when the scope has a less than or equal to criterion' do
|
973
|
+
let(:criteria) do
|
974
|
+
described_class.parse do |scope|
|
975
|
+
{ 'published_at' => scope.less_than_or_equal_to('1972-03-13') }
|
976
|
+
end
|
977
|
+
end
|
978
|
+
|
979
|
+
describe 'with empty data' do
|
980
|
+
let(:data) { [] }
|
981
|
+
|
982
|
+
it { expect(filtered_data).to be == [] }
|
983
|
+
end
|
984
|
+
|
985
|
+
wrap_context 'with data' do
|
986
|
+
let(:matching) do
|
987
|
+
data.select do |item|
|
988
|
+
item['published_at'] <= '1972-03-13'
|
989
|
+
end
|
990
|
+
end
|
991
|
+
|
992
|
+
it { expect(filtered_data).to match_array expected }
|
993
|
+
end
|
994
|
+
end
|
995
|
+
|
996
|
+
context 'when the scope has a not equal criterion' do
|
997
|
+
let(:criteria) do
|
998
|
+
described_class.parse do |scope|
|
999
|
+
{ 'author' => scope.not_equal('J.R.R. Tolkien') }
|
1000
|
+
end
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
describe 'with empty data' do
|
1004
|
+
let(:data) { [] }
|
1005
|
+
|
1006
|
+
it { expect(filtered_data).to be == [] }
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
wrap_context 'with data' do
|
1010
|
+
let(:matching) do
|
1011
|
+
data.reject do |item|
|
1012
|
+
item['author'] == 'J.R.R. Tolkien'
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
it { expect(filtered_data).to match_array expected }
|
1017
|
+
end
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
context 'when the scope has a not one of criterion' do
|
1021
|
+
let(:criteria) do
|
1022
|
+
described_class.parse do |scope|
|
1023
|
+
titles = ['The Fellowship Of The Ring', 'The Two Towers']
|
1024
|
+
|
1025
|
+
{ 'title' => scope.not_one_of(titles) }
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
describe 'with empty data' do
|
1030
|
+
let(:data) { [] }
|
1031
|
+
|
1032
|
+
it { expect(filtered_data).to be == [] }
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
wrap_context 'with data' do
|
1036
|
+
let(:matching) do
|
1037
|
+
data.reject do |item|
|
1038
|
+
['The Fellowship Of The Ring', 'The Two Towers']
|
1039
|
+
.include?(item['title'])
|
1040
|
+
end
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
it { expect(filtered_data).to match_array expected }
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
context 'when the scope has a one of criterion' do
|
1048
|
+
let(:criteria) do
|
1049
|
+
described_class.parse do |scope|
|
1050
|
+
titles = ['The Fellowship Of The Ring', 'The Two Towers']
|
1051
|
+
|
1052
|
+
{ 'title' => scope.one_of(titles) }
|
1053
|
+
end
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
describe 'with empty data' do
|
1057
|
+
let(:data) { [] }
|
1058
|
+
|
1059
|
+
it { expect(filtered_data).to be == [] }
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
wrap_context 'with data' do
|
1063
|
+
let(:matching) do
|
1064
|
+
data.select do |item|
|
1065
|
+
['The Fellowship Of The Ring', 'The Two Towers']
|
1066
|
+
.include?(item['title'])
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
it { expect(filtered_data).to match_array expected }
|
1071
|
+
end
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
context 'when the scope has mixed criteria' do
|
1075
|
+
let(:criteria) do
|
1076
|
+
described_class.parse do |scope|
|
1077
|
+
titles = ['The Fellowship Of The Ring', 'The Two Towers']
|
1078
|
+
|
1079
|
+
{
|
1080
|
+
'author' => 'J.R.R. Tolkien',
|
1081
|
+
'title' => scope.not_one_of(titles)
|
1082
|
+
}
|
1083
|
+
end
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
describe 'with empty data' do
|
1087
|
+
let(:data) { [] }
|
1088
|
+
|
1089
|
+
it { expect(filtered_data).to be == [] }
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
wrap_context 'with data' do
|
1093
|
+
let(:matching) do
|
1094
|
+
data
|
1095
|
+
.select { |item| item['author'] == 'J.R.R. Tolkien' }
|
1096
|
+
.reject do |item|
|
1097
|
+
['The Fellowship Of The Ring', 'The Two Towers']
|
1098
|
+
.include?(item['title'])
|
1099
|
+
end
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
it { expect(filtered_data).to match_array expected }
|
1103
|
+
end
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
context 'when the scope has multiple matching criteria' do
|
1107
|
+
let(:criteria) do
|
1108
|
+
described_class.parse({
|
1109
|
+
'author' => 'J.R.R. Tolkien',
|
1110
|
+
'series' => 'The Lord of the Rings'
|
1111
|
+
})
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
describe 'with empty data' do
|
1115
|
+
let(:data) { [] }
|
1116
|
+
|
1117
|
+
it { expect(filtered_data).to be == [] }
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
wrap_context 'with data' do
|
1121
|
+
let(:matching) do
|
1122
|
+
data
|
1123
|
+
.select { |item| item['author'] == 'J.R.R. Tolkien' }
|
1124
|
+
.select { |item| item['series'] == 'The Lord of the Rings' }
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
it { expect(filtered_data).to match_array expected }
|
1128
|
+
end
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
context 'when the scope has non-overlapping criteria' do
|
1132
|
+
let(:criteria) do
|
1133
|
+
described_class.parse({
|
1134
|
+
'author' => 'J.R.R. Tolkien',
|
1135
|
+
'series' => 'Earthsea'
|
1136
|
+
})
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
describe 'with empty data' do
|
1140
|
+
let(:data) { [] }
|
1141
|
+
|
1142
|
+
it { expect(filtered_data).to be == [] }
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
wrap_context 'with data' do
|
1146
|
+
let(:matching) do
|
1147
|
+
data
|
1148
|
+
.select { |item| item['author'] == 'J.R.R. Tolkien' }
|
1149
|
+
.select { |item| item['series'] == 'Earthsea' }
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
it { expect(filtered_data).to match_array expected }
|
1153
|
+
end
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
context 'when the scope has invalid operators' do
|
1157
|
+
next if ignore_invalid
|
1158
|
+
|
1159
|
+
let(:criteria) { [['title', :random, nil]] }
|
1160
|
+
let(:error_class) do
|
1161
|
+
Cuprum::Collections::Queries::UnknownOperatorException
|
1162
|
+
end
|
1163
|
+
let(:error_message) do
|
1164
|
+
'unknown operator "random"'
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
describe 'with empty data' do
|
1168
|
+
let(:data) { [] }
|
1169
|
+
|
1170
|
+
it 'should return an empty result or raise an exception',
|
1171
|
+
:aggregate_failures \
|
1172
|
+
do
|
1173
|
+
# :nocov:
|
1174
|
+
begin
|
1175
|
+
actual = filtered_data
|
1176
|
+
rescue StandardError => exception
|
1177
|
+
expect(exception).to be_a error_class
|
1178
|
+
expect(exception.message).to be == error_message
|
1179
|
+
|
1180
|
+
next
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
expect(actual).to be == []
|
1184
|
+
# :nocov:
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
wrap_context 'with data' do
|
1189
|
+
it 'should raise an exception' do
|
1190
|
+
expect { filtered_data }
|
1191
|
+
.to raise_error error_class, error_message
|
1192
|
+
end
|
1193
|
+
end
|
1194
|
+
end
|
1195
|
+
end
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
# Contract validating the parsing of criteria.
|
1199
|
+
module ShouldParseCriteriaContract
|
1200
|
+
extend RSpec::SleepingKingStudios::Contract
|
1201
|
+
|
1202
|
+
# @!method apply(example_group)
|
1203
|
+
# Adds the contract to the example group.
|
1204
|
+
#
|
1205
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
1206
|
+
# which the contract is applied.
|
1207
|
+
contract do
|
1208
|
+
describe 'with a block' do
|
1209
|
+
include_contract 'should parse criteria from a block'
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
describe 'with a hash' do
|
1213
|
+
include_contract 'should parse criteria from a hash'
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
describe 'with a hash and a block' do
|
1217
|
+
let(:operators) { Cuprum::Collections::Queries::Operators }
|
1218
|
+
let(:block) do
|
1219
|
+
lambda do |scope|
|
1220
|
+
{ 'author' => scope.one_of('J.R.R. Tolkien', 'Ursula K. LeGuin') }
|
1221
|
+
end
|
1222
|
+
end
|
1223
|
+
let(:value) do
|
1224
|
+
{ 'category' => 'Science Fiction and Fantasy' }
|
1225
|
+
end
|
1226
|
+
let(:expected) do
|
1227
|
+
[
|
1228
|
+
[
|
1229
|
+
'category',
|
1230
|
+
operators::EQUAL,
|
1231
|
+
'Science Fiction and Fantasy'
|
1232
|
+
],
|
1233
|
+
[
|
1234
|
+
'author',
|
1235
|
+
operators::ONE_OF,
|
1236
|
+
['J.R.R. Tolkien', 'Ursula K. LeGuin']
|
1237
|
+
]
|
1238
|
+
]
|
1239
|
+
end
|
1240
|
+
|
1241
|
+
it 'should parse the criteria' do
|
1242
|
+
expect(parse_criteria(value, &block)).to be == expected
|
1243
|
+
end
|
1244
|
+
end
|
1245
|
+
end
|
1246
|
+
end
|
1247
|
+
|
1248
|
+
# Contract validating the parsing of criteria from a Hash.
|
1249
|
+
module ShouldParseCriteriaFromABlockContract
|
1250
|
+
extend RSpec::SleepingKingStudios::Contract
|
1251
|
+
|
1252
|
+
# @!method apply(example_group)
|
1253
|
+
# Adds the contract to the example group.
|
1254
|
+
#
|
1255
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
1256
|
+
# which the contract is applied.
|
1257
|
+
contract do
|
1258
|
+
let(:operators) { Cuprum::Collections::Queries::Operators }
|
1259
|
+
|
1260
|
+
describe 'without a block' do
|
1261
|
+
let(:error_message) { 'no block given' }
|
1262
|
+
|
1263
|
+
it 'should raise an exception' do
|
1264
|
+
expect { parse_criteria }
|
1265
|
+
.to raise_error ArgumentError, error_message
|
1266
|
+
end
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
describe 'with a block returning nil' do
|
1270
|
+
let(:error_message) do
|
1271
|
+
'value must be a Hash with String or Symbol keys'
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
it 'should raise an exception' do
|
1275
|
+
expect { parse_criteria { nil } }
|
1276
|
+
.to raise_error ArgumentError, error_message
|
1277
|
+
end
|
1278
|
+
end
|
1279
|
+
|
1280
|
+
describe 'with a block returning an Object' do
|
1281
|
+
let(:error_message) do
|
1282
|
+
'value must be a Hash with String or Symbol keys'
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
it 'should raise an exception' do
|
1286
|
+
expect { parse_criteria { Object.new.freeze } }
|
1287
|
+
.to raise_error ArgumentError, error_message
|
1288
|
+
end
|
1289
|
+
end
|
1290
|
+
|
1291
|
+
describe 'with a block returning a Hash with invalid keys' do
|
1292
|
+
let(:error_message) do
|
1293
|
+
'value must be a Hash with String or Symbol keys'
|
1294
|
+
end
|
1295
|
+
let(:block) { -> { { nil => 'invalid' } } }
|
1296
|
+
|
1297
|
+
it 'should raise an exception' do
|
1298
|
+
expect { parse_criteria(&block) }
|
1299
|
+
.to raise_error ArgumentError, error_message
|
1300
|
+
end
|
1301
|
+
end
|
1302
|
+
|
1303
|
+
describe 'with a block returning an empty Hash' do
|
1304
|
+
let(:block) { -> { {} } }
|
1305
|
+
|
1306
|
+
it { expect(parse_criteria(&block)).to be == [] }
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
describe 'with a block returning a Hash with one key' do
|
1310
|
+
let(:operators) { Cuprum::Collections::Queries::Operators }
|
1311
|
+
let(:block) do
|
1312
|
+
-> { { 'title' => 'A Wizard of Earthsea' } }
|
1313
|
+
end
|
1314
|
+
let(:expected) do
|
1315
|
+
[['title', operators::EQUAL, 'A Wizard of Earthsea']]
|
1316
|
+
end
|
1317
|
+
|
1318
|
+
it { expect(parse_criteria(&block)).to be == expected }
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
describe 'with a Hash with many String keys' do
|
1322
|
+
let(:operators) { Cuprum::Collections::Queries::Operators }
|
1323
|
+
let(:block) do
|
1324
|
+
lambda do
|
1325
|
+
{
|
1326
|
+
'title' => 'A Wizard of Earthsea',
|
1327
|
+
'author' => 'Ursula K. LeGuin',
|
1328
|
+
'series' => 'Earthsea'
|
1329
|
+
}
|
1330
|
+
end
|
1331
|
+
end
|
1332
|
+
let(:expected) do
|
1333
|
+
[
|
1334
|
+
['title', operators::EQUAL, 'A Wizard of Earthsea'],
|
1335
|
+
['author', operators::EQUAL, 'Ursula K. LeGuin'],
|
1336
|
+
['series', operators::EQUAL, 'Earthsea']
|
1337
|
+
]
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
it { expect(parse_criteria(&block)).to be == expected }
|
1341
|
+
end
|
1342
|
+
|
1343
|
+
describe 'with a Hash with many Symbol keys' do
|
1344
|
+
let(:operators) { Cuprum::Collections::Queries::Operators }
|
1345
|
+
let(:block) do
|
1346
|
+
lambda do
|
1347
|
+
{
|
1348
|
+
title: 'A Wizard of Earthsea',
|
1349
|
+
author: 'Ursula K. LeGuin',
|
1350
|
+
series: 'Earthsea'
|
1351
|
+
}
|
1352
|
+
end
|
1353
|
+
end
|
1354
|
+
let(:expected) do
|
1355
|
+
[
|
1356
|
+
['title', operators::EQUAL, 'A Wizard of Earthsea'],
|
1357
|
+
['author', operators::EQUAL, 'Ursula K. LeGuin'],
|
1358
|
+
['series', operators::EQUAL, 'Earthsea']
|
1359
|
+
]
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
it { expect(parse_criteria(&block)).to be == expected }
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
describe 'with a Hash with an "eq" operator' do
|
1366
|
+
let(:block) do
|
1367
|
+
->(scope) { { 'title' => scope.eq('A Wizard of Earthsea') } }
|
1368
|
+
end
|
1369
|
+
let(:expected) do
|
1370
|
+
[['title', operators::EQUAL, 'A Wizard of Earthsea']]
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
it 'should parse the criteria' do
|
1374
|
+
expect(parse_criteria(&block)).to be == expected
|
1375
|
+
end
|
1376
|
+
end
|
1377
|
+
|
1378
|
+
describe 'with a Hash with an "equal" operator' do
|
1379
|
+
let(:block) do
|
1380
|
+
->(scope) { { 'title' => scope.equal('A Wizard of Earthsea') } }
|
1381
|
+
end
|
1382
|
+
let(:expected) do
|
1383
|
+
[['title', operators::EQUAL, 'A Wizard of Earthsea']]
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
it 'should parse the criteria' do
|
1387
|
+
expect(parse_criteria(&block)).to be == expected
|
1388
|
+
end
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
describe 'with a Hash with an "equals" operator' do
|
1392
|
+
let(:block) do
|
1393
|
+
->(scope) { { 'title' => scope.equals('A Wizard of Earthsea') } }
|
1394
|
+
end
|
1395
|
+
let(:expected) do
|
1396
|
+
[['title', operators::EQUAL, 'A Wizard of Earthsea']]
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
it 'should parse the criteria' do
|
1400
|
+
expect(parse_criteria(&block)).to be == expected
|
1401
|
+
end
|
1402
|
+
end
|
1403
|
+
|
1404
|
+
describe 'with a Hash with a "greater_than" operator' do
|
1405
|
+
let(:block) do
|
1406
|
+
->(scope) { { 'published_at' => scope.greater_than('1970-01-01') } }
|
1407
|
+
end
|
1408
|
+
let(:expected) do
|
1409
|
+
[['published_at', operators::GREATER_THAN, '1970-01-01']]
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
it 'should parse the criteria' do
|
1413
|
+
expect(parse_criteria(&block)).to be == expected
|
1414
|
+
end
|
1415
|
+
end
|
1416
|
+
|
1417
|
+
describe 'with a Hash with a "greater_than_or_equal_to" operator' do
|
1418
|
+
let(:block) do
|
1419
|
+
lambda do |scope|
|
1420
|
+
{ 'published_at' => scope.greater_than_or_equal_to('1970-01-01') }
|
1421
|
+
end
|
1422
|
+
end
|
1423
|
+
let(:expected) do
|
1424
|
+
[
|
1425
|
+
[
|
1426
|
+
'published_at',
|
1427
|
+
operators::GREATER_THAN_OR_EQUAL_TO,
|
1428
|
+
'1970-01-01'
|
1429
|
+
]
|
1430
|
+
]
|
1431
|
+
end
|
1432
|
+
|
1433
|
+
it 'should parse the criteria' do
|
1434
|
+
expect(parse_criteria(&block)).to be == expected
|
1435
|
+
end
|
1436
|
+
end
|
1437
|
+
|
1438
|
+
describe 'with a Hash with a "gt" operator' do
|
1439
|
+
let(:block) do
|
1440
|
+
->(scope) { { 'published_at' => scope.gt('1970-01-01') } }
|
1441
|
+
end
|
1442
|
+
let(:expected) do
|
1443
|
+
[['published_at', operators::GREATER_THAN, '1970-01-01']]
|
1444
|
+
end
|
1445
|
+
|
1446
|
+
it 'should parse the criteria' do
|
1447
|
+
expect(parse_criteria(&block)).to be == expected
|
1448
|
+
end
|
1449
|
+
end
|
1450
|
+
|
1451
|
+
describe 'with a Hash with a "gte" operator' do
|
1452
|
+
let(:block) do
|
1453
|
+
->(scope) { { 'published_at' => scope.gte('1970-01-01') } }
|
1454
|
+
end
|
1455
|
+
let(:expected) do
|
1456
|
+
[
|
1457
|
+
[
|
1458
|
+
'published_at',
|
1459
|
+
operators::GREATER_THAN_OR_EQUAL_TO,
|
1460
|
+
'1970-01-01'
|
1461
|
+
]
|
1462
|
+
]
|
1463
|
+
end
|
1464
|
+
|
1465
|
+
it 'should parse the criteria' do
|
1466
|
+
expect(parse_criteria(&block)).to be == expected
|
1467
|
+
end
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
describe 'with a Hash with a "less_than" operator' do
|
1471
|
+
let(:block) do
|
1472
|
+
->(scope) { { 'published_at' => scope.less_than('1970-01-01') } }
|
1473
|
+
end
|
1474
|
+
let(:expected) do
|
1475
|
+
[['published_at', operators::LESS_THAN, '1970-01-01']]
|
1476
|
+
end
|
1477
|
+
|
1478
|
+
it 'should parse the criteria' do
|
1479
|
+
expect(parse_criteria(&block)).to be == expected
|
1480
|
+
end
|
1481
|
+
end
|
1482
|
+
|
1483
|
+
describe 'with a Hash with a "less_than_or_equal_to" operator' do
|
1484
|
+
let(:block) do
|
1485
|
+
lambda do |scope|
|
1486
|
+
{ 'published_at' => scope.less_than_or_equal_to('1970-01-01') }
|
1487
|
+
end
|
1488
|
+
end
|
1489
|
+
let(:expected) do
|
1490
|
+
[['published_at', operators::LESS_THAN_OR_EQUAL_TO, '1970-01-01']]
|
1491
|
+
end
|
1492
|
+
|
1493
|
+
it 'should parse the criteria' do
|
1494
|
+
expect(parse_criteria(&block)).to be == expected
|
1495
|
+
end
|
1496
|
+
end
|
1497
|
+
|
1498
|
+
describe 'with a Hash with a "lt" operator' do
|
1499
|
+
let(:block) do
|
1500
|
+
->(scope) { { 'published_at' => scope.lt('1970-01-01') } }
|
1501
|
+
end
|
1502
|
+
let(:expected) do
|
1503
|
+
[['published_at', operators::LESS_THAN, '1970-01-01']]
|
1504
|
+
end
|
1505
|
+
|
1506
|
+
it 'should parse the criteria' do
|
1507
|
+
expect(parse_criteria(&block)).to be == expected
|
1508
|
+
end
|
1509
|
+
end
|
1510
|
+
|
1511
|
+
describe 'with a Hash with a "lte" operator' do
|
1512
|
+
let(:block) do
|
1513
|
+
->(scope) { { 'published_at' => scope.lte('1970-01-01') } }
|
1514
|
+
end
|
1515
|
+
let(:expected) do
|
1516
|
+
[['published_at', operators::LESS_THAN_OR_EQUAL_TO, '1970-01-01']]
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
it 'should parse the criteria' do
|
1520
|
+
expect(parse_criteria(&block)).to be == expected
|
1521
|
+
end
|
1522
|
+
end
|
1523
|
+
|
1524
|
+
describe 'with a Hash with a "ne" operator' do
|
1525
|
+
let(:block) do
|
1526
|
+
->(scope) { { 'series' => scope.ne('Earthsea') } }
|
1527
|
+
end
|
1528
|
+
let(:expected) do
|
1529
|
+
[['series', operators::NOT_EQUAL, 'Earthsea']]
|
1530
|
+
end
|
1531
|
+
|
1532
|
+
it 'should parse the criteria' do
|
1533
|
+
expect(parse_criteria(&block)).to be == expected
|
1534
|
+
end
|
1535
|
+
end
|
1536
|
+
|
1537
|
+
describe 'with a Hash with a "not_equal" operator' do
|
1538
|
+
let(:block) do
|
1539
|
+
->(scope) { { 'series' => scope.not_equal('Earthsea') } }
|
1540
|
+
end
|
1541
|
+
let(:expected) do
|
1542
|
+
[['series', operators::NOT_EQUAL, 'Earthsea']]
|
1543
|
+
end
|
1544
|
+
|
1545
|
+
it 'should parse the criteria' do
|
1546
|
+
expect(parse_criteria(&block)).to be == expected
|
1547
|
+
end
|
1548
|
+
end
|
1549
|
+
|
1550
|
+
describe 'with a Hash with a "not_one_of" operator and an Array' do
|
1551
|
+
let(:block) do
|
1552
|
+
lambda do |scope|
|
1553
|
+
{
|
1554
|
+
'series' => scope.not_one_of(
|
1555
|
+
['Earthsea', 'The Lord of the Rings']
|
1556
|
+
)
|
1557
|
+
}
|
1558
|
+
end
|
1559
|
+
end
|
1560
|
+
let(:expected) do
|
1561
|
+
[
|
1562
|
+
[
|
1563
|
+
'series',
|
1564
|
+
operators::NOT_ONE_OF,
|
1565
|
+
['Earthsea', 'The Lord of the Rings']
|
1566
|
+
]
|
1567
|
+
]
|
1568
|
+
end
|
1569
|
+
|
1570
|
+
it 'should parse the criteria' do
|
1571
|
+
expect(parse_criteria(&block)).to be == expected
|
1572
|
+
end
|
1573
|
+
end
|
1574
|
+
|
1575
|
+
describe 'with a Hash with a "not_one_of" operator and values' do
|
1576
|
+
let(:block) do
|
1577
|
+
lambda do |scope|
|
1578
|
+
{
|
1579
|
+
'series' => scope.not_one_of(
|
1580
|
+
'Earthsea',
|
1581
|
+
'The Lord of the Rings'
|
1582
|
+
)
|
1583
|
+
}
|
1584
|
+
end
|
1585
|
+
end
|
1586
|
+
let(:expected) do
|
1587
|
+
[
|
1588
|
+
[
|
1589
|
+
'series',
|
1590
|
+
operators::NOT_ONE_OF,
|
1591
|
+
['Earthsea', 'The Lord of the Rings']
|
1592
|
+
]
|
1593
|
+
]
|
1594
|
+
end
|
1595
|
+
|
1596
|
+
it 'should parse the criteria' do
|
1597
|
+
expect(parse_criteria(&block)).to be == expected
|
1598
|
+
end
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
describe 'with a Hash with a "one_of" operator and an Array' do
|
1602
|
+
let(:block) do
|
1603
|
+
lambda do |scope|
|
1604
|
+
{
|
1605
|
+
'series' => scope.one_of(
|
1606
|
+
['Earthsea', 'The Lord of the Rings']
|
1607
|
+
)
|
1608
|
+
}
|
1609
|
+
end
|
1610
|
+
end
|
1611
|
+
let(:expected) do
|
1612
|
+
[
|
1613
|
+
[
|
1614
|
+
'series',
|
1615
|
+
operators::ONE_OF,
|
1616
|
+
['Earthsea', 'The Lord of the Rings']
|
1617
|
+
]
|
1618
|
+
]
|
1619
|
+
end
|
1620
|
+
|
1621
|
+
it 'should parse the criteria' do
|
1622
|
+
expect(parse_criteria(&block)).to be == expected
|
1623
|
+
end
|
1624
|
+
end
|
1625
|
+
|
1626
|
+
describe 'with a Hash with a "one_of" operator and values' do
|
1627
|
+
let(:block) do
|
1628
|
+
lambda do |scope|
|
1629
|
+
{
|
1630
|
+
'series' => scope.one_of(
|
1631
|
+
'Earthsea',
|
1632
|
+
'The Lord of the Rings'
|
1633
|
+
)
|
1634
|
+
}
|
1635
|
+
end
|
1636
|
+
end
|
1637
|
+
let(:expected) do
|
1638
|
+
[
|
1639
|
+
[
|
1640
|
+
'series',
|
1641
|
+
operators::ONE_OF,
|
1642
|
+
['Earthsea', 'The Lord of the Rings']
|
1643
|
+
]
|
1644
|
+
]
|
1645
|
+
end
|
1646
|
+
|
1647
|
+
it 'should parse the criteria' do
|
1648
|
+
expect(parse_criteria(&block)).to be == expected
|
1649
|
+
end
|
1650
|
+
end
|
1651
|
+
|
1652
|
+
describe 'with a Hash with an unknown operator' do
|
1653
|
+
let(:error_class) do
|
1654
|
+
Cuprum::Collections::Queries::UnknownOperatorException
|
1655
|
+
end
|
1656
|
+
let(:error_message) do
|
1657
|
+
'unknown operator "random"'
|
1658
|
+
end
|
1659
|
+
let(:block) do
|
1660
|
+
-> { { 'genre' => random('Science Fiction', 'Fantasy') } }
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
it 'should raise an exception' do
|
1664
|
+
expect { parse_criteria(&block) }
|
1665
|
+
.to raise_error error_class, error_message
|
1666
|
+
end
|
1667
|
+
|
1668
|
+
it 'should preserve the original exception', :aggregate_failures do
|
1669
|
+
parse_criteria(&block)
|
1670
|
+
rescue error_class => exception
|
1671
|
+
expect(exception.cause).to be_a NameError
|
1672
|
+
expect(exception.name).to be == :random
|
1673
|
+
end
|
1674
|
+
end
|
1675
|
+
|
1676
|
+
describe 'with a Hash with multiple operators' do
|
1677
|
+
let(:block) do
|
1678
|
+
lambda do |scope|
|
1679
|
+
{
|
1680
|
+
'published_at' => scope.greater_than('1970-01-01'),
|
1681
|
+
'series' => scope.one_of(
|
1682
|
+
['Earthsea', 'The Lord of the Rings']
|
1683
|
+
),
|
1684
|
+
'title' => scope.not_equal('The Tombs of Atuan')
|
1685
|
+
}
|
1686
|
+
end
|
1687
|
+
end
|
1688
|
+
let(:expected) do
|
1689
|
+
[
|
1690
|
+
[
|
1691
|
+
'published_at',
|
1692
|
+
operators::GREATER_THAN,
|
1693
|
+
'1970-01-01'
|
1694
|
+
],
|
1695
|
+
[
|
1696
|
+
'series',
|
1697
|
+
operators::ONE_OF,
|
1698
|
+
['Earthsea', 'The Lord of the Rings']
|
1699
|
+
],
|
1700
|
+
[
|
1701
|
+
'title',
|
1702
|
+
operators::NOT_EQUAL,
|
1703
|
+
'The Tombs of Atuan'
|
1704
|
+
]
|
1705
|
+
]
|
1706
|
+
end
|
1707
|
+
|
1708
|
+
it 'should parse the criteria' do
|
1709
|
+
expect(parse_criteria(&block)).to be == expected
|
1710
|
+
end
|
1711
|
+
end
|
1712
|
+
|
1713
|
+
describe 'with a Hash with mixed keys and operators' do
|
1714
|
+
let(:block) do
|
1715
|
+
lambda do |scope|
|
1716
|
+
{
|
1717
|
+
'author' => scope.one_of(
|
1718
|
+
'J.R.R. Tolkien',
|
1719
|
+
'Ursula K. LeGuin'
|
1720
|
+
),
|
1721
|
+
'category' => 'Science Fiction and Fantasy'
|
1722
|
+
}
|
1723
|
+
end
|
1724
|
+
end
|
1725
|
+
let(:expected) do
|
1726
|
+
[
|
1727
|
+
[
|
1728
|
+
'author',
|
1729
|
+
operators::ONE_OF,
|
1730
|
+
['J.R.R. Tolkien', 'Ursula K. LeGuin']
|
1731
|
+
],
|
1732
|
+
[
|
1733
|
+
'category',
|
1734
|
+
operators::EQUAL,
|
1735
|
+
'Science Fiction and Fantasy'
|
1736
|
+
]
|
1737
|
+
]
|
1738
|
+
end
|
1739
|
+
|
1740
|
+
it 'should parse the criteria' do
|
1741
|
+
expect(parse_criteria(&block)).to be == expected
|
1742
|
+
end
|
1743
|
+
end
|
1744
|
+
|
1745
|
+
# @deprecated v0.5.0 Implicit receivers are deprecated.
|
1746
|
+
describe 'with a block with legacy implicit receiver' do
|
1747
|
+
shared_examples 'should print a deprecation warning for' \
|
1748
|
+
do |operator_name|
|
1749
|
+
let(:deprecation_warning) do
|
1750
|
+
'#parse with implicit receiver'
|
1751
|
+
end
|
1752
|
+
let(:deprecation_message) do
|
1753
|
+
'Pass a block with one parameter to #parse: { |scope| { scope' \
|
1754
|
+
".#{operator_name}: value } }"
|
1755
|
+
end
|
1756
|
+
|
1757
|
+
it 'should print a deprecation warning' do
|
1758
|
+
parse_criteria(&block)
|
1759
|
+
|
1760
|
+
expect(tools.core_tools)
|
1761
|
+
.to have_received(:deprecate)
|
1762
|
+
.with(deprecation_warning, message: deprecation_message)
|
1763
|
+
end
|
1764
|
+
end
|
1765
|
+
|
1766
|
+
def tools
|
1767
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
1768
|
+
end
|
1769
|
+
|
1770
|
+
before(:example) do
|
1771
|
+
allow(tools.core_tools).to receive(:deprecate)
|
1772
|
+
end
|
1773
|
+
|
1774
|
+
describe 'with a Hash with an "eq" operator' do
|
1775
|
+
let(:block) do
|
1776
|
+
-> { { 'title' => eq('A Wizard of Earthsea') } }
|
1777
|
+
end
|
1778
|
+
let(:expected) do
|
1779
|
+
[['title', operators::EQUAL, 'A Wizard of Earthsea']]
|
1780
|
+
end
|
1781
|
+
|
1782
|
+
it 'should parse the criteria' do
|
1783
|
+
expect(parse_criteria(&block)).to be == expected
|
1784
|
+
end
|
1785
|
+
|
1786
|
+
include_examples 'should print a deprecation warning for', 'eq'
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
describe 'with a Hash with an "equal" operator' do
|
1790
|
+
let(:block) do
|
1791
|
+
-> { { 'title' => equal('A Wizard of Earthsea') } }
|
1792
|
+
end
|
1793
|
+
let(:expected) do
|
1794
|
+
[['title', operators::EQUAL, 'A Wizard of Earthsea']]
|
1795
|
+
end
|
1796
|
+
|
1797
|
+
it 'should parse the criteria' do
|
1798
|
+
expect(parse_criteria(&block)).to be == expected
|
1799
|
+
end
|
1800
|
+
|
1801
|
+
include_examples 'should print a deprecation warning for', 'equal'
|
1802
|
+
end
|
1803
|
+
|
1804
|
+
describe 'with a Hash with an "equals" operator' do
|
1805
|
+
let(:block) do
|
1806
|
+
-> { { 'title' => equals('A Wizard of Earthsea') } }
|
1807
|
+
end
|
1808
|
+
let(:expected) do
|
1809
|
+
[['title', operators::EQUAL, 'A Wizard of Earthsea']]
|
1810
|
+
end
|
1811
|
+
|
1812
|
+
it 'should parse the criteria' do
|
1813
|
+
expect(parse_criteria(&block)).to be == expected
|
1814
|
+
end
|
1815
|
+
|
1816
|
+
include_examples 'should print a deprecation warning for', 'equals'
|
1817
|
+
end
|
1818
|
+
|
1819
|
+
describe 'with a Hash with a "greater_than" operator' do
|
1820
|
+
let(:block) do
|
1821
|
+
-> { { 'published_at' => greater_than('1970-01-01') } }
|
1822
|
+
end
|
1823
|
+
let(:expected) do
|
1824
|
+
[['published_at', operators::GREATER_THAN, '1970-01-01']]
|
1825
|
+
end
|
1826
|
+
|
1827
|
+
it 'should parse the criteria' do
|
1828
|
+
expect(parse_criteria(&block)).to be == expected
|
1829
|
+
end
|
1830
|
+
|
1831
|
+
include_examples 'should print a deprecation warning for',
|
1832
|
+
'greater_than'
|
1833
|
+
end
|
1834
|
+
|
1835
|
+
describe 'with a Hash with a "greater_than_or_equal_to" operator' do
|
1836
|
+
let(:block) do
|
1837
|
+
lambda do
|
1838
|
+
{ 'published_at' => greater_than_or_equal_to('1970-01-01') }
|
1839
|
+
end
|
1840
|
+
end
|
1841
|
+
let(:expected) do
|
1842
|
+
[
|
1843
|
+
[
|
1844
|
+
'published_at',
|
1845
|
+
operators::GREATER_THAN_OR_EQUAL_TO,
|
1846
|
+
'1970-01-01'
|
1847
|
+
]
|
1848
|
+
]
|
1849
|
+
end
|
1850
|
+
|
1851
|
+
it 'should parse the criteria' do
|
1852
|
+
expect(parse_criteria(&block)).to be == expected
|
1853
|
+
end
|
1854
|
+
|
1855
|
+
include_examples 'should print a deprecation warning for',
|
1856
|
+
'greater_than_or_equal_to'
|
1857
|
+
end
|
1858
|
+
|
1859
|
+
describe 'with a Hash with a "gt" operator' do
|
1860
|
+
let(:block) do
|
1861
|
+
-> { { 'published_at' => gt('1970-01-01') } }
|
1862
|
+
end
|
1863
|
+
let(:expected) do
|
1864
|
+
[['published_at', operators::GREATER_THAN, '1970-01-01']]
|
1865
|
+
end
|
1866
|
+
|
1867
|
+
it 'should parse the criteria' do
|
1868
|
+
expect(parse_criteria(&block)).to be == expected
|
1869
|
+
end
|
1870
|
+
|
1871
|
+
include_examples 'should print a deprecation warning for', 'gt'
|
1872
|
+
end
|
1873
|
+
|
1874
|
+
describe 'with a Hash with a "gte" operator' do
|
1875
|
+
let(:block) do
|
1876
|
+
-> { { 'published_at' => gte('1970-01-01') } }
|
1877
|
+
end
|
1878
|
+
let(:expected) do
|
1879
|
+
[
|
1880
|
+
[
|
1881
|
+
'published_at',
|
1882
|
+
operators::GREATER_THAN_OR_EQUAL_TO,
|
1883
|
+
'1970-01-01'
|
1884
|
+
]
|
1885
|
+
]
|
1886
|
+
end
|
1887
|
+
|
1888
|
+
it 'should parse the criteria' do
|
1889
|
+
expect(parse_criteria(&block)).to be == expected
|
1890
|
+
end
|
1891
|
+
|
1892
|
+
include_examples 'should print a deprecation warning for', 'gte'
|
1893
|
+
end
|
1894
|
+
|
1895
|
+
describe 'with a Hash with a "less_than" operator' do
|
1896
|
+
let(:block) do
|
1897
|
+
-> { { 'published_at' => less_than('1970-01-01') } }
|
1898
|
+
end
|
1899
|
+
let(:expected) do
|
1900
|
+
[['published_at', operators::LESS_THAN, '1970-01-01']]
|
1901
|
+
end
|
1902
|
+
|
1903
|
+
it 'should parse the criteria' do
|
1904
|
+
expect(parse_criteria(&block)).to be == expected
|
1905
|
+
end
|
1906
|
+
|
1907
|
+
include_examples 'should print a deprecation warning for',
|
1908
|
+
'less_than'
|
1909
|
+
end
|
1910
|
+
|
1911
|
+
describe 'with a Hash with a "less_than_or_equal_to" operator' do
|
1912
|
+
let(:block) do
|
1913
|
+
-> { { 'published_at' => less_than_or_equal_to('1970-01-01') } }
|
1914
|
+
end
|
1915
|
+
let(:expected) do
|
1916
|
+
[['published_at', operators::LESS_THAN_OR_EQUAL_TO, '1970-01-01']]
|
1917
|
+
end
|
1918
|
+
|
1919
|
+
it 'should parse the criteria' do
|
1920
|
+
expect(parse_criteria(&block)).to be == expected
|
1921
|
+
end
|
1922
|
+
|
1923
|
+
include_examples 'should print a deprecation warning for',
|
1924
|
+
'less_than_or_equal_to'
|
1925
|
+
end
|
1926
|
+
|
1927
|
+
describe 'with a Hash with a "lt" operator' do
|
1928
|
+
let(:block) do
|
1929
|
+
-> { { 'published_at' => lt('1970-01-01') } }
|
1930
|
+
end
|
1931
|
+
let(:expected) do
|
1932
|
+
[['published_at', operators::LESS_THAN, '1970-01-01']]
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
it 'should parse the criteria' do
|
1936
|
+
expect(parse_criteria(&block)).to be == expected
|
1937
|
+
end
|
1938
|
+
|
1939
|
+
include_examples 'should print a deprecation warning for', 'lt'
|
1940
|
+
end
|
1941
|
+
|
1942
|
+
describe 'with a Hash with a "lte" operator' do
|
1943
|
+
let(:block) do
|
1944
|
+
-> { { 'published_at' => lte('1970-01-01') } }
|
1945
|
+
end
|
1946
|
+
let(:expected) do
|
1947
|
+
[['published_at', operators::LESS_THAN_OR_EQUAL_TO, '1970-01-01']]
|
1948
|
+
end
|
1949
|
+
|
1950
|
+
it 'should parse the criteria' do
|
1951
|
+
expect(parse_criteria(&block)).to be == expected
|
1952
|
+
end
|
1953
|
+
|
1954
|
+
include_examples 'should print a deprecation warning for', 'lte'
|
1955
|
+
end
|
1956
|
+
|
1957
|
+
describe 'with a Hash with a "ne" operator' do
|
1958
|
+
let(:block) do
|
1959
|
+
-> { { 'series' => ne('Earthsea') } }
|
1960
|
+
end
|
1961
|
+
let(:expected) do
|
1962
|
+
[['series', operators::NOT_EQUAL, 'Earthsea']]
|
1963
|
+
end
|
1964
|
+
|
1965
|
+
it 'should parse the criteria' do
|
1966
|
+
expect(parse_criteria(&block)).to be == expected
|
1967
|
+
end
|
1968
|
+
|
1969
|
+
include_examples 'should print a deprecation warning for', 'ne'
|
1970
|
+
end
|
1971
|
+
|
1972
|
+
describe 'with a Hash with a "not_equal" operator' do
|
1973
|
+
let(:block) do
|
1974
|
+
-> { { 'series' => not_equal('Earthsea') } }
|
1975
|
+
end
|
1976
|
+
let(:expected) do
|
1977
|
+
[['series', operators::NOT_EQUAL, 'Earthsea']]
|
1978
|
+
end
|
1979
|
+
|
1980
|
+
it 'should parse the criteria' do
|
1981
|
+
expect(parse_criteria(&block)).to be == expected
|
1982
|
+
end
|
1983
|
+
|
1984
|
+
include_examples 'should print a deprecation warning for',
|
1985
|
+
'not_equal'
|
1986
|
+
end
|
1987
|
+
|
1988
|
+
describe 'with a Hash with a "not_one_of" operator and an Array' do
|
1989
|
+
let(:block) do
|
1990
|
+
lambda do
|
1991
|
+
{
|
1992
|
+
'series' => not_one_of(
|
1993
|
+
['Earthsea', 'The Lord of the Rings']
|
1994
|
+
)
|
1995
|
+
}
|
1996
|
+
end
|
1997
|
+
end
|
1998
|
+
let(:expected) do
|
1999
|
+
[
|
2000
|
+
[
|
2001
|
+
'series',
|
2002
|
+
operators::NOT_ONE_OF,
|
2003
|
+
['Earthsea', 'The Lord of the Rings']
|
2004
|
+
]
|
2005
|
+
]
|
2006
|
+
end
|
2007
|
+
|
2008
|
+
it 'should parse the criteria' do
|
2009
|
+
expect(parse_criteria(&block)).to be == expected
|
2010
|
+
end
|
2011
|
+
|
2012
|
+
include_examples 'should print a deprecation warning for',
|
2013
|
+
'not_one_of'
|
2014
|
+
end
|
2015
|
+
|
2016
|
+
describe 'with a Hash with a "not_one_of" operator and values' do
|
2017
|
+
let(:block) do
|
2018
|
+
lambda do
|
2019
|
+
{ 'series' => not_one_of('Earthsea', 'The Lord of the Rings') }
|
2020
|
+
end
|
2021
|
+
end
|
2022
|
+
let(:expected) do
|
2023
|
+
[
|
2024
|
+
[
|
2025
|
+
'series',
|
2026
|
+
operators::NOT_ONE_OF,
|
2027
|
+
['Earthsea', 'The Lord of the Rings']
|
2028
|
+
]
|
2029
|
+
]
|
2030
|
+
end
|
2031
|
+
|
2032
|
+
it 'should parse the criteria' do
|
2033
|
+
expect(parse_criteria(&block)).to be == expected
|
2034
|
+
end
|
2035
|
+
|
2036
|
+
include_examples 'should print a deprecation warning for',
|
2037
|
+
'not_one_of'
|
2038
|
+
end
|
2039
|
+
|
2040
|
+
describe 'with a Hash with a "one_of" operator and an Array' do
|
2041
|
+
let(:block) do
|
2042
|
+
lambda do
|
2043
|
+
{ 'series' => one_of(['Earthsea', 'The Lord of the Rings']) }
|
2044
|
+
end
|
2045
|
+
end
|
2046
|
+
let(:expected) do
|
2047
|
+
[
|
2048
|
+
[
|
2049
|
+
'series',
|
2050
|
+
operators::ONE_OF,
|
2051
|
+
['Earthsea', 'The Lord of the Rings']
|
2052
|
+
]
|
2053
|
+
]
|
2054
|
+
end
|
2055
|
+
|
2056
|
+
it 'should parse the criteria' do
|
2057
|
+
expect(parse_criteria(&block)).to be == expected
|
2058
|
+
end
|
2059
|
+
|
2060
|
+
include_examples 'should print a deprecation warning for', 'one_of'
|
2061
|
+
end
|
2062
|
+
|
2063
|
+
describe 'with a Hash with a "one_of" operator and values' do
|
2064
|
+
let(:block) do
|
2065
|
+
lambda do
|
2066
|
+
{ 'series' => one_of('Earthsea', 'The Lord of the Rings') }
|
2067
|
+
end
|
2068
|
+
end
|
2069
|
+
let(:expected) do
|
2070
|
+
[
|
2071
|
+
[
|
2072
|
+
'series',
|
2073
|
+
operators::ONE_OF,
|
2074
|
+
['Earthsea', 'The Lord of the Rings']
|
2075
|
+
]
|
2076
|
+
]
|
2077
|
+
end
|
2078
|
+
|
2079
|
+
it 'should parse the criteria' do
|
2080
|
+
expect(parse_criteria(&block)).to be == expected
|
2081
|
+
end
|
2082
|
+
|
2083
|
+
include_examples 'should print a deprecation warning for', 'one_of'
|
2084
|
+
end
|
2085
|
+
|
2086
|
+
describe 'with a Hash with an unknown operator' do
|
2087
|
+
let(:error_class) do
|
2088
|
+
Cuprum::Collections::Queries::UnknownOperatorException
|
2089
|
+
end
|
2090
|
+
let(:error_message) do
|
2091
|
+
'unknown operator "random"'
|
2092
|
+
end
|
2093
|
+
let(:block) do
|
2094
|
+
-> { { 'genre' => random('Science Fiction', 'Fantasy') } }
|
2095
|
+
end
|
2096
|
+
|
2097
|
+
it 'should raise an exception' do
|
2098
|
+
expect { parse_criteria(&block) }
|
2099
|
+
.to raise_error error_class, error_message
|
2100
|
+
end
|
2101
|
+
|
2102
|
+
it 'should preserve the original exception', :aggregate_failures do
|
2103
|
+
parse_criteria(&block)
|
2104
|
+
rescue error_class => exception
|
2105
|
+
expect(exception.cause).to be_a NameError
|
2106
|
+
expect(exception.name).to be == :random
|
2107
|
+
end
|
2108
|
+
end
|
2109
|
+
end
|
2110
|
+
end
|
2111
|
+
end
|
2112
|
+
|
2113
|
+
# Contract validating the parsing of criteria from a Hash.
|
2114
|
+
module ShouldParseCriteriaFromAHashContract
|
2115
|
+
extend RSpec::SleepingKingStudios::Contract
|
2116
|
+
|
2117
|
+
# @!method apply(example_group)
|
2118
|
+
# Adds the contract to the example group.
|
2119
|
+
#
|
2120
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
2121
|
+
# which the contract is applied.
|
2122
|
+
contract do
|
2123
|
+
describe 'with nil' do
|
2124
|
+
let(:error_message) do
|
2125
|
+
'value must be a Hash with String or Symbol keys'
|
2126
|
+
end
|
2127
|
+
|
2128
|
+
it 'should raise an exception' do
|
2129
|
+
expect { parse_criteria(nil) }
|
2130
|
+
.to raise_error ArgumentError, error_message
|
2131
|
+
end
|
2132
|
+
end
|
2133
|
+
|
2134
|
+
describe 'with an Object' do
|
2135
|
+
let(:error_message) do
|
2136
|
+
'value must be a Hash with String or Symbol keys'
|
2137
|
+
end
|
2138
|
+
|
2139
|
+
it 'should raise an exception' do
|
2140
|
+
expect { parse_criteria(Object.new.freeze) }
|
2141
|
+
.to raise_error ArgumentError, error_message
|
2142
|
+
end
|
2143
|
+
end
|
2144
|
+
|
2145
|
+
describe 'with a Hash with invalid keys' do
|
2146
|
+
let(:error_message) do
|
2147
|
+
'value must be a Hash with String or Symbol keys'
|
2148
|
+
end
|
2149
|
+
let(:value) { { nil => 'invalid' } }
|
2150
|
+
|
2151
|
+
it 'should raise an exception' do
|
2152
|
+
expect { parse_criteria(value) }
|
2153
|
+
.to raise_error ArgumentError, error_message
|
2154
|
+
end
|
2155
|
+
end
|
2156
|
+
|
2157
|
+
describe 'with an empty Hash' do
|
2158
|
+
let(:value) { {} }
|
2159
|
+
|
2160
|
+
it { expect(parse_criteria(value)).to be == [] }
|
2161
|
+
end
|
2162
|
+
|
2163
|
+
describe 'with a Hash with one key' do
|
2164
|
+
let(:operators) { Cuprum::Collections::Queries::Operators }
|
2165
|
+
let(:value) do
|
2166
|
+
{ 'title' => 'A Wizard of Earthsea' }
|
2167
|
+
end
|
2168
|
+
let(:expected) do
|
2169
|
+
[['title', operators::EQUAL, 'A Wizard of Earthsea']]
|
2170
|
+
end
|
2171
|
+
|
2172
|
+
it { expect(parse_criteria(value)).to be == expected }
|
2173
|
+
end
|
2174
|
+
|
2175
|
+
describe 'with a Hash with many String keys' do
|
2176
|
+
let(:operators) { Cuprum::Collections::Queries::Operators }
|
2177
|
+
let(:value) do
|
2178
|
+
{
|
2179
|
+
'title' => 'A Wizard of Earthsea',
|
2180
|
+
'author' => 'Ursula K. LeGuin',
|
2181
|
+
'series' => 'Earthsea'
|
2182
|
+
}
|
2183
|
+
end
|
2184
|
+
let(:expected) do
|
2185
|
+
[
|
2186
|
+
['title', operators::EQUAL, 'A Wizard of Earthsea'],
|
2187
|
+
['author', operators::EQUAL, 'Ursula K. LeGuin'],
|
2188
|
+
['series', operators::EQUAL, 'Earthsea']
|
2189
|
+
]
|
2190
|
+
end
|
2191
|
+
|
2192
|
+
it { expect(parse_criteria(value)).to be == expected }
|
2193
|
+
end
|
2194
|
+
|
2195
|
+
describe 'with a Hash with many Symbol keys' do
|
2196
|
+
let(:operators) { Cuprum::Collections::Queries::Operators }
|
2197
|
+
let(:value) do
|
2198
|
+
{
|
2199
|
+
title: 'A Wizard of Earthsea',
|
2200
|
+
author: 'Ursula K. LeGuin',
|
2201
|
+
series: 'Earthsea'
|
2202
|
+
}
|
2203
|
+
end
|
2204
|
+
let(:expected) do
|
2205
|
+
[
|
2206
|
+
['title', operators::EQUAL, 'A Wizard of Earthsea'],
|
2207
|
+
['author', operators::EQUAL, 'Ursula K. LeGuin'],
|
2208
|
+
['series', operators::EQUAL, 'Earthsea']
|
2209
|
+
]
|
2210
|
+
end
|
2211
|
+
|
2212
|
+
it { expect(parse_criteria(value)).to be == expected }
|
2213
|
+
end
|
2214
|
+
end
|
2215
|
+
end
|
2216
|
+
end
|
2217
|
+
end
|