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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/lib/cuprum/collections/adaptable/collection.rb +18 -0
  4. data/lib/cuprum/collections/adaptable/command.rb +22 -0
  5. data/lib/cuprum/collections/adaptable/commands/abstract_assign_one.rb +27 -0
  6. data/lib/cuprum/collections/adaptable/commands/abstract_build_one.rb +25 -0
  7. data/lib/cuprum/collections/adaptable/commands/abstract_validate_one.rb +35 -0
  8. data/lib/cuprum/collections/adaptable/commands.rb +15 -0
  9. data/lib/cuprum/collections/adaptable/query.rb +64 -0
  10. data/lib/cuprum/collections/adaptable.rb +13 -0
  11. data/lib/cuprum/collections/adapter.rb +300 -0
  12. data/lib/cuprum/collections/adapters/data_adapter.rb +82 -0
  13. data/lib/cuprum/collections/adapters/entity_adapter.rb +76 -0
  14. data/lib/cuprum/collections/adapters/hash_adapter.rb +48 -0
  15. data/lib/cuprum/collections/adapters.rb +14 -0
  16. data/lib/cuprum/collections/basic/collection.rb +2 -20
  17. data/lib/cuprum/collections/basic/commands/destroy_one.rb +1 -1
  18. data/lib/cuprum/collections/basic/commands/find_many.rb +0 -31
  19. data/lib/cuprum/collections/basic/commands/find_matching.rb +0 -94
  20. data/lib/cuprum/collections/basic/commands/find_one.rb +0 -18
  21. data/lib/cuprum/collections/basic/commands/insert_one.rb +1 -1
  22. data/lib/cuprum/collections/basic/commands/update_one.rb +1 -1
  23. data/lib/cuprum/collections/basic/scopes/criteria_scope.rb +36 -21
  24. data/lib/cuprum/collections/basic.rb +6 -5
  25. data/lib/cuprum/collections/collection.rb +6 -0
  26. data/lib/cuprum/collections/collection_command.rb +1 -1
  27. data/lib/cuprum/collections/commands/abstract_find_many.rb +40 -3
  28. data/lib/cuprum/collections/commands/abstract_find_matching.rb +102 -0
  29. data/lib/cuprum/collections/commands/abstract_find_one.rb +23 -1
  30. data/lib/cuprum/collections/commands/associations/find_many.rb +1 -3
  31. data/lib/cuprum/collections/commands/associations/require_many.rb +1 -1
  32. data/lib/cuprum/collections/commands/find_one_matching.rb +10 -10
  33. data/lib/cuprum/collections/commands/query_command.rb +6 -4
  34. data/lib/cuprum/collections/commands/upsert.rb +0 -2
  35. data/lib/cuprum/collections/constraints/order/attributes_array.rb +5 -4
  36. data/lib/cuprum/collections/constraints/order/attributes_hash.rb +5 -4
  37. data/lib/cuprum/collections/constraints/order/sort_direction.rb +2 -2
  38. data/lib/cuprum/collections/constraints/ordering.rb +11 -9
  39. data/lib/cuprum/collections/constraints/query_hash.rb +2 -2
  40. data/lib/cuprum/collections/errors/abstract_find_error.rb +101 -23
  41. data/lib/cuprum/collections/errors/extra_attributes.rb +3 -3
  42. data/lib/cuprum/collections/errors/failed_validation.rb +3 -3
  43. data/lib/cuprum/collections/errors/missing_default_contract.rb +12 -4
  44. data/lib/cuprum/collections/queries.rb +4 -0
  45. data/lib/cuprum/collections/relation.rb +0 -2
  46. data/lib/cuprum/collections/relations/parameters.rb +120 -68
  47. data/lib/cuprum/collections/repository.rb +71 -6
  48. data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +23 -4
  49. data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +18 -0
  50. data/lib/cuprum/collections/rspec/contracts/scope_contracts.rb +51 -0
  51. data/lib/cuprum/collections/rspec/contracts/scopes/builder_contracts.rb +10 -0
  52. data/lib/cuprum/collections/rspec/contracts/scopes/composition_contracts.rb +8 -0
  53. data/lib/cuprum/collections/rspec/contracts/scopes/criteria_contracts.rb +18 -366
  54. data/lib/cuprum/collections/rspec/contracts/scopes/logical_contracts.rb +30 -0
  55. data/lib/cuprum/collections/rspec/contracts/scopes.rb +2 -0
  56. data/lib/cuprum/collections/rspec/contracts.rb +2 -10
  57. data/lib/cuprum/collections/rspec/deferred/adapter_examples.rb +1077 -0
  58. data/lib/cuprum/collections/rspec/deferred/collection_examples.rb +27 -7
  59. data/lib/cuprum/collections/rspec/deferred/commands/assign_one_examples.rb +4 -4
  60. data/lib/cuprum/collections/rspec/deferred/commands/build_one_examples.rb +2 -2
  61. data/lib/cuprum/collections/rspec/deferred/commands/destroy_one_examples.rb +2 -2
  62. data/lib/cuprum/collections/rspec/deferred/commands/find_many_examples.rb +5 -5
  63. data/lib/cuprum/collections/rspec/deferred/commands/find_matching_examples.rb +45 -12
  64. data/lib/cuprum/collections/rspec/deferred/commands/find_one_examples.rb +2 -2
  65. data/lib/cuprum/collections/rspec/deferred/commands/insert_one_examples.rb +1 -1
  66. data/lib/cuprum/collections/rspec/deferred/commands/update_one_examples.rb +1 -1
  67. data/lib/cuprum/collections/rspec/deferred/query_examples.rb +930 -0
  68. data/lib/cuprum/collections/rspec/deferred/relation_examples.rb +48 -17
  69. data/lib/cuprum/collections/rspec/deferred/repository_examples.rb +961 -0
  70. data/lib/cuprum/collections/rspec/deferred/scope_examples.rb +598 -0
  71. data/lib/cuprum/collections/rspec/deferred/scopes/all_examples.rb +391 -0
  72. data/lib/cuprum/collections/rspec/deferred/scopes/builder_examples.rb +857 -0
  73. data/lib/cuprum/collections/rspec/deferred/scopes/composition_examples.rb +93 -0
  74. data/lib/cuprum/collections/rspec/deferred/scopes/conjunction_examples.rb +438 -0
  75. data/lib/cuprum/collections/rspec/deferred/scopes/criteria_examples.rb +1941 -0
  76. data/lib/cuprum/collections/rspec/deferred/scopes/disjunction_examples.rb +415 -0
  77. data/lib/cuprum/collections/rspec/deferred/scopes/none_examples.rb +385 -0
  78. data/lib/cuprum/collections/rspec/deferred/scopes/parser_examples.rb +740 -0
  79. data/lib/cuprum/collections/rspec/deferred/scopes.rb +8 -0
  80. data/lib/cuprum/collections/scope.rb +2 -2
  81. data/lib/cuprum/collections/scopes/container.rb +5 -4
  82. data/lib/cuprum/collections/scopes/criteria/parser.rb +24 -48
  83. data/lib/cuprum/collections/scopes/criteria.rb +7 -6
  84. data/lib/cuprum/collections/version.rb +4 -4
  85. data/lib/cuprum/collections.rb +5 -1
  86. metadata +47 -11
  87. data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +0 -2127
  88. data/lib/cuprum/collections/rspec/contracts/basic.rb +0 -11
  89. data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +0 -387
  90. data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +0 -169
  91. data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +0 -1264
