cuprum-collections 0.3.0 → 0.4.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 +48 -0
- data/DEVELOPMENT.md +2 -2
- data/README.md +13 -11
- data/lib/cuprum/collections/association.rb +256 -0
- data/lib/cuprum/collections/associations/belongs_to.rb +32 -0
- data/lib/cuprum/collections/associations/has_many.rb +23 -0
- data/lib/cuprum/collections/associations/has_one.rb +23 -0
- data/lib/cuprum/collections/associations.rb +10 -0
- data/lib/cuprum/collections/basic/collection.rb +39 -74
- data/lib/cuprum/collections/basic/commands/find_many.rb +1 -1
- data/lib/cuprum/collections/basic/commands/find_matching.rb +1 -1
- data/lib/cuprum/collections/basic/repository.rb +9 -33
- data/lib/cuprum/collections/basic.rb +1 -0
- data/lib/cuprum/collections/collection.rb +154 -0
- data/lib/cuprum/collections/commands/associations/find_many.rb +161 -0
- data/lib/cuprum/collections/commands/associations/require_many.rb +48 -0
- data/lib/cuprum/collections/commands/associations.rb +13 -0
- data/lib/cuprum/collections/commands/find_one_matching.rb +1 -1
- data/lib/cuprum/collections/commands.rb +1 -0
- data/lib/cuprum/collections/errors/abstract_find_error.rb +1 -1
- data/lib/cuprum/collections/relation.rb +401 -0
- data/lib/cuprum/collections/repository.rb +71 -4
- data/lib/cuprum/collections/resource.rb +65 -0
- data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +2137 -0
- data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +484 -0
- data/lib/cuprum/collections/rspec/contracts/basic.rb +11 -0
- data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +429 -0
- data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +1462 -0
- data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +1093 -0
- data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +1381 -0
- data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +605 -0
- data/lib/cuprum/collections/rspec/contracts.rb +23 -0
- data/lib/cuprum/collections/rspec/fixtures.rb +85 -82
- data/lib/cuprum/collections/rspec.rb +4 -1
- data/lib/cuprum/collections/version.rb +1 -1
- data/lib/cuprum/collections.rb +9 -4
- metadata +23 -19
- data/lib/cuprum/collections/base.rb +0 -11
- data/lib/cuprum/collections/basic/rspec/command_contract.rb +0 -392
- data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +0 -168
- data/lib/cuprum/collections/rspec/build_one_command_contract.rb +0 -93
- data/lib/cuprum/collections/rspec/collection_contract.rb +0 -190
- data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +0 -108
- data/lib/cuprum/collections/rspec/find_many_command_contract.rb +0 -407
- data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +0 -194
- data/lib/cuprum/collections/rspec/find_one_command_contract.rb +0 -157
- data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +0 -84
- data/lib/cuprum/collections/rspec/query_builder_contract.rb +0 -92
- data/lib/cuprum/collections/rspec/query_contract.rb +0 -650
- data/lib/cuprum/collections/rspec/querying_contract.rb +0 -298
- data/lib/cuprum/collections/rspec/repository_contract.rb +0 -235
- data/lib/cuprum/collections/rspec/update_one_command_contract.rb +0 -80
- data/lib/cuprum/collections/rspec/validate_one_command_contract.rb +0 -96
@@ -0,0 +1,484 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/rspec/contracts/basic'
|
4
|
+
|
5
|
+
module Cuprum::Collections::RSpec::Contracts::Basic
|
6
|
+
# Contracts for asserting on Basic::Command objects.
|
7
|
+
module CommandContracts
|
8
|
+
# Contract validating the behavior of a basic command implementation.
|
9
|
+
module ShouldBeABasicCommandContract
|
10
|
+
extend RSpec::SleepingKingStudios::Contract
|
11
|
+
|
12
|
+
# @!method apply(example_group)
|
13
|
+
# Adds the contract to the example group.
|
14
|
+
#
|
15
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
16
|
+
# which the contract is applied.
|
17
|
+
contract do
|
18
|
+
describe '.subclass' do
|
19
|
+
let(:subclass) { described_class.subclass }
|
20
|
+
let(:constructor_options) do
|
21
|
+
{
|
22
|
+
collection_name: 'books',
|
23
|
+
data: data,
|
24
|
+
optional_key: 'optional value'
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should define the class method' do
|
29
|
+
expect(described_class)
|
30
|
+
.to respond_to(:subclass)
|
31
|
+
.with(0).arguments
|
32
|
+
.and_any_keywords
|
33
|
+
end
|
34
|
+
|
35
|
+
it { expect(subclass).to be_a Class }
|
36
|
+
|
37
|
+
it { expect(subclass).to be < described_class }
|
38
|
+
|
39
|
+
it 'should define the constructor' do
|
40
|
+
expect(subclass)
|
41
|
+
.to respond_to(:new)
|
42
|
+
.with(0).arguments
|
43
|
+
.and_any_keywords
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should return the collection name' do
|
47
|
+
expect(subclass.new(**constructor_options).collection_name)
|
48
|
+
.to be collection_name
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should return the data' do
|
52
|
+
expect(subclass.new(**constructor_options).data)
|
53
|
+
.to be data
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should return the options' do
|
57
|
+
expect(subclass.new(**constructor_options).options)
|
58
|
+
.to be == { optional_key: 'optional value' }
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'with options' do
|
62
|
+
let(:default_options) do
|
63
|
+
{
|
64
|
+
collection_name: 'books',
|
65
|
+
custom_key: 'custom value'
|
66
|
+
}
|
67
|
+
end
|
68
|
+
let(:constructor_options) do
|
69
|
+
{
|
70
|
+
data: data,
|
71
|
+
optional_key: 'optional value'
|
72
|
+
}
|
73
|
+
end
|
74
|
+
let(:subclass) { described_class.subclass(**default_options) }
|
75
|
+
|
76
|
+
it { expect(subclass).to be_a Class }
|
77
|
+
|
78
|
+
it { expect(subclass).to be < described_class }
|
79
|
+
|
80
|
+
it 'should define the constructor' do
|
81
|
+
expect(subclass)
|
82
|
+
.to respond_to(:new)
|
83
|
+
.with(0).arguments
|
84
|
+
.and_any_keywords
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should return the collection name' do
|
88
|
+
expect(subclass.new(**constructor_options).collection_name)
|
89
|
+
.to be collection_name
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should return the data' do
|
93
|
+
expect(subclass.new(**constructor_options).data)
|
94
|
+
.to be data
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should return the options' do
|
98
|
+
expect(subclass.new(**constructor_options).options)
|
99
|
+
.to be == {
|
100
|
+
custom_key: 'custom value',
|
101
|
+
optional_key: 'optional value'
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#collection_name' do
|
108
|
+
include_examples 'should have reader',
|
109
|
+
:collection_name,
|
110
|
+
-> { collection_name }
|
111
|
+
|
112
|
+
context 'when initialized with collection_name: symbol' do
|
113
|
+
let(:collection_name) { :books }
|
114
|
+
|
115
|
+
it { expect(command.collection_name).to be == collection_name.to_s }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#data' do
|
120
|
+
include_examples 'should define reader', :data, -> { data }
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#default_contract' do
|
124
|
+
include_examples 'should define reader', :default_contract, nil
|
125
|
+
|
126
|
+
context 'when initialized with a default contract' do
|
127
|
+
let(:default_contract) { Stannum::Contract.new }
|
128
|
+
let(:constructor_options) do
|
129
|
+
super().merge(default_contract: default_contract)
|
130
|
+
end
|
131
|
+
|
132
|
+
it { expect(command.default_contract).to be default_contract }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '#member_name' do
|
137
|
+
def tools
|
138
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
139
|
+
end
|
140
|
+
|
141
|
+
include_examples 'should have reader',
|
142
|
+
:member_name,
|
143
|
+
-> { tools.str.singularize(collection_name) }
|
144
|
+
|
145
|
+
context 'when initialized with collection_name: value' do
|
146
|
+
let(:collection_name) { :books }
|
147
|
+
|
148
|
+
it 'should return the singular collection name' do
|
149
|
+
expect(command.member_name)
|
150
|
+
.to be == tools.str.singularize(collection_name.to_s)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'when initialized with member_name: string' do
|
155
|
+
let(:member_name) { 'tome' }
|
156
|
+
let(:constructor_options) do
|
157
|
+
super().merge(member_name: member_name)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should return the singular collection name' do
|
161
|
+
expect(command.member_name).to be member_name
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'when initialized with member_name: symbol' do
|
166
|
+
let(:member_name) { :tome }
|
167
|
+
let(:constructor_options) do
|
168
|
+
super().merge(member_name: member_name)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'should return the singular collection name' do
|
172
|
+
expect(command.member_name).to be == member_name.to_s
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '#options' do
|
178
|
+
let(:expected_options) do
|
179
|
+
defined?(super()) ? super() : constructor_options
|
180
|
+
end
|
181
|
+
|
182
|
+
include_examples 'should define reader',
|
183
|
+
:options,
|
184
|
+
-> { be == expected_options }
|
185
|
+
|
186
|
+
context 'when initialized with options' do
|
187
|
+
let(:constructor_options) { super().merge({ key: 'value' }) }
|
188
|
+
let(:expected_options) { super().merge({ key: 'value' }) }
|
189
|
+
|
190
|
+
it { expect(command.options).to be == expected_options }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe '#primary_key_name' do
|
195
|
+
include_examples 'should define reader', :primary_key_name, :id
|
196
|
+
|
197
|
+
context 'when initialized with a primary key name' do
|
198
|
+
let(:primary_key_name) { :uuid }
|
199
|
+
let(:constructor_options) do
|
200
|
+
super().merge({ primary_key_name: primary_key_name })
|
201
|
+
end
|
202
|
+
|
203
|
+
it { expect(command.primary_key_name).to be == primary_key_name }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe '#primary_key_type' do
|
208
|
+
include_examples 'should define reader', :primary_key_type, Integer
|
209
|
+
|
210
|
+
context 'when initialized with a primary key type' do
|
211
|
+
let(:primary_key_type) { String }
|
212
|
+
let(:constructor_options) do
|
213
|
+
super().merge({ primary_key_type: primary_key_type })
|
214
|
+
end
|
215
|
+
|
216
|
+
it { expect(command.primary_key_type).to be == primary_key_type }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe '#validate_primary_key' do
|
221
|
+
let(:primary_key_type) { Integer }
|
222
|
+
let(:expected_error) do
|
223
|
+
type = primary_key_type
|
224
|
+
contract = Stannum::Contracts::ParametersContract.new do
|
225
|
+
keyword :primary_key, type
|
226
|
+
end
|
227
|
+
errors = contract.errors_for(
|
228
|
+
{
|
229
|
+
arguments: [],
|
230
|
+
block: nil,
|
231
|
+
keywords: { primary_key: nil }
|
232
|
+
}
|
233
|
+
)
|
234
|
+
|
235
|
+
Cuprum::Collections::Errors::InvalidParameters.new(
|
236
|
+
command: command,
|
237
|
+
errors: errors
|
238
|
+
)
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'should define the private method' do
|
242
|
+
expect(command)
|
243
|
+
.to respond_to(:validate_primary_key, true)
|
244
|
+
.with(1).argument
|
245
|
+
end
|
246
|
+
|
247
|
+
describe 'with nil' do
|
248
|
+
it 'should return a failing result' do
|
249
|
+
expect(command.send(:validate_primary_key, nil))
|
250
|
+
.to be_a_failing_result
|
251
|
+
.with_error(expected_error)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe 'with an Object' do
|
256
|
+
it 'should return a failing result' do
|
257
|
+
expect(command.send(:validate_primary_key, Object.new.freeze))
|
258
|
+
.to be_a_failing_result
|
259
|
+
.with_error(expected_error)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
describe 'with a String' do
|
264
|
+
it 'should return a failing result' do
|
265
|
+
expect(command.send(:validate_primary_key, '12345'))
|
266
|
+
.to be_a_failing_result
|
267
|
+
.with_error(expected_error)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
describe 'with an Integer' do
|
272
|
+
it 'should not return a result' do
|
273
|
+
expect(command.send(:validate_primary_key, 12_345))
|
274
|
+
.not_to be_a_result
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
context 'when initialized with a primary key type' do
|
279
|
+
let(:primary_key_type) { String }
|
280
|
+
let(:constructor_options) do
|
281
|
+
super().merge({ primary_key_type: primary_key_type })
|
282
|
+
end
|
283
|
+
|
284
|
+
describe 'with an Integer' do
|
285
|
+
it 'should return a failing result' do
|
286
|
+
expect(command.send(:validate_primary_key, 12_345))
|
287
|
+
.to be_a_failing_result
|
288
|
+
.with_error(expected_error)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
describe 'with a String' do
|
293
|
+
it 'should not return a result' do
|
294
|
+
expect(command.send(:validate_primary_key, '12345'))
|
295
|
+
.not_to be_a_result
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
describe '#validate_primary_keys' do
|
302
|
+
let(:primary_keys) { nil }
|
303
|
+
let(:primary_key_type) { Integer }
|
304
|
+
let(:expected_error) do
|
305
|
+
type = primary_key_type
|
306
|
+
contract = Stannum::Contracts::ParametersContract.new do
|
307
|
+
keyword :primary_keys,
|
308
|
+
Stannum::Constraints::Types::ArrayType.new(item_type: type)
|
309
|
+
end
|
310
|
+
errors = contract.errors_for(
|
311
|
+
{
|
312
|
+
arguments: [],
|
313
|
+
block: nil,
|
314
|
+
keywords: { primary_keys: primary_keys }
|
315
|
+
}
|
316
|
+
)
|
317
|
+
|
318
|
+
Cuprum::Collections::Errors::InvalidParameters.new(
|
319
|
+
command: command,
|
320
|
+
errors: errors
|
321
|
+
)
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'should define the private method' do
|
325
|
+
expect(command)
|
326
|
+
.to respond_to(:validate_primary_keys, true)
|
327
|
+
.with(1).argument
|
328
|
+
end
|
329
|
+
|
330
|
+
describe 'with nil' do
|
331
|
+
it 'should return a failing result' do
|
332
|
+
expect(command.send(:validate_primary_keys, nil))
|
333
|
+
.to be_a_failing_result
|
334
|
+
.with_error(expected_error)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
describe 'with an Object' do
|
339
|
+
it 'should return a failing result' do
|
340
|
+
expect(command.send(:validate_primary_keys, Object.new.freeze))
|
341
|
+
.to be_a_failing_result
|
342
|
+
.with_error(expected_error)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
describe 'with a String' do
|
347
|
+
it 'should return a failing result' do
|
348
|
+
expect(command.send(:validate_primary_keys, '12345'))
|
349
|
+
.to be_a_failing_result
|
350
|
+
.with_error(expected_error)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
describe 'with an Integer' do
|
355
|
+
it 'should return a failing result' do
|
356
|
+
expect(command.send(:validate_primary_keys, 12_345))
|
357
|
+
.to be_a_failing_result
|
358
|
+
.with_error(expected_error)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
describe 'with an empty Array' do
|
363
|
+
it 'should not return a result' do
|
364
|
+
expect(command.send(:validate_primary_keys, []))
|
365
|
+
.not_to be_a_result
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
describe 'with an Array with nil values' do
|
370
|
+
let(:primary_keys) { Array.new(3, nil) }
|
371
|
+
|
372
|
+
it 'should return a failing result' do
|
373
|
+
expect(command.send(:validate_primary_keys, primary_keys))
|
374
|
+
.to be_a_failing_result
|
375
|
+
.with_error(expected_error)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
describe 'with an Array with Object values' do
|
380
|
+
let(:primary_keys) { Array.new(3) { Object.new.freeze } }
|
381
|
+
|
382
|
+
it 'should return a failing result' do
|
383
|
+
expect(command.send(:validate_primary_keys, primary_keys))
|
384
|
+
.to be_a_failing_result
|
385
|
+
.with_error(expected_error)
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
describe 'with an Array with String values' do
|
390
|
+
let(:primary_keys) { %w[ichi ni san] }
|
391
|
+
|
392
|
+
it 'should return a failing result' do
|
393
|
+
expect(command.send(:validate_primary_keys, primary_keys))
|
394
|
+
.to be_a_failing_result
|
395
|
+
.with_error(expected_error)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
describe 'with an Array with Integer values' do
|
400
|
+
it 'should not return a result' do
|
401
|
+
expect(command.send(:validate_primary_keys, [0, 1, 2]))
|
402
|
+
.not_to be_a_result
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# Contract defining contexts for validating basic commands.
|
410
|
+
module WithBasicCommandContextsContract
|
411
|
+
extend RSpec::SleepingKingStudios::Contract
|
412
|
+
|
413
|
+
# @!method apply(example_group)
|
414
|
+
# Adds the contract to the example group.
|
415
|
+
#
|
416
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
417
|
+
# which the contract is applied.
|
418
|
+
contract do
|
419
|
+
shared_context 'with parameters for a basic contract' do
|
420
|
+
let(:collection_name) { 'books' }
|
421
|
+
let(:data) { [] }
|
422
|
+
let(:mapped_data) { data }
|
423
|
+
let(:constructor_options) { {} }
|
424
|
+
let(:expected_options) { {} }
|
425
|
+
let(:primary_key_name) { :id }
|
426
|
+
let(:primary_key_type) { Integer }
|
427
|
+
let(:entity_type) do
|
428
|
+
Stannum::Constraints::Types::HashWithStringKeys.new
|
429
|
+
end
|
430
|
+
let(:fixtures_data) do
|
431
|
+
Cuprum::Collections::RSpec::Fixtures::BOOKS_FIXTURES.dup
|
432
|
+
end
|
433
|
+
let(:query) do
|
434
|
+
Cuprum::Collections::Basic::Query.new(mapped_data)
|
435
|
+
end
|
436
|
+
let(:scope) do
|
437
|
+
Cuprum::Collections::Basic::Query
|
438
|
+
.new(mapped_data).where(scope_filter)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
shared_context 'with a custom primary key' do
|
443
|
+
let(:primary_key_name) { :uuid }
|
444
|
+
let(:primary_key_type) { String }
|
445
|
+
let(:constructor_options) do
|
446
|
+
super().merge(
|
447
|
+
primary_key_name: primary_key_name,
|
448
|
+
primary_key_type: primary_key_type
|
449
|
+
)
|
450
|
+
end
|
451
|
+
let(:mapped_data) do
|
452
|
+
data.map do |item|
|
453
|
+
item.dup.tap do |hsh|
|
454
|
+
value = hsh.delete('id').to_s.rjust(12, '0')
|
455
|
+
|
456
|
+
hsh['uuid'] = "00000000-0000-0000-0000-#{value}"
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
let(:invalid_primary_key_value) do
|
461
|
+
'00000000-0000-0000-0000-000000000100'
|
462
|
+
end
|
463
|
+
let(:valid_primary_key_value) do
|
464
|
+
'00000000-0000-0000-0000-000000000000'
|
465
|
+
end
|
466
|
+
let(:invalid_primary_key_values) do
|
467
|
+
%w[
|
468
|
+
00000000-0000-0000-0000-000000000100
|
469
|
+
00000000-0000-0000-0000-000000000101
|
470
|
+
00000000-0000-0000-0000-000000000102
|
471
|
+
]
|
472
|
+
end
|
473
|
+
let(:valid_primary_key_values) do
|
474
|
+
%w[
|
475
|
+
00000000-0000-0000-0000-000000000000
|
476
|
+
00000000-0000-0000-0000-000000000001
|
477
|
+
00000000-0000-0000-0000-000000000002
|
478
|
+
]
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/rspec/contracts'
|
4
|
+
|
5
|
+
module Cuprum::Collections::RSpec::Contracts
|
6
|
+
# Namespace for RSpec contract objects for Basic collections.
|
7
|
+
module Basic
|
8
|
+
autoload :CommandContracts,
|
9
|
+
'cuprum/collections/rspec/contracts/basic/command_contracts'
|
10
|
+
end
|
11
|
+
end
|