cuprum-collections 0.3.0 → 0.4.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -0
  3. data/DEVELOPMENT.md +2 -2
  4. data/README.md +13 -11
  5. data/lib/cuprum/collections/association.rb +256 -0
  6. data/lib/cuprum/collections/associations/belongs_to.rb +32 -0
  7. data/lib/cuprum/collections/associations/has_many.rb +23 -0
  8. data/lib/cuprum/collections/associations/has_one.rb +23 -0
  9. data/lib/cuprum/collections/associations.rb +10 -0
  10. data/lib/cuprum/collections/basic/collection.rb +39 -74
  11. data/lib/cuprum/collections/basic/commands/find_many.rb +1 -1
  12. data/lib/cuprum/collections/basic/commands/find_matching.rb +1 -1
  13. data/lib/cuprum/collections/basic/repository.rb +9 -33
  14. data/lib/cuprum/collections/basic.rb +1 -0
  15. data/lib/cuprum/collections/collection.rb +154 -0
  16. data/lib/cuprum/collections/commands/associations/find_many.rb +161 -0
  17. data/lib/cuprum/collections/commands/associations/require_many.rb +48 -0
  18. data/lib/cuprum/collections/commands/associations.rb +13 -0
  19. data/lib/cuprum/collections/commands/find_one_matching.rb +1 -1
  20. data/lib/cuprum/collections/commands.rb +1 -0
  21. data/lib/cuprum/collections/errors/abstract_find_error.rb +1 -1
  22. data/lib/cuprum/collections/relation.rb +401 -0
  23. data/lib/cuprum/collections/repository.rb +71 -4
  24. data/lib/cuprum/collections/resource.rb +65 -0
  25. data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +2137 -0
  26. data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +484 -0
  27. data/lib/cuprum/collections/rspec/contracts/basic.rb +11 -0
  28. data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +429 -0
  29. data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +1462 -0
  30. data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +1093 -0
  31. data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +1381 -0
  32. data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +605 -0
  33. data/lib/cuprum/collections/rspec/contracts.rb +23 -0
  34. data/lib/cuprum/collections/rspec/fixtures.rb +85 -82
  35. data/lib/cuprum/collections/rspec.rb +4 -1
  36. data/lib/cuprum/collections/version.rb +3 -3
  37. data/lib/cuprum/collections.rb +9 -4
  38. metadata +25 -21
  39. data/lib/cuprum/collections/base.rb +0 -11
  40. data/lib/cuprum/collections/basic/rspec/command_contract.rb +0 -392
  41. data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +0 -168
  42. data/lib/cuprum/collections/rspec/build_one_command_contract.rb +0 -93
  43. data/lib/cuprum/collections/rspec/collection_contract.rb +0 -190
  44. data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +0 -108
  45. data/lib/cuprum/collections/rspec/find_many_command_contract.rb +0 -407
  46. data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +0 -194
  47. data/lib/cuprum/collections/rspec/find_one_command_contract.rb +0 -157
  48. data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +0 -84
  49. data/lib/cuprum/collections/rspec/query_builder_contract.rb +0 -92
  50. data/lib/cuprum/collections/rspec/query_contract.rb +0 -650
  51. data/lib/cuprum/collections/rspec/querying_contract.rb +0 -298
  52. data/lib/cuprum/collections/rspec/repository_contract.rb +0 -235
  53. data/lib/cuprum/collections/rspec/update_one_command_contract.rb +0 -80
  54. 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