@@ -0,0 +1,1077 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/sleeping_king_studios/deferred/provider'
4
+
5
+ require 'cuprum/collections/rspec/deferred'
6
+
7
+ module Cuprum::Collections::RSpec::Deferred
8
+ # Deferred examples for testing collection adapters.
9
+ module AdapterExamples
10
+ include RSpec::SleepingKingStudios::Deferred::Provider
11
+
12
+ deferred_context 'when initialized with attribute names' do
13
+ let(:configured_attribute_names) do
14
+ next valid_attribute_names if defined?(valid_attribute_names)
15
+
16
+ %w[title author series]
17
+ end
18
+ let(:constructor_options) do
19
+ super().merge(attribute_names: configured_attribute_names)
20
+ end
21
+ end
22
+
23
+ deferred_context 'with parameters for verifying adapters' do
24
+ let(:configured_invalid_attribute_names) do
25
+ next invalid_attribute_names if defined?(invalid_attribute_names)
26
+
27
+ %w[invalid_attribute other_invalid_attribute]
28
+ end
29
+ let(:configured_invalid_attributes) do
30
+ next invalid_attributes if defined?(invalid_attributes)
31
+
32
+ {}
33
+ end
34
+ let(:configured_partial_attributes) do
35
+ next partial_attributes if defined?(partial_attributes)
36
+
37
+ { series: 'The Locked Tomb' }
38
+ end
39
+ let(:configured_valid_attributes) do
40
+ next valid_attributes if defined?(valid_attributes)
41
+
42
+ {
43
+ title: 'Gideon the Ninth',
44
+ author: 'Tamsyn Muir'
45
+ }
46
+ end
47
+ let(:configured_invalid_entity) do
48
+ next invalid_entity if defined?(invalid_entity)
49
+
50
+ build_entity(configured_invalid_attributes)
51
+ end
52
+ let(:configured_valid_entity) do
53
+ next valid_entity if defined?(valid_entity)
54
+
55
+ build_entity(configured_valid_attributes)
56
+ end
57
+ let(:configured_contract) do
58
+ next contract if defined?(contract)
59
+
60
+ type = 'spec.example_constraint'
61
+ message = 'is not a valid data object'
62
+
63
+ Stannum::Constraint.new(message:, type:) do |entity|
64
+ value =
65
+ if entity.respond_to?(:title)
66
+ entity.title
67
+ elsif entity.respond_to?(:[])
68
+ entity[:title] || entity['title']
69
+ end
70
+
71
+ value.is_a?(String) && !value.empty?
72
+ end
73
+ end
74
+ end
75
+
76
+ deferred_examples 'should implement the Adapter interface' do
77
+ describe '#allow_extra_attributes?' do
78
+ include_examples 'should define predicate', :allow_extra_attributes?
79
+ end
80
+
81
+ describe '#attribute_names' do
82
+ include_examples 'should define reader',
83
+ :attribute_names,
84
+ lambda {
85
+ be_a(Set)
86
+ .and(satisfy { |set| set.all? { |item| item.is_a?(String) } })
87
+ }
88
+ end
89
+
90
+ describe '#build' do
91
+ let(:attributes) { configured_valid_attributes }
92
+
93
+ define_method :call_adapter_method do
94
+ subject.build(attributes:)
95
+ end
96
+
97
+ include_deferred 'with parameters for verifying adapters'
98
+
99
+ it 'should define the method' do
100
+ expect(subject)
101
+ .to respond_to(:build)
102
+ .with(0).arguments
103
+ .and_keywords(:attributes)
104
+ end
105
+
106
+ it 'should return a result' do
107
+ expect(call_adapter_method).to be_a_result
108
+ end
109
+
110
+ describe 'with attributes: nil' do
111
+ let(:attributes) { nil }
112
+
113
+ include_deferred 'should validate the attributes parameter'
114
+ end
115
+
116
+ describe 'with attributes: an Object' do
117
+ let(:attributes) { Object.new.freeze }
118
+
119
+ include_deferred 'should validate the attributes parameter'
120
+ end
121
+
122
+ describe 'with attributes: a Hash with invalid keys' do
123
+ let(:attributes) do
124
+ super().merge({
125
+ nil => 'null',
126
+ '' => 'blank',
127
+ 10_000 => 'km'
128
+ })
129
+ end
130
+ let(:expected_failures) do
131
+ [
132
+ "attributes[nil] key can't be blank",
133
+ "attributes[\"\"] key can't be blank",
134
+ 'attributes[10000] key is not a String or a Symbol'
135
+ ]
136
+ end
137
+
138
+ include_deferred 'should validate the attributes parameter'
139
+ end
140
+ end
141
+
142
+ describe '#default_contract' do
143
+ include_examples 'should define reader', :default_contract
144
+ end
145
+
146
+ describe '#entity_class' do
147
+ include_examples 'should define reader', :entity_class
148
+ end
149
+
150
+ describe '#merge' do
151
+ let(:attributes) { configured_partial_attributes }
152
+ let(:entity) { configured_valid_entity }
153
+
154
+ define_method :call_adapter_method do
155
+ subject.merge(attributes:, entity:)
156
+ end
157
+
158
+ include_deferred 'with parameters for verifying adapters'
159
+
160
+ it 'should define the method' do
161
+ expect(subject)
162
+ .to respond_to(:merge)
163
+ .with(0).arguments
164
+ .and_keywords(:attributes, :entity)
165
+ end
166
+
167
+ it 'should return a result' do
168
+ expect(call_adapter_method).to be_a_result
169
+ end
170
+
171
+ describe 'with attributes: nil' do
172
+ let(:attributes) { nil }
173
+
174
+ include_deferred 'should validate the attributes parameter'
175
+ end
176
+
177
+ describe 'with attributes: an Object' do
178
+ let(:attributes) { Object.new.freeze }
179
+
180
+ include_deferred 'should validate the attributes parameter'
181
+ end
182
+
183
+ describe 'with attributes: a Hash with invalid keys' do
184
+ let(:attributes) do
185
+ super().merge({
186
+ nil => 'null',
187
+ '' => 'blank',
188
+ 10_000 => 'km'
189
+ })
190
+ end
191
+ let(:expected_failures) do
192
+ [
193
+ "attributes[nil] key can't be blank",
194
+ "attributes[\"\"] key can't be blank",
195
+ 'attributes[10000] key is not a String or a Symbol'
196
+ ]
197
+ end
198
+
199
+ include_deferred 'should validate the attributes parameter'
200
+ end
201
+ end
202
+
203
+ describe '#serialize' do
204
+ let(:entity) { configured_valid_entity }
205
+
206
+ define_method :call_adapter_method do
207
+ subject.serialize(entity:)
208
+ end
209
+
210
+ include_deferred 'with parameters for verifying adapters'
211
+
212
+ it 'should define the method' do
213
+ expect(subject)
214
+ .to respond_to(:serialize)
215
+ .with(0).arguments
216
+ .and_keywords(:entity)
217
+ end
218
+
219
+ it 'should return a result' do
220
+ expect(call_adapter_method).to be_a_result
221
+ end
222
+ end
223
+
224
+ describe '#validate' do
225
+ let(:entity) { configured_valid_entity }
226
+ let(:options) { {} }
227
+
228
+ define_method :call_adapter_method do
229
+ subject.validate(entity:, **options)
230
+ end
231
+
232
+ include_deferred 'with parameters for verifying adapters'
233
+
234
+ it 'should define the method' do
235
+ expect(subject)
236
+ .to respond_to(:validate)
237
+ .with(0).arguments
238
+ .and_keywords(:contract, :entity)
239
+ end
240
+
241
+ it 'should return a result' do
242
+ expect(call_adapter_method).to be_a_result
243
+ end
244
+ end
245
+
246
+ describe '#validate_attributes_parameter' do
247
+ it 'should define the method' do
248
+ expect(subject)
249
+ .to respond_to(:validate_attributes_parameter)
250
+ .with(1).argument
251
+ .and_keywords(:as)
252
+ end
253
+ end
254
+
255
+ describe '#validate_entity_parameter' do
256
+ it 'should define the method' do
257
+ expect(subject)
258
+ .to respond_to(:validate_entity_parameter)
259
+ .with(1).argument
260
+ .and_keywords(:as)
261
+ end
262
+ end
263
+ end
264
+
265
+ deferred_examples 'should implement the Adapter methods' do
266
+ describe '#build' do
267
+ let(:attributes) { configured_valid_attributes }
268
+ let(:expected_value) { build_entity(attributes) }
269
+
270
+ define_method :call_adapter_method do
271
+ subject.build(attributes:)
272
+ end
273
+
274
+ include_deferred 'with parameters for verifying adapters'
275
+
276
+ describe 'with an empty Hash' do
277
+ let(:attributes) { {} }
278
+
279
+ it 'should return a passing result' do
280
+ expect(call_adapter_method)
281
+ .to be_a_passing_result
282
+ .with_value(expected_value)
283
+ end
284
+ end
285
+
286
+ describe 'with a Hash with String keys' do
287
+ let(:attributes) { super().transform_keys(&:to_s) }
288
+
289
+ it 'should return a passing result' do
290
+ expect(call_adapter_method)
291
+ .to be_a_passing_result
292
+ .with_value(expected_value)
293
+ end
294
+ end
295
+
296
+ describe 'with a Hash with Symbol keys' do
297
+ let(:attributes) { super().transform_keys(&:to_sym) }
298
+
299
+ it 'should return a passing result' do
300
+ expect(call_adapter_method)
301
+ .to be_a_passing_result
302
+ .with_value(expected_value)
303
+ end
304
+ end
305
+ end
306
+
307
+ describe '#merge' do
308
+ let(:attributes) { configured_partial_attributes }
309
+ let(:entity) { configured_valid_entity }
310
+ let(:expected_attributes) do
311
+ configured_valid_attributes.merge(attributes)
312
+ end
313
+ let(:expected_value) { build_entity(expected_attributes) }
314
+
315
+ define_method :call_adapter_method do
316
+ subject.merge(attributes:, entity:)
317
+ end
318
+
319
+ include_deferred 'with parameters for verifying adapters'
320
+
321
+ describe 'with an empty Hash' do
322
+ let(:attributes) { {} }
323
+
324
+ it 'should return a passing result' do
325
+ expect(call_adapter_method)
326
+ .to be_a_passing_result
327
+ .with_value(expected_value)
328
+ end
329
+ end
330
+
331
+ describe 'with a Hash with String keys' do
332
+ let(:attributes) { super().transform_keys(&:to_s) }
333
+
334
+ it 'should return a passing result' do
335
+ expect(call_adapter_method)
336
+ .to be_a_passing_result
337
+ .with_value(expected_value)
338
+ end
339
+ end
340
+
341
+ describe 'with a Hash with Symbol keys' do
342
+ let(:attributes) { super().transform_keys(&:to_sym) }
343
+
344
+ it 'should return a passing result' do
345
+ expect(call_adapter_method)
346
+ .to be_a_passing_result
347
+ .with_value(expected_value)
348
+ end
349
+ end
350
+ end
351
+
352
+ describe '#serialize' do
353
+ let(:entity) { configured_valid_entity }
354
+ let(:expected_value) { serialize_entity(entity).transform_keys(&:to_s) }
355
+
356
+ define_method :call_adapter_method do
357
+ subject.serialize(entity:)
358
+ end
359
+
360
+ include_deferred 'with parameters for verifying adapters'
361
+
362
+ it 'should return a passing result' do
363
+ expect(call_adapter_method)
364
+ .to be_a_passing_result
365
+ .with_value(expected_value)
366
+ end
367
+ end
368
+
369
+ describe '#validate_attributes_parameter' do
370
+ describe 'with nil' do
371
+ let(:error_message) do
372
+ SleepingKingStudios::Tools::Toolbelt
373
+ .instance
374
+ .assertions
375
+ .error_message_for(
376
+ 'sleeping_king_studios.tools.assertions.instance_of',
377
+ as: 'attributes',
378
+ expected: Hash
379
+ )
380
+ end
381
+
382
+ it 'should return the error message' do
383
+ expect(adapter.validate_attributes_parameter(nil))
384
+ .to be == error_message
385
+ end
386
+ end
387
+
388
+ describe 'with an Object' do
389
+ let(:error_message) do
390
+ SleepingKingStudios::Tools::Toolbelt
391
+ .instance
392
+ .assertions
393
+ .error_message_for(
394
+ 'sleeping_king_studios.tools.assertions.instance_of',
395
+ as: 'attributes',
396
+ expected: Hash
397
+ )
398
+ end
399
+
400
+ it 'should return the error message' do
401
+ expect(adapter.validate_attributes_parameter(Object.new.freeze))
402
+ .to be == error_message
403
+ end
404
+ end
405
+
406
+ describe 'with an attributes Hash with invalid keys' do
407
+ let(:attributes) do
408
+ {
409
+ nil => 'null',
410
+ '' => 'blank',
411
+ 10_000 => 'km'
412
+ }
413
+ end
414
+ let(:error_messages) do
415
+ [
416
+ "attributes[nil] key can't be blank",
417
+ "attributes[\"\"] key can't be blank",
418
+ 'attributes[10000] key is not a String or a Symbol'
419
+ ]
420
+ end
421
+
422
+ it 'should return the error message' do
423
+ expect(adapter.validate_attributes_parameter(attributes))
424
+ .to be == error_messages
425
+ end
426
+ end
427
+ end
428
+ end
429
+
430
+ deferred_examples 'should validate the attributes parameter' do
431
+ let(:configured_failures) do
432
+ next expected_failures if defined?(expected_failures)
433
+
434
+ message =
435
+ SleepingKingStudios::Tools::Toolbelt
436
+ .instance
437
+ .assertions
438
+ .error_message_for(
439
+ 'sleeping_king_studios.tools.assertions.instance_of',
440
+ as: 'attributes',
441
+ expected: Hash
442
+ )
443
+
444
+ [message]
445
+ end
446
+ let(:expected_error) do
447
+ Cuprum::Errors::InvalidParameters.new(
448
+ command_class: described_class,
449
+ failures: configured_failures
450
+ )
451
+ end
452
+
453
+ it 'should return a failing result' do
454
+ expect(call_adapter_method)
455
+ .to be_a_failing_result
456
+ .with_error(expected_error)
457
+ end
458
+ end
459
+
460
+ deferred_examples 'should validate the attribute names' do
461
+ let(:expected_error) do
462
+ Cuprum::Collections::Errors::ExtraAttributes.new(
463
+ entity_class: adapter.entity_class,
464
+ extra_attributes: attributes.keys,
465
+ valid_attributes: adapter.attribute_names.to_a
466
+ )
467
+ end
468
+
469
+ it 'should return a failing result' do
470
+ expect(call_adapter_method)
471
+ .to be_a_failing_result
472
+ .with_error(expected_error)
473
+ end
474
+ end
475
+
476
+ deferred_examples 'should validate the attribute names by entity class' do
477
+ describe '#build' do
478
+ let(:attributes) do
479
+ configured_invalid_attribute_names.to_h { |key| [key, 'value'] }
480
+ end
481
+
482
+ define_method :call_adapter_method do
483
+ subject.build(attributes:)
484
+ end
485
+
486
+ include_deferred 'with parameters for verifying adapters'
487
+
488
+ describe 'with an attributes Hash with extra Strings' do
489
+ let(:attributes) { super().transform_keys(&:to_s) }
490
+
491
+ include_deferred 'should validate the attribute names'
492
+ end
493
+
494
+ describe 'with an attributes Hash with extra Symbols' do
495
+ let(:attributes) { super().transform_keys(&:to_sym) }
496
+
497
+ include_deferred 'should validate the attribute names'
498
+ end
499
+ end
500
+
501
+ describe '#merge' do
502
+ let(:attributes) do
503
+ configured_invalid_attribute_names.to_h { |key| [key, 'value'] }
504
+ end
505
+ let(:entity) { configured_valid_entity }
506
+
507
+ define_method :call_adapter_method do
508
+ subject.merge(attributes:, entity:)
509
+ end
510
+
511
+ include_deferred 'with parameters for verifying adapters'
512
+
513
+ describe 'with an attributes Hash with extra Strings' do
514
+ let(:attributes) { super().transform_keys(&:to_s) }
515
+
516
+ include_deferred 'should validate the attribute names'
517
+ end
518
+
519
+ describe 'with an attributes Hash with extra Symbols' do
520
+ let(:attributes) { super().transform_keys(&:to_sym) }
521
+
522
+ include_deferred 'should validate the attribute names'
523
+ end
524
+ end
525
+ end
526
+
527
+ deferred_examples 'should validate the attribute names by parameter' do
528
+ describe '#build' do
529
+ let(:attributes) do
530
+ configured_invalid_attribute_names.to_h { |key| [key, 'value'] }
531
+ end
532
+
533
+ define_method :call_adapter_method do
534
+ subject.build(attributes:)
535
+ end
536
+
537
+ include_deferred 'with parameters for verifying adapters'
538
+
539
+ wrap_deferred 'when initialized with attribute names' do
540
+ describe 'with an attributes Hash with extra Strings' do
541
+ let(:attributes) { super().transform_keys(&:to_s) }
542
+
543
+ include_deferred 'should validate the attribute names'
544
+ end
545
+
546
+ describe 'with an attributes Hash with extra Symbols' do
547
+ let(:attributes) { super().transform_keys(&:to_sym) }
548
+
549
+ include_deferred 'should validate the attribute names'
550
+ end
551
+ end
552
+ end
553
+
554
+ describe '#merge' do
555
+ let(:attributes) do
556
+ configured_invalid_attribute_names.to_h { |key| [key, 'value'] }
557
+ end
558
+ let(:entity) { configured_valid_entity }
559
+
560
+ define_method :call_adapter_method do
561
+ subject.merge(attributes:, entity:)
562
+ end
563
+
564
+ include_deferred 'with parameters for verifying adapters'
565
+
566
+ wrap_deferred 'when initialized with attribute names' do
567
+ describe 'with an attributes Hash with extra Strings' do
568
+ let(:attributes) { super().transform_keys(&:to_s) }
569
+
570
+ include_deferred 'should validate the attribute names'
571
+ end
572
+
573
+ describe 'with an attributes Hash with extra Symbols' do
574
+ let(:attributes) { super().transform_keys(&:to_sym) }
575
+
576
+ include_deferred 'should validate the attribute names'
577
+ end
578
+ end
579
+ end
580
+ end
581
+
582
+ deferred_examples 'should validate the entity parameter' do
583
+ let(:configured_failures) do
584
+ next expected_failures if defined?(expected_failures)
585
+
586
+ message =
587
+ SleepingKingStudios::Tools::Toolbelt
588
+ .instance
589
+ .assertions
590
+ .error_message_for(
591
+ 'sleeping_king_studios.tools.assertions.instance_of',
592
+ as: 'entity',
593
+ expected: adapter.entity_class
594
+ )
595
+
596
+ [message]
597
+ end
598
+ let(:expected_error) do
599
+ failures = configured_failures
600
+
601
+ Cuprum::Errors::InvalidParameters.new(
602
+ command_class: described_class,
603
+ failures:
604
+ )
605
+ end
606
+
607
+ it 'should return a failing result' do
608
+ expect(call_adapter_method)
609
+ .to be_a_failing_result
610
+ .with_error(expected_error)
611
+ end
612
+ end
613
+
614
+ deferred_examples 'should validate the entity by fixed class' do
615
+ describe '#merge' do
616
+ let(:attributes) { configured_partial_attributes }
617
+ let(:entity) { configured_valid_entity }
618
+
619
+ define_method :call_adapter_method do
620
+ subject.merge(attributes:, entity:)
621
+ end
622
+
623
+ include_deferred 'with parameters for verifying adapters'
624
+
625
+ describe 'with entity: nil' do
626
+ let(:entity) { nil }
627
+
628
+ include_deferred 'should validate the entity parameter'
629
+ end
630
+
631
+ describe 'with entity: an Object' do
632
+ let(:entity) { Object.new.freeze }
633
+
634
+ include_deferred 'should validate the entity parameter'
635
+ end
636
+ end
637
+
638
+ describe '#serialize' do
639
+ let(:entity) { configured_valid_entity }
640
+
641
+ define_method :call_adapter_method do
642
+ subject.serialize(entity:)
643
+ end
644
+
645
+ include_deferred 'with parameters for verifying adapters'
646
+
647
+ describe 'with entity: nil' do
648
+ let(:entity) { nil }
649
+
650
+ include_deferred 'should validate the entity parameter'
651
+ end
652
+
653
+ describe 'with entity: an Object' do
654
+ let(:entity) { Object.new.freeze }
655
+
656
+ include_deferred 'should validate the entity parameter'
657
+ end
658
+ end
659
+
660
+ describe '#validate' do
661
+ let(:entity) { configured_valid_entity }
662
+ let(:options) { {} }
663
+
664
+ define_method :call_adapter_method do
665
+ subject.validate(entity:, **options)
666
+ end
667
+
668
+ include_deferred 'with parameters for verifying adapters'
669
+
670
+ describe 'with entity: nil' do
671
+ let(:entity) { nil }
672
+
673
+ include_deferred 'should validate the entity parameter'
674
+ end
675
+
676
+ describe 'with entity: an Object' do
677
+ let(:entity) { Object.new.freeze }
678
+
679
+ include_deferred 'should validate the entity parameter'
680
+ end
681
+ end
682
+
683
+ describe '#validate_entity_parameter' do
684
+ let(:configured_failures) do
685
+ next expected_failures if defined?(expected_failures)
686
+
687
+ message =
688
+ SleepingKingStudios::Tools::Toolbelt
689
+ .instance
690
+ .assertions
691
+ .error_message_for(
692
+ 'sleeping_king_studios.tools.assertions.instance_of',
693
+ as: 'entity',
694
+ expected: adapter.entity_class
695
+ )
696
+
697
+ [message]
698
+ end
699
+
700
+ include_deferred 'with parameters for verifying adapters'
701
+
702
+ describe 'with entity: nil' do
703
+ let(:entity) { nil }
704
+
705
+ it 'should return the failure message' do
706
+ expect(adapter.validate_entity_parameter(entity))
707
+ .to be == configured_failures.join(', ')
708
+ end
709
+ end
710
+
711
+ describe 'with entity: an Object' do
712
+ let(:entity) { Object.new.freeze }
713
+
714
+ it 'should return the failure message' do
715
+ expect(adapter.validate_entity_parameter(entity))
716
+ .to be == configured_failures.join(', ')
717
+ end
718
+ end
719
+
720
+ describe 'with entity: a valid entity' do
721
+ let(:entity) { configured_valid_entity }
722
+
723
+ it { expect(adapter.validate_entity_parameter(entity)).to be nil }
724
+ end
725
+ end
726
+ end
727
+
728
+ deferred_examples 'should validate the entity by optional parameter' do
729
+ describe '#merge' do
730
+ let(:attributes) { configured_partial_attributes }
731
+ let(:entity) { configured_valid_entity }
732
+
733
+ define_method :call_adapter_method do
734
+ subject.merge(attributes:, entity:)
735
+ end
736
+
737
+ include_deferred 'with parameters for verifying adapters'
738
+
739
+ wrap_deferred 'when initialized with an entity class' do
740
+ describe 'with entity: nil' do
741
+ let(:entity) { nil }
742
+
743
+ include_deferred 'should validate the entity parameter'
744
+ end
745
+
746
+ describe 'with entity: an Object' do
747
+ let(:entity) { Object.new.freeze }
748
+
749
+ include_deferred 'should validate the entity parameter'
750
+ end
751
+ end
752
+ end
753
+
754
+ describe '#serialize' do
755
+ let(:entity) { configured_valid_entity }
756
+
757
+ define_method :call_adapter_method do
758
+ subject.serialize(entity:)
759
+ end
760
+
761
+ include_deferred 'with parameters for verifying adapters'
762
+
763
+ wrap_deferred 'when initialized with an entity class' do
764
+ describe 'with entity: nil' do
765
+ let(:entity) { nil }
766
+
767
+ include_deferred 'should validate the entity parameter'
768
+ end
769
+
770
+ describe 'with entity: an Object' do
771
+ let(:entity) { Object.new.freeze }
772
+
773
+ include_deferred 'should validate the entity parameter'
774
+ end
775
+ end
776
+ end
777
+
778
+ describe '#validate' do
779
+ let(:entity) { configured_valid_entity }
780
+ let(:options) { {} }
781
+
782
+ define_method :call_adapter_method do
783
+ subject.validate(entity:, **options)
784
+ end
785
+
786
+ include_deferred 'with parameters for verifying adapters'
787
+
788
+ wrap_deferred 'when initialized with an entity class' do
789
+ describe 'with entity: nil' do
790
+ let(:entity) { nil }
791
+
792
+ include_deferred 'should validate the entity parameter'
793
+ end
794
+
795
+ describe 'with entity: an Object' do
796
+ let(:entity) { Object.new.freeze }
797
+
798
+ include_deferred 'should validate the entity parameter'
799
+ end
800
+ end
801
+ end
802
+
803
+ describe '#validate_entity_parameter' do
804
+ let(:configured_failures) do
805
+ next expected_failures if defined?(expected_failures)
806
+
807
+ message =
808
+ SleepingKingStudios::Tools::Toolbelt
809
+ .instance
810
+ .assertions
811
+ .error_message_for(
812
+ 'sleeping_king_studios.tools.assertions.instance_of',
813
+ as: 'entity',
814
+ expected: adapter.entity_class
815
+ )
816
+
817
+ [message]
818
+ end
819
+
820
+ include_deferred 'with parameters for verifying adapters'
821
+
822
+ wrap_deferred 'when initialized with an entity class' do
823
+ describe 'with entity: nil' do
824
+ let(:entity) { nil }
825
+
826
+ it 'should return the failure message' do
827
+ expect(adapter.validate_entity_parameter(entity))
828
+ .to be == configured_failures.join(', ')
829
+ end
830
+ end
831
+
832
+ describe 'with entity: an Object' do
833
+ let(:entity) { Object.new.freeze }
834
+
835
+ it 'should return the failure message' do
836
+ expect(adapter.validate_entity_parameter(entity))
837
+ .to be == configured_failures.join(', ')
838
+ end
839
+ end
840
+
841
+ describe 'with entity: a valid entity' do
842
+ let(:entity) { configured_valid_entity }
843
+
844
+ it { expect(adapter.validate_entity_parameter(entity)).to be nil }
845
+ end
846
+ end
847
+ end
848
+ end
849
+
850
+ deferred_examples 'should validate the entity by required parameter' do
851
+ describe '#merge' do
852
+ let(:attributes) { configured_partial_attributes }
853
+ let(:entity) { configured_valid_entity }
854
+
855
+ define_method :call_adapter_method do
856
+ subject.merge(attributes:, entity:)
857
+ end
858
+
859
+ include_deferred 'with parameters for verifying adapters'
860
+
861
+ describe 'with entity: nil' do
862
+ let(:entity) { nil }
863
+
864
+ include_deferred 'should validate the entity parameter'
865
+ end
866
+
867
+ describe 'with entity: an Object' do
868
+ let(:entity) { Object.new.freeze }
869
+
870
+ include_deferred 'should validate the entity parameter'
871
+ end
872
+ end
873
+
874
+ describe '#serialize' do
875
+ let(:entity) { configured_valid_entity }
876
+
877
+ define_method :call_adapter_method do
878
+ subject.serialize(entity:)
879
+ end
880
+
881
+ include_deferred 'with parameters for verifying adapters'
882
+
883
+ describe 'with entity: nil' do
884
+ let(:entity) { nil }
885
+
886
+ include_deferred 'should validate the entity parameter'
887
+ end
888
+
889
+ describe 'with entity: an Object' do
890
+ let(:entity) { Object.new.freeze }
891
+
892
+ include_deferred 'should validate the entity parameter'
893
+ end
894
+ end
895
+
896
+ describe '#validate' do
897
+ let(:entity) { configured_valid_entity }
898
+ let(:options) { {} }
899
+
900
+ define_method :call_adapter_method do
901
+ subject.validate(entity:, **options)
902
+ end
903
+
904
+ include_deferred 'with parameters for verifying adapters'
905
+
906
+ describe 'with entity: nil' do
907
+ let(:entity) { nil }
908
+
909
+ include_deferred 'should validate the entity parameter'
910
+ end
911
+
912
+ describe 'with entity: an Object' do
913
+ let(:entity) { Object.new.freeze }
914
+
915
+ include_deferred 'should validate the entity parameter'
916
+ end
917
+ end
918
+
919
+ describe '#validate_entity_parameter' do
920
+ let(:configured_failures) do
921
+ next expected_failures if defined?(expected_failures)
922
+
923
+ message =
924
+ SleepingKingStudios::Tools::Toolbelt
925
+ .instance
926
+ .assertions
927
+ .error_message_for(
928
+ 'sleeping_king_studios.tools.assertions.instance_of',
929
+ as: 'entity',
930
+ expected: adapter.entity_class
931
+ )
932
+
933
+ [message]
934
+ end
935
+
936
+ include_deferred 'with parameters for verifying adapters'
937
+
938
+ describe 'with entity: nil' do
939
+ let(:entity) { nil }
940
+
941
+ it 'should return the failure message' do
942
+ expect(adapter.validate_entity_parameter(entity))
943
+ .to be == configured_failures.join(', ')
944
+ end
945
+ end
946
+
947
+ describe 'with entity: an Object' do
948
+ let(:entity) { Object.new.freeze }
949
+
950
+ it 'should return the failure message' do
951
+ expect(adapter.validate_entity_parameter(entity))
952
+ .to be == configured_failures.join(', ')
953
+ end
954
+ end
955
+
956
+ describe 'with entity: a valid entity' do
957
+ let(:entity) { configured_valid_entity }
958
+
959
+ it { expect(adapter.validate_entity_parameter(entity)).to be nil }
960
+ end
961
+ end
962
+ end
963
+
964
+ deferred_examples 'should validate the entity using the default contract' \
965
+ do |**deferred_options|
966
+ describe '#validate' do
967
+ let(:entity) { configured_valid_entity }
968
+ let(:options) { {} }
969
+ let(:expected_error) do
970
+ Cuprum::Collections::Errors::MissingDefaultContract
971
+ .new(entity_class: adapter.entity_class)
972
+ end
973
+
974
+ define_method :call_adapter_method do
975
+ subject.validate(entity:, **options)
976
+ end
977
+
978
+ include_deferred 'with parameters for verifying adapters'
979
+
980
+ if deferred_options.fetch(:require_default_contract, true)
981
+ it 'should return a failing result' do
982
+ expect(call_adapter_method)
983
+ .to be_a_failing_result
984
+ .with_error(expected_error)
985
+ end
986
+ end
987
+
988
+ describe 'with contract: value' do
989
+ let(:options) { super().merge(contract: configured_contract) }
990
+
991
+ describe 'when the entity does not match the contract' do
992
+ let(:entity) { configured_invalid_entity }
993
+ let(:expected_error) do
994
+ errors = configured_contract.errors_for(entity)
995
+
996
+ Cuprum::Collections::Errors::FailedValidation.new(
997
+ entity_class: adapter.entity_class,
998
+ errors:
999
+ )
1000
+ end
1001
+
1002
+ it 'should return a failing result' do
1003
+ expect(call_adapter_method)
1004
+ .to be_a_failing_result
1005
+ .with_error(expected_error)
1006
+ end
1007
+ end
1008
+
1009
+ describe 'when the entity matches the contract' do
1010
+ let(:entity) { configured_valid_entity }
1011
+
1012
+ it 'should return a passing result' do
1013
+ expect(call_adapter_method)
1014
+ .to be_a_passing_result
1015
+ .with_value(entity)
1016
+ end
1017
+ end
1018
+ end
1019
+
1020
+ context 'when initialized with a default contract' do
1021
+ let(:constructor_options) do
1022
+ super().merge(default_contract: configured_contract)
1023
+ end
1024
+
1025
+ describe 'when the entity does not match the contract' do
1026
+ let(:entity) { configured_invalid_entity }
1027
+ let(:expected_error) do
1028
+ errors = configured_contract.errors_for(entity)
1029
+
1030
+ Cuprum::Collections::Errors::FailedValidation.new(
1031
+ entity_class: adapter.entity_class,
1032
+ errors:
1033
+ )
1034
+ end
1035
+
1036
+ it 'should return a failing result' do
1037
+ expect(call_adapter_method)
1038
+ .to be_a_failing_result
1039
+ .with_error(expected_error)
1040
+ end
1041
+ end
1042
+
1043
+ describe 'when the entity matches the contract' do
1044
+ let(:entity) { configured_valid_entity }
1045
+
1046
+ it 'should return a passing result' do
1047
+ expect(call_adapter_method)
1048
+ .to be_a_passing_result
1049
+ .with_value(entity)
1050
+ end
1051
+ end
1052
+
1053
+ describe 'with contract: value' do
1054
+ let(:options) do
1055
+ super().merge(contract: Stannum::Constraints::Nothing.new)
1056
+ end
1057
+ let(:entity) { configured_valid_entity }
1058
+ let(:expected_error) do
1059
+ errors = options[:contract].errors_for(entity)
1060
+
1061
+ Cuprum::Collections::Errors::FailedValidation.new(
1062
+ entity_class: adapter.entity_class,
1063
+ errors:
1064
+ )
1065
+ end
1066
+
1067
+ it 'should return a failing result' do
1068
+ expect(call_adapter_method)
1069
+ .to be_a_failing_result
1070
+ .with_error(expected_error)
1071
+ end
1072
+ end
1073
+ end
1074
+ end
1075
+ end
1076
+ end
1077
+ end