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