cuprum-collections 0.5.1 → 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 +47 -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 +4 -4
- data/lib/cuprum/collections.rb +5 -1
- metadata +47 -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,857 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec/sleeping_king_studios/deferred'
|
|
4
|
+
|
|
5
|
+
require 'cuprum/collections/rspec/deferred/scopes'
|
|
6
|
+
require 'cuprum/collections/rspec/deferred/scopes/parser_examples'
|
|
7
|
+
|
|
8
|
+
module Cuprum::Collections::RSpec::Deferred::Scopes
|
|
9
|
+
# Deferred examples for asserting on scope builders.
|
|
10
|
+
module BuilderExamples
|
|
11
|
+
include RSpec::SleepingKingStudios::Deferred::Provider
|
|
12
|
+
include Cuprum::Collections::RSpec::Deferred::Scopes::ParserExamples
|
|
13
|
+
|
|
14
|
+
deferred_examples 'should build collection Scopes' do |**deferred_options|
|
|
15
|
+
all_scope_class = deferred_options.fetch(:all_class) do
|
|
16
|
+
deferred_options[:namespace]&.const_get(:AllScope)
|
|
17
|
+
end
|
|
18
|
+
conjunction_scope_class = deferred_options.fetch(:conjunction_class) do
|
|
19
|
+
deferred_options[:namespace]&.const_get(:ConjunctionScope)
|
|
20
|
+
end
|
|
21
|
+
criteria_scope_class = deferred_options.fetch(:criteria_class) do
|
|
22
|
+
deferred_options[:namespace]&.const_get(:CriteriaScope)
|
|
23
|
+
end
|
|
24
|
+
disjunction_scope_class = deferred_options.fetch(:disjunction_class) do
|
|
25
|
+
deferred_options[:namespace]&.const_get(:DisjunctionScope)
|
|
26
|
+
end
|
|
27
|
+
none_scope_class = deferred_options.fetch(:none_class) do
|
|
28
|
+
deferred_options[:namespace]&.const_get(:NoneScope)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
deferred_context 'with container scope helpers' do
|
|
32
|
+
let(:scope) { build_container(scopes:) }
|
|
33
|
+
|
|
34
|
+
# :nocov:
|
|
35
|
+
define_method :expected_class_for do |type|
|
|
36
|
+
case type
|
|
37
|
+
when :all then all_scope_class
|
|
38
|
+
when :conjunction then conjunction_scope_class
|
|
39
|
+
when :criteria then criteria_scope_class
|
|
40
|
+
when :disjunction then disjunction_scope_class
|
|
41
|
+
when :none then none_scope_class
|
|
42
|
+
else
|
|
43
|
+
raise "unknown scope type #{type.inspect}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# rubocop:disable Metrics/AbcSize
|
|
48
|
+
define_method :should_recursively_convert_scopes \
|
|
49
|
+
do |original_scopes, converted|
|
|
50
|
+
original_scopes.zip(converted).each do |original, scope|
|
|
51
|
+
expect(scope).to be_a expected_class_for(original.type)
|
|
52
|
+
|
|
53
|
+
if scope.type == :criteria
|
|
54
|
+
expect(scope.criteria).to be == original.criteria
|
|
55
|
+
elsif %i[conjunction disjunction].include?(scope.type)
|
|
56
|
+
should_recursively_convert_scopes(original.scopes, scope.scopes)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
# rubocop:enable Metrics/AbcSize
|
|
61
|
+
|
|
62
|
+
# :nocov:
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
deferred_examples 'should build an all scope' do
|
|
66
|
+
let(:scope) { build_all }
|
|
67
|
+
|
|
68
|
+
# :nocov:
|
|
69
|
+
unless all_scope_class
|
|
70
|
+
pending '(must specify :all_class option)'
|
|
71
|
+
|
|
72
|
+
next
|
|
73
|
+
end
|
|
74
|
+
# :nocov:
|
|
75
|
+
|
|
76
|
+
it { expect(scope).to be_a all_scope_class }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
deferred_examples 'should build a conjunction scope' do
|
|
80
|
+
include_deferred 'with container scope helpers'
|
|
81
|
+
|
|
82
|
+
# :nocov:
|
|
83
|
+
unless conjunction_scope_class
|
|
84
|
+
pending '(must specify :conjunction_class option)'
|
|
85
|
+
|
|
86
|
+
next
|
|
87
|
+
end
|
|
88
|
+
# :nocov:
|
|
89
|
+
|
|
90
|
+
describe 'with scopes: an empty Array' do
|
|
91
|
+
let(:scopes) { [] }
|
|
92
|
+
|
|
93
|
+
it { expect(scope).to be_a conjunction_scope_class }
|
|
94
|
+
|
|
95
|
+
it { expect(scope.scopes).to be == scopes }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe 'with scopes: an Array of Scopes' do
|
|
99
|
+
let(:scopes) { Array.new(3) { build_scope } }
|
|
100
|
+
|
|
101
|
+
it { expect(scope).to be_a conjunction_scope_class }
|
|
102
|
+
|
|
103
|
+
it { expect(scope.scopes.size).to be == scopes.size }
|
|
104
|
+
|
|
105
|
+
it 'should convert the scopes', :aggregate_failures do
|
|
106
|
+
should_recursively_convert_scopes(scopes, scope.scopes)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
deferred_examples 'should build a criteria scope' do |inverted: false|
|
|
112
|
+
let(:scope) { build_criteria(criteria:) }
|
|
113
|
+
|
|
114
|
+
# :nocov:
|
|
115
|
+
unless criteria_scope_class
|
|
116
|
+
pending '(must specify :criteria_class option)'
|
|
117
|
+
|
|
118
|
+
next
|
|
119
|
+
end
|
|
120
|
+
# :nocov:
|
|
121
|
+
|
|
122
|
+
describe 'with criteria: an empty Array' do
|
|
123
|
+
let(:criteria) { [] }
|
|
124
|
+
|
|
125
|
+
it { expect(scope).to be_a criteria_scope_class }
|
|
126
|
+
|
|
127
|
+
it { expect(scope.criteria).to be == criteria }
|
|
128
|
+
|
|
129
|
+
it { expect(scope.inverted?).to be inverted }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
describe 'with criteria: an Array of criteria' do
|
|
133
|
+
let(:criteria) do
|
|
134
|
+
operators = Cuprum::Collections::Queries::Operators
|
|
135
|
+
|
|
136
|
+
[
|
|
137
|
+
[
|
|
138
|
+
'title',
|
|
139
|
+
operators::EQUAL,
|
|
140
|
+
'The Word For World Is Forest'
|
|
141
|
+
],
|
|
142
|
+
[
|
|
143
|
+
'author',
|
|
144
|
+
operators::EQUAL,
|
|
145
|
+
'Ursula K. LeGuin'
|
|
146
|
+
]
|
|
147
|
+
]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it { expect(scope).to be_a criteria_scope_class }
|
|
151
|
+
|
|
152
|
+
it { expect(scope.criteria).to be == criteria }
|
|
153
|
+
|
|
154
|
+
it { expect(scope.inverted?).to be inverted }
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
deferred_examples 'should build a disjunction scope' do
|
|
159
|
+
include_deferred 'with container scope helpers'
|
|
160
|
+
|
|
161
|
+
let(:scope) { build_container(scopes:) }
|
|
162
|
+
|
|
163
|
+
# :nocov:
|
|
164
|
+
unless disjunction_scope_class
|
|
165
|
+
pending '(must specify :disjunction_class option)'
|
|
166
|
+
|
|
167
|
+
next
|
|
168
|
+
end
|
|
169
|
+
# :nocov:
|
|
170
|
+
|
|
171
|
+
describe 'with scopes: an empty Array' do
|
|
172
|
+
let(:scopes) { [] }
|
|
173
|
+
|
|
174
|
+
it { expect(scope).to be_a disjunction_scope_class }
|
|
175
|
+
|
|
176
|
+
it { expect(scope.scopes).to be == scopes }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
describe 'with scopes: an Array of Scopes' do
|
|
180
|
+
let(:scopes) { Array.new(3) { build_scope } }
|
|
181
|
+
|
|
182
|
+
it { expect(scope).to be_a disjunction_scope_class }
|
|
183
|
+
|
|
184
|
+
it { expect(scope.scopes.size).to be == scopes.size }
|
|
185
|
+
|
|
186
|
+
it 'should convert the scopes', :aggregate_failures do
|
|
187
|
+
should_recursively_convert_scopes(scopes, scope.scopes)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
deferred_examples 'should build a none scope' do
|
|
193
|
+
let(:scope) { build_none }
|
|
194
|
+
|
|
195
|
+
# :nocov:
|
|
196
|
+
unless none_scope_class
|
|
197
|
+
pending '(must specify :none_class option)'
|
|
198
|
+
|
|
199
|
+
next
|
|
200
|
+
end
|
|
201
|
+
# :nocov:
|
|
202
|
+
|
|
203
|
+
it { expect(scope).to be_a none_scope_class }
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
deferred_examples 'should validate the criteria' do
|
|
207
|
+
describe 'with criteria: nil' do
|
|
208
|
+
let(:error_message) { 'criteria must be an Array' }
|
|
209
|
+
|
|
210
|
+
it 'should raise an exception' do
|
|
211
|
+
expect { build_criteria(criteria: nil) }
|
|
212
|
+
.to raise_error ArgumentError, error_message
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
describe 'with criteria: an Object' do
|
|
217
|
+
let(:error_message) { 'criteria must be an Array' }
|
|
218
|
+
|
|
219
|
+
it 'should raise an exception' do
|
|
220
|
+
expect { build_criteria(criteria: Object.new.freeze) }
|
|
221
|
+
.to raise_error ArgumentError, error_message
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
describe 'with criteria: an Array of non-Array items' do
|
|
226
|
+
let(:error_message) { 'criterion must be an Array of size 3' }
|
|
227
|
+
|
|
228
|
+
it 'should raise an exception' do
|
|
229
|
+
expect { build_criteria(criteria: [nil]) }
|
|
230
|
+
.to raise_error ArgumentError, error_message
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
describe 'with criteria: an Array of invalid Arrays' do
|
|
235
|
+
let(:error_message) { 'criterion must be an Array of size 3' }
|
|
236
|
+
|
|
237
|
+
it 'should raise an exception' do
|
|
238
|
+
expect { build_criteria(criteria: [[], [], []]) }
|
|
239
|
+
.to raise_error ArgumentError, error_message
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
deferred_examples 'should validate the scopes' do
|
|
245
|
+
describe 'with scopes: nil' do
|
|
246
|
+
let(:error_message) { 'scopes must be an Array' }
|
|
247
|
+
|
|
248
|
+
it 'should raise an exception' do
|
|
249
|
+
expect { build_container(scopes: nil) }
|
|
250
|
+
.to raise_error ArgumentError, error_message
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
describe 'with scopes: an Object' do
|
|
255
|
+
let(:error_message) { 'scopes must be an Array' }
|
|
256
|
+
|
|
257
|
+
it 'should raise an exception' do
|
|
258
|
+
expect { build_container(scopes: Object.new.freeze) }
|
|
259
|
+
.to raise_error ArgumentError, error_message
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
describe 'with scopes: an invalid Array' do
|
|
264
|
+
let(:error_message) { 'scope must be a Scope instance' }
|
|
265
|
+
|
|
266
|
+
it 'should raise an exception' do
|
|
267
|
+
expect { build_container(scopes: [nil]) }
|
|
268
|
+
.to raise_error ArgumentError, error_message
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
describe '.instance' do
|
|
274
|
+
it 'should define the class method' do
|
|
275
|
+
expect(described_class).to respond_to(:instance).with(0).arguments
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
it { expect(described_class.instance).to be_a described_class }
|
|
279
|
+
|
|
280
|
+
it { expect(described_class.instance).to be subject }
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
describe '#build' do
|
|
284
|
+
it 'should define the method' do
|
|
285
|
+
expect(subject)
|
|
286
|
+
.to respond_to(:build)
|
|
287
|
+
.with(0..1).arguments
|
|
288
|
+
.and_a_block
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
describe 'with an invalid scope' do
|
|
292
|
+
let(:original) { build_scope }
|
|
293
|
+
let(:error_class) do
|
|
294
|
+
Cuprum::Collections::Scopes::Building::UnknownScopeTypeError
|
|
295
|
+
end
|
|
296
|
+
let(:error_message) do
|
|
297
|
+
"#{described_class.name} cannot transform scopes of " \
|
|
298
|
+
"type #{original.type.inspect} (#{original.class.name})"
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
before(:example) do
|
|
302
|
+
allow(original).to receive(:type).and_return(:invalid)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
it 'should raise an exception' do
|
|
306
|
+
expect { subject.build(original) }
|
|
307
|
+
.to raise_error error_class, error_message
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
next if deferred_options.fetch(:abstract, false)
|
|
312
|
+
|
|
313
|
+
describe 'with a block' do
|
|
314
|
+
def build_criteria(criteria:)
|
|
315
|
+
value = criteria.to_h do |(attribute, _, expected)|
|
|
316
|
+
[attribute, expected]
|
|
317
|
+
end
|
|
318
|
+
block = -> { value }
|
|
319
|
+
|
|
320
|
+
subject.build(&block)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def parse_criteria(...)
|
|
324
|
+
subject.build(...).criteria
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
include_deferred 'should build a criteria scope'
|
|
328
|
+
|
|
329
|
+
include_deferred 'should parse Scope criteria from a block'
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
describe 'with a proc' do
|
|
333
|
+
def build_criteria(criteria:)
|
|
334
|
+
value = criteria.to_h do |(attribute, _, expected)|
|
|
335
|
+
[attribute, expected]
|
|
336
|
+
end
|
|
337
|
+
block = -> { value }
|
|
338
|
+
|
|
339
|
+
subject.build(block)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def parse_criteria(...)
|
|
343
|
+
subject.build(...).criteria
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
include_deferred 'should build a criteria scope'
|
|
347
|
+
|
|
348
|
+
include_deferred 'should parse Scope criteria from a block'
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
describe 'with a hash' do
|
|
352
|
+
def build_criteria(criteria:)
|
|
353
|
+
value = criteria.to_h do |(attribute, _, expected)|
|
|
354
|
+
[attribute, expected]
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
subject.build(value)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def parse_criteria(value)
|
|
361
|
+
subject.build(value).criteria
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
include_deferred 'should build a criteria scope'
|
|
365
|
+
|
|
366
|
+
include_deferred 'should parse Scope criteria from a Hash'
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
describe 'with an all scope' do
|
|
370
|
+
def build_all
|
|
371
|
+
original = Cuprum::Collections::Scopes::AllScope.new
|
|
372
|
+
|
|
373
|
+
subject.build(original)
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
include_deferred 'should build an all scope'
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
describe 'with a conjunction scope' do
|
|
380
|
+
def build_container(scopes:)
|
|
381
|
+
original =
|
|
382
|
+
Cuprum::Collections::Scopes::ConjunctionScope
|
|
383
|
+
.new(scopes:)
|
|
384
|
+
|
|
385
|
+
subject.build(original)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
include_deferred 'should build a conjunction scope'
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
describe 'with a criteria scope' do
|
|
392
|
+
def build_criteria(criteria:)
|
|
393
|
+
original =
|
|
394
|
+
Cuprum::Collections::Scopes::CriteriaScope
|
|
395
|
+
.new(criteria:)
|
|
396
|
+
|
|
397
|
+
subject.build(original)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
include_deferred 'should build a criteria scope'
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
describe 'with a disjunction scope' do
|
|
404
|
+
def build_container(scopes:)
|
|
405
|
+
original =
|
|
406
|
+
Cuprum::Collections::Scopes::DisjunctionScope
|
|
407
|
+
.new(scopes:)
|
|
408
|
+
|
|
409
|
+
subject.build(original)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
include_deferred 'should build a disjunction scope'
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
describe 'with a none scope' do
|
|
416
|
+
def build_none
|
|
417
|
+
original = Cuprum::Collections::Scopes::NoneScope.new
|
|
418
|
+
|
|
419
|
+
subject.build(original)
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
include_deferred 'should build a none scope'
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
describe 'with an all scope of matching class' do
|
|
426
|
+
# :nocov:
|
|
427
|
+
unless all_scope_class
|
|
428
|
+
pending '(must specify :all_class option)'
|
|
429
|
+
|
|
430
|
+
next
|
|
431
|
+
end
|
|
432
|
+
# :nocov:
|
|
433
|
+
|
|
434
|
+
let(:original) do
|
|
435
|
+
all_scope_class.new
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
it { expect(subject.build(original)).to be original }
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
describe 'with a conjunction scope of matching class' do
|
|
442
|
+
# :nocov:
|
|
443
|
+
unless conjunction_scope_class
|
|
444
|
+
pending '(must specify :conjunction_class option)'
|
|
445
|
+
|
|
446
|
+
next
|
|
447
|
+
end
|
|
448
|
+
# :nocov:
|
|
449
|
+
|
|
450
|
+
let(:original) do
|
|
451
|
+
conjunction_scope_class.new(scopes: [])
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
it { expect(subject.build(original)).to be original }
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
describe 'with a criteria scope of matching class' do
|
|
458
|
+
# :nocov:
|
|
459
|
+
unless criteria_scope_class
|
|
460
|
+
pending '(must specify :criteria_scope_class option)'
|
|
461
|
+
|
|
462
|
+
next
|
|
463
|
+
end
|
|
464
|
+
# :nocov:
|
|
465
|
+
|
|
466
|
+
let(:original) do
|
|
467
|
+
criteria_scope_class.new(criteria: [])
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
it { expect(subject.build(original)).to be original }
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
describe 'with a disjunction scope of matching class' do
|
|
474
|
+
# :nocov:
|
|
475
|
+
unless disjunction_scope_class
|
|
476
|
+
pending '(must specify :disjunction_scope_class option)'
|
|
477
|
+
|
|
478
|
+
next
|
|
479
|
+
end
|
|
480
|
+
# :nocov:
|
|
481
|
+
|
|
482
|
+
let(:original) do
|
|
483
|
+
disjunction_scope_class.new(scopes: [])
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
it { expect(subject.build(original)).to be original }
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
describe 'with a none scope of matching class' do
|
|
490
|
+
# :nocov:
|
|
491
|
+
unless none_scope_class
|
|
492
|
+
pending '(must specify :none_class option)'
|
|
493
|
+
|
|
494
|
+
next
|
|
495
|
+
end
|
|
496
|
+
# :nocov:
|
|
497
|
+
|
|
498
|
+
let(:original) do
|
|
499
|
+
none_scope_class.new
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
it { expect(subject.build(original)).to be original }
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
describe '#build_all_scope' do
|
|
507
|
+
define_method :build_all do
|
|
508
|
+
subject.build_all_scope
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
it 'should define the method' do
|
|
512
|
+
expect(subject)
|
|
513
|
+
.to respond_to(:build_all_scope)
|
|
514
|
+
.with(0).arguments
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
next if deferred_options.fetch(:abstract, false)
|
|
518
|
+
|
|
519
|
+
include_deferred 'should build an all scope'
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
describe '#build_conjunction_scope' do
|
|
523
|
+
let(:scope) { subject.build_conjunction_scope(scopes:) }
|
|
524
|
+
|
|
525
|
+
define_method :build_container do |scopes:|
|
|
526
|
+
subject.build_conjunction_scope(scopes:)
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
it 'should define the method' do
|
|
530
|
+
expect(subject)
|
|
531
|
+
.to respond_to(:build_conjunction_scope)
|
|
532
|
+
.with(0).arguments
|
|
533
|
+
.and_keywords(:safe, :scopes)
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
include_deferred 'should validate the scopes'
|
|
537
|
+
|
|
538
|
+
next if deferred_options.fetch(:abstract, false)
|
|
539
|
+
|
|
540
|
+
include_deferred 'should build a conjunction scope'
|
|
541
|
+
|
|
542
|
+
describe 'with safe: false' do
|
|
543
|
+
let(:scope) do
|
|
544
|
+
subject.build_conjunction_scope(scopes:, safe: false)
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
describe 'with scopes: an empty Array' do
|
|
548
|
+
let(:scopes) { [] }
|
|
549
|
+
|
|
550
|
+
it { expect(scope).to be_a conjunction_scope_class }
|
|
551
|
+
|
|
552
|
+
it { expect(scope.scopes).to be == scopes }
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
describe 'with scopes: an Array of Scopes' do
|
|
556
|
+
let(:scopes) { Array.new(3) { build_scope } }
|
|
557
|
+
|
|
558
|
+
it { expect(scope).to be_a conjunction_scope_class }
|
|
559
|
+
|
|
560
|
+
it { expect(scope.scopes).to be == scopes }
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
describe '#build_criteria_scope' do
|
|
566
|
+
let(:inverted) { false }
|
|
567
|
+
|
|
568
|
+
define_method :build_criteria do |criteria:|
|
|
569
|
+
subject.build_criteria_scope(criteria:, inverted:)
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
it 'should define the method' do
|
|
573
|
+
expect(subject)
|
|
574
|
+
.to respond_to(:build_criteria_scope)
|
|
575
|
+
.with(0).arguments
|
|
576
|
+
.and_keywords(:criteria, :inverted)
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
include_deferred 'should validate the criteria'
|
|
580
|
+
|
|
581
|
+
next if deferred_options.fetch(:abstract, false)
|
|
582
|
+
|
|
583
|
+
include_deferred 'should build a criteria scope'
|
|
584
|
+
|
|
585
|
+
context 'with inverted: true' do
|
|
586
|
+
let(:inverted) { true }
|
|
587
|
+
|
|
588
|
+
include_deferred 'should build a criteria scope', inverted: true
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
describe '#build_disjunction_scope' do
|
|
593
|
+
let(:scope) { subject.build_disjunction_scope(scopes:) }
|
|
594
|
+
|
|
595
|
+
define_method :build_container do |scopes:|
|
|
596
|
+
subject.build_disjunction_scope(scopes:)
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
it 'should define the method' do
|
|
600
|
+
expect(subject)
|
|
601
|
+
.to respond_to(:build_disjunction_scope)
|
|
602
|
+
.with(0).arguments
|
|
603
|
+
.and_keywords(:safe, :scopes)
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
include_deferred 'should validate the scopes'
|
|
607
|
+
|
|
608
|
+
next if deferred_options.fetch(:abstract, false)
|
|
609
|
+
|
|
610
|
+
include_deferred 'should build a disjunction scope'
|
|
611
|
+
|
|
612
|
+
describe 'with safe: false' do
|
|
613
|
+
let(:scope) do
|
|
614
|
+
subject.build_disjunction_scope(scopes:, safe: false)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
describe 'with scopes: an empty Array' do
|
|
618
|
+
let(:scopes) { [] }
|
|
619
|
+
|
|
620
|
+
it { expect(scope).to be_a disjunction_scope_class }
|
|
621
|
+
|
|
622
|
+
it { expect(scope.scopes).to be == scopes }
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
describe 'with scopes: an Array of Scopes' do
|
|
626
|
+
let(:scopes) { Array.new(3) { build_scope } }
|
|
627
|
+
|
|
628
|
+
it { expect(scope).to be_a disjunction_scope_class }
|
|
629
|
+
|
|
630
|
+
it { expect(scope.scopes).to be == scopes }
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
describe '#build_none_scope' do
|
|
636
|
+
define_method :build_none do
|
|
637
|
+
subject.build_none_scope
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
it 'should define the method' do
|
|
641
|
+
expect(subject)
|
|
642
|
+
.to respond_to(:build_none_scope)
|
|
643
|
+
.with(0).arguments
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
next if deferred_options.fetch(:abstract, false)
|
|
647
|
+
|
|
648
|
+
include_deferred 'should build a none scope'
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
describe '#transform_scope' do
|
|
652
|
+
let(:scope) { subject.transform_scope(scope: original) }
|
|
653
|
+
|
|
654
|
+
it 'should define the method' do
|
|
655
|
+
expect(subject)
|
|
656
|
+
.to respond_to(:transform_scope)
|
|
657
|
+
.with(0).arguments
|
|
658
|
+
.and_keywords(:scope)
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
describe 'with scope: nil' do
|
|
662
|
+
let(:error_message) { 'scope must be a Scope instance' }
|
|
663
|
+
|
|
664
|
+
it 'should raise an exception' do
|
|
665
|
+
expect { subject.transform_scope(scope: nil) }
|
|
666
|
+
.to raise_error ArgumentError, error_message
|
|
667
|
+
end
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
describe 'with scope: an Object' do
|
|
671
|
+
let(:error_message) { 'scope must be a Scope instance' }
|
|
672
|
+
|
|
673
|
+
it 'should raise an exception' do
|
|
674
|
+
expect { subject.transform_scope(scope: Object.new.freeze) }
|
|
675
|
+
.to raise_error ArgumentError, error_message
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
describe 'with an invalid scope' do
|
|
680
|
+
let(:original) { build_scope }
|
|
681
|
+
let(:error_class) do
|
|
682
|
+
Cuprum::Collections::Scopes::Building::UnknownScopeTypeError
|
|
683
|
+
end
|
|
684
|
+
let(:error_message) do
|
|
685
|
+
"#{described_class.name} cannot transform scopes of " \
|
|
686
|
+
"type #{original.type.inspect} (#{original.class.name})"
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
before(:example) do
|
|
690
|
+
allow(original).to receive(:type).and_return(:invalid)
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
it 'should raise an exception' do
|
|
694
|
+
expect { subject.transform_scope(scope: original) }
|
|
695
|
+
.to raise_error error_class, error_message
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
next if deferred_options.fetch(:abstract, false)
|
|
700
|
+
|
|
701
|
+
describe 'with an all scope' do
|
|
702
|
+
define_method :build_all do
|
|
703
|
+
original = Cuprum::Collections::Scopes::AllScope.new
|
|
704
|
+
|
|
705
|
+
subject.transform_scope(scope: original)
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
include_deferred 'should build an all scope'
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
describe 'with a conjunction scope' do
|
|
712
|
+
define_method :build_container do |scopes:|
|
|
713
|
+
original =
|
|
714
|
+
Cuprum::Collections::Scopes::ConjunctionScope
|
|
715
|
+
.new(scopes:)
|
|
716
|
+
|
|
717
|
+
subject.transform_scope(scope: original)
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
include_deferred 'should build a conjunction scope'
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
describe 'with a criteria scope' do
|
|
724
|
+
let(:inverted) { false }
|
|
725
|
+
|
|
726
|
+
define_method :build_criteria do |criteria:|
|
|
727
|
+
original =
|
|
728
|
+
Cuprum::Collections::Scopes::CriteriaScope
|
|
729
|
+
.new(criteria:, inverted:)
|
|
730
|
+
|
|
731
|
+
subject.transform_scope(scope: original)
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
include_deferred 'should build a criteria scope'
|
|
735
|
+
|
|
736
|
+
context 'when the scope is inverted' do
|
|
737
|
+
let(:inverted) { true }
|
|
738
|
+
|
|
739
|
+
include_deferred 'should build a criteria scope', inverted: true
|
|
740
|
+
end
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
describe 'with a disjunction scope' do
|
|
744
|
+
define_method :build_container do |scopes:|
|
|
745
|
+
original =
|
|
746
|
+
Cuprum::Collections::Scopes::DisjunctionScope
|
|
747
|
+
.new(scopes:)
|
|
748
|
+
|
|
749
|
+
subject.transform_scope(scope: original)
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
include_deferred 'should build a disjunction scope'
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
describe 'with a none scope' do
|
|
756
|
+
define_method :build_none do
|
|
757
|
+
original = Cuprum::Collections::Scopes::NoneScope.new
|
|
758
|
+
|
|
759
|
+
subject.transform_scope(scope: original)
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
include_deferred 'should build a none scope'
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
describe 'with an all scope of matching class' do
|
|
766
|
+
# :nocov:
|
|
767
|
+
unless all_scope_class
|
|
768
|
+
pending '(must specify :all_class option)'
|
|
769
|
+
|
|
770
|
+
next
|
|
771
|
+
end
|
|
772
|
+
# :nocov:
|
|
773
|
+
|
|
774
|
+
let(:original) do
|
|
775
|
+
all_scope_class.new
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
it 'should return the original scope' do
|
|
779
|
+
expect(subject.transform_scope(scope: original)).to be original
|
|
780
|
+
end
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
describe 'with a conjunction scope of matching class' do
|
|
784
|
+
# :nocov:
|
|
785
|
+
unless conjunction_scope_class
|
|
786
|
+
pending '(must specify :conjunction_class option)'
|
|
787
|
+
|
|
788
|
+
next
|
|
789
|
+
end
|
|
790
|
+
# :nocov:
|
|
791
|
+
|
|
792
|
+
let(:original) do
|
|
793
|
+
conjunction_scope_class.new(scopes: [])
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
it 'should return the original scope' do
|
|
797
|
+
expect(subject.transform_scope(scope: original)).to be original
|
|
798
|
+
end
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
describe 'with a criteria scope of matching class' do
|
|
802
|
+
# :nocov:
|
|
803
|
+
unless criteria_scope_class
|
|
804
|
+
pending '(must specify :criteria_scope_class option)'
|
|
805
|
+
|
|
806
|
+
next
|
|
807
|
+
end
|
|
808
|
+
# :nocov:
|
|
809
|
+
|
|
810
|
+
let(:original) do
|
|
811
|
+
criteria_scope_class.new(criteria: [])
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
it 'should return the original scope' do
|
|
815
|
+
expect(subject.transform_scope(scope: original)).to be original
|
|
816
|
+
end
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
describe 'with a disjunction scope of matching class' do
|
|
820
|
+
# :nocov:
|
|
821
|
+
unless disjunction_scope_class
|
|
822
|
+
pending '(must specify :disjunction_scope_class option)'
|
|
823
|
+
|
|
824
|
+
next
|
|
825
|
+
end
|
|
826
|
+
# :nocov:
|
|
827
|
+
|
|
828
|
+
let(:original) do
|
|
829
|
+
disjunction_scope_class.new(scopes: [])
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
it 'should return the original scope' do
|
|
833
|
+
expect(subject.transform_scope(scope: original)).to be original
|
|
834
|
+
end
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
describe 'with a none scope of matching class' do
|
|
838
|
+
# :nocov:
|
|
839
|
+
unless none_scope_class
|
|
840
|
+
pending '(must specify :none_class option)'
|
|
841
|
+
|
|
842
|
+
next
|
|
843
|
+
end
|
|
844
|
+
# :nocov:
|
|
845
|
+
|
|
846
|
+
let(:original) do
|
|
847
|
+
none_scope_class.new
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
it 'should return the original scope' do
|
|
851
|
+
expect(subject.transform_scope(scope: original)).to be original
|
|
852
|
+
end
|
|
853
|
+
end
|
|
854
|
+
end
|
|
855
|
+
end
|
|
856
|
+
end
|
|
857
|
+
end
|