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