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.
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 +1 -1
  37. data/lib/cuprum/collections.rb +9 -4
  38. metadata +23 -19
  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,1381 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/rspec/contracts'
4
+
5
+ module Cuprum::Collections::RSpec::Contracts
6
+ # Contracts for asserting on Relation objects.
7
+ module RelationContracts
8
+ # Contract asserting that the Relation resolves the given parameter.
9
+ module ShouldDisambiguateParameter
10
+ extend RSpec::SleepingKingStudios::Contract
11
+
12
+ # @!method apply(example_group, key, as:, value:)
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
+ # @param key [Symbol] the original parameter key.
18
+ # @param as [Symbol, Array<Symbol>] the aliased key or keys.
19
+ # @param value [Object] the custom value for the property.
20
+ contract do |key, as:, value: 'custom value'|
21
+ describe '.new' do
22
+ Array(as).each do |alt|
23
+ describe "with #{key}: value and #{alt}: value" do
24
+ let(:error_message) do
25
+ "ambiguous parameter #{key}: initialized with parameters " \
26
+ "#{key}: #{value.inspect}, #{alt}: #{value.inspect}"
27
+ end
28
+
29
+ it 'should raise an exception' do
30
+ expect do
31
+ described_class.new(
32
+ name: 'books',
33
+ key => value,
34
+ alt => value
35
+ )
36
+ end
37
+ .to raise_error ArgumentError, error_message
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ Array(as).each do |alt|
44
+ describe "##{alt}" do
45
+ let(:core_tools) do
46
+ SleepingKingStudios::Tools::Toolbelt.instance.core_tools
47
+ end
48
+
49
+ before(:example) { allow(core_tools).to receive(:deprecate) }
50
+
51
+ include_examples 'should define reader',
52
+ alt,
53
+ -> { subject.send(key) }
54
+
55
+ it 'should print a deprecation warning' do
56
+ subject.send(alt)
57
+
58
+ expect(core_tools)
59
+ .to have_received(:deprecate)
60
+ .with(
61
+ "##{alt} method",
62
+ message: "Use ##{key} instead"
63
+ )
64
+ end
65
+
66
+ context "when initialized with #{key}: value" do
67
+ let(:constructor_options) do
68
+ super().merge(key => value)
69
+ end
70
+
71
+ it { expect(subject.send(alt)).to be == value }
72
+ end
73
+
74
+ Array(as).each do |given|
75
+ context "when initialized with #{given}: value" do
76
+ let(:constructor_options) do
77
+ super()
78
+ .tap { |hsh| hsh.delete(key) }
79
+ .merge(given => value)
80
+ end
81
+
82
+ it { expect(subject.send(alt)).to be == value }
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "##{key}" do
89
+ Array(as).each do |alt|
90
+ context "when initialized with #{alt}: value" do
91
+ let(:constructor_options) do
92
+ super()
93
+ .tap { |hsh| hsh.delete(key) }
94
+ .merge(alt => value)
95
+ end
96
+ let(:core_tools) do
97
+ SleepingKingStudios::Tools::Toolbelt.instance.core_tools
98
+ end
99
+
100
+ before(:example) { allow(core_tools).to receive(:deprecate) }
101
+
102
+ it { expect(subject.send(key)).to be == value }
103
+ end
104
+ end
105
+ end
106
+
107
+ describe '#options' do
108
+ Array(as).each do |alt|
109
+ context "when initialized with #{alt}: value" do
110
+ let(:constructor_options) do
111
+ super()
112
+ .tap { |hsh| hsh.delete(key) }
113
+ .merge(alt => value)
114
+ end
115
+ let(:core_tools) do
116
+ SleepingKingStudios::Tools::Toolbelt.instance.core_tools
117
+ end
118
+
119
+ before(:example) { allow(core_tools).to receive(:deprecate) }
120
+
121
+ it { expect(subject.options).not_to have_key alt }
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ # Contract asserting that the method validates the required parameters.
129
+ module ShouldValidateTheParametersContract
130
+ extend RSpec::SleepingKingStudios::Contract
131
+
132
+ # @!method apply(example_group)
133
+ # Adds the contract to the example group.
134
+ #
135
+ # @param example_group [RSpec::Core::ExampleGroup] the example group to
136
+ # which the contract is applied.
137
+ contract do
138
+ describe 'with no parameters' do
139
+ let(:error_message) { "name or entity class can't be blank" }
140
+
141
+ it 'should raise an exception' do
142
+ expect { call_method }
143
+ .to raise_error ArgumentError, error_message
144
+ end
145
+ end
146
+
147
+ describe 'with entity_class: nil' do
148
+ let(:error_message) { "name or entity class can't be blank" }
149
+
150
+ it 'should raise an exception' do
151
+ expect { call_method(entity_class: nil) }
152
+ .to raise_error ArgumentError, error_message
153
+ end
154
+ end
155
+
156
+ describe 'with entity_class: an Object' do
157
+ let(:error_message) do
158
+ 'entity class is not a Class, a String or a Symbol'
159
+ end
160
+
161
+ it 'should raise an exception' do
162
+ expect do
163
+ call_method(entity_class: Object.new.freeze)
164
+ end
165
+ .to raise_error ArgumentError, error_message
166
+ end
167
+ end
168
+
169
+ describe 'with entity_class: an empty String' do
170
+ let(:error_message) { "entity class can't be blank" }
171
+
172
+ it 'should raise an exception' do
173
+ expect { call_method(entity_class: '') }
174
+ .to raise_error ArgumentError, error_message
175
+ end
176
+ end
177
+
178
+ describe 'with entity_class: an empty Symbol' do
179
+ let(:error_message) { "entity class can't be blank" }
180
+
181
+ it 'should raise an exception' do
182
+ expect { call_method(entity_class: :'') }
183
+ .to raise_error ArgumentError, error_message
184
+ end
185
+ end
186
+
187
+ describe 'with name: nil' do
188
+ let(:error_message) { "name or entity class can't be blank" }
189
+
190
+ it 'should raise an exception' do
191
+ expect { call_method(name: nil) }
192
+ .to raise_error ArgumentError, error_message
193
+ end
194
+ end
195
+
196
+ describe 'with name: nil and entity_class: value' do
197
+ it 'should not raise an exception' do
198
+ expect do
199
+ call_method(entity_class: 'Book', name: nil)
200
+ end
201
+ .not_to raise_error
202
+ end
203
+ end
204
+
205
+ describe 'with name: an Object' do
206
+ let(:error_message) { 'name is not a String or a Symbol' }
207
+
208
+ it 'should raise an exception' do
209
+ expect { call_method(name: Object.new.freeze) }
210
+ .to raise_error ArgumentError, error_message
211
+ end
212
+ end
213
+
214
+ describe 'with name: an empty String' do
215
+ let(:error_message) { "name can't be blank" }
216
+
217
+ it 'should raise an exception' do
218
+ expect { call_method(name: '') }
219
+ .to raise_error ArgumentError, error_message
220
+ end
221
+ end
222
+
223
+ describe 'with name: an empty Symbol' do
224
+ let(:error_message) { "name can't be blank" }
225
+
226
+ it 'should raise an exception' do
227
+ expect { call_method(name: '') }
228
+ .to raise_error ArgumentError, error_message
229
+ end
230
+ end
231
+
232
+ describe 'with name: value and entity_class: nil' do
233
+ it 'should not raise an exception' do
234
+ expect do
235
+ call_method(entity_class: 'Book', name: nil)
236
+ end
237
+ .not_to raise_error
238
+ end
239
+ end
240
+
241
+ describe 'with name: value and entity_class: an Object' do
242
+ let(:error_message) do
243
+ 'entity class is not a Class, a String or a Symbol'
244
+ end
245
+
246
+ it 'should raise an exception' do
247
+ expect do
248
+ call_method(
249
+ entity_class: Object.new.freeze,
250
+ name: 'books'
251
+ )
252
+ end
253
+ .to raise_error ArgumentError, error_message
254
+ end
255
+ end
256
+
257
+ describe 'with name: value and entity_class: an empty String' do
258
+ let(:error_message) { "entity class can't be blank" }
259
+
260
+ it 'should raise an exception' do
261
+ expect { call_method(entity_class: '', name: 'books') }
262
+ .to raise_error ArgumentError, error_message
263
+ end
264
+ end
265
+
266
+ describe 'with name: value and entity_class: an empty Symbol' do
267
+ let(:error_message) { "entity class can't be blank" }
268
+
269
+ it 'should raise an exception' do
270
+ expect { call_method(entity_class: :'', name: 'books') }
271
+ .to raise_error ArgumentError, error_message
272
+ end
273
+ end
274
+
275
+ describe 'with plural_name: an Object' do
276
+ let(:error_message) { 'plural name is not a String or a Symbol' }
277
+
278
+ it 'should raise an exception' do
279
+ expect do
280
+ call_method(
281
+ name: 'books',
282
+ plural_name: Object.new.freeze
283
+ )
284
+ end
285
+ .to raise_error ArgumentError, error_message
286
+ end
287
+ end
288
+
289
+ describe 'with plural_name: an empty String' do
290
+ let(:error_message) { "plural name can't be blank" }
291
+
292
+ it 'should raise an exception' do
293
+ expect do
294
+ call_method(
295
+ name: 'books',
296
+ plural_name: ''
297
+ )
298
+ end
299
+ .to raise_error ArgumentError, error_message
300
+ end
301
+ end
302
+
303
+ describe 'with plural_name: an empty Symbol' do
304
+ let(:error_message) { "plural name can't be blank" }
305
+
306
+ it 'should raise an exception' do
307
+ expect do
308
+ call_method(
309
+ name: 'books',
310
+ plural_name: :''
311
+ )
312
+ end
313
+ .to raise_error ArgumentError, error_message
314
+ end
315
+ end
316
+
317
+ describe 'with qualified_name: an Object' do
318
+ let(:error_message) { 'qualified name is not a String or a Symbol' }
319
+
320
+ it 'should raise an exception' do
321
+ expect do
322
+ call_method(
323
+ name: 'books',
324
+ qualified_name: Object.new.freeze
325
+ )
326
+ end
327
+ .to raise_error ArgumentError, error_message
328
+ end
329
+ end
330
+
331
+ describe 'with qualified_name: an empty String' do
332
+ let(:error_message) { "qualified name can't be blank" }
333
+
334
+ it 'should raise an exception' do
335
+ expect do
336
+ call_method(
337
+ name: 'books',
338
+ qualified_name: ''
339
+ )
340
+ end
341
+ .to raise_error ArgumentError, error_message
342
+ end
343
+ end
344
+
345
+ describe 'with qualified_name: an empty Symbol' do
346
+ let(:error_message) { "qualified name can't be blank" }
347
+
348
+ it 'should raise an exception' do
349
+ expect do
350
+ call_method(
351
+ name: 'books',
352
+ qualified_name: :''
353
+ )
354
+ end
355
+ .to raise_error ArgumentError, error_message
356
+ end
357
+ end
358
+
359
+ describe 'with qualified_name: value and name, entity_class: nil' do
360
+ it 'should not raise an exception' do
361
+ expect do
362
+ call_method(entity_class: nil, name: nil, qualified_name: 'books')
363
+ end
364
+ .not_to raise_error
365
+ end
366
+ end
367
+
368
+ describe 'with singular_name: an Object' do
369
+ let(:error_message) { 'singular name is not a String or a Symbol' }
370
+
371
+ it 'should raise an exception' do
372
+ expect do
373
+ call_method(
374
+ name: 'books',
375
+ singular_name: Object.new.freeze
376
+ )
377
+ end
378
+ .to raise_error ArgumentError, error_message
379
+ end
380
+ end
381
+
382
+ describe 'with singular_name: an empty String' do
383
+ let(:error_message) { "singular name can't be blank" }
384
+
385
+ it 'should raise an exception' do
386
+ expect do
387
+ call_method(
388
+ name: 'books',
389
+ singular_name: ''
390
+ )
391
+ end
392
+ .to raise_error ArgumentError, error_message
393
+ end
394
+ end
395
+
396
+ describe 'with singular_name: an empty Symbol' do
397
+ let(:error_message) { "singular name can't be blank" }
398
+
399
+ it 'should raise an exception' do
400
+ expect do
401
+ call_method(
402
+ name: 'books',
403
+ singular_name: :''
404
+ )
405
+ end
406
+ .to raise_error ArgumentError, error_message
407
+ end
408
+ end
409
+ end
410
+ end
411
+
412
+ # Contract validating the behavior of a Relation.
413
+ module ShouldBeARelationContract
414
+ extend RSpec::SleepingKingStudios::Contract
415
+
416
+ # @!method apply(example_group, **options)
417
+ # Adds the contract to the example group.
418
+ #
419
+ # @param example_group [RSpec::Core::ExampleGroup] the example group to
420
+ # which the contract is applied.
421
+ # @param options [Hash] additional options for the contract.
422
+ #
423
+ # @option options cardinality [Boolean] true if the relation accepts
424
+ # cardinality keywords (:plural, :singular); otherwise false.
425
+ # @option options constructor [Boolean] if false, does not generate
426
+ # constructor specs. Defaults to true.
427
+ # @option options default_entity_class [Class] the default entity class
428
+ # for the relation, if any.
429
+ # @option options expected_keywords [Array<Symbol>] additional keywords
430
+ # for the constructor.
431
+ contract do |**options|
432
+ include Cuprum::Collections::RSpec::Contracts::RelationContracts
433
+
434
+ if options.fetch(:constructor, true)
435
+ describe '.new' do
436
+ let(:expected_keywords) do
437
+ keywords = %i[
438
+ entity_class
439
+ name
440
+ plural_name
441
+ qualified_name
442
+ singular_name
443
+ ]
444
+
445
+ keywords += %i[plural singular] if options[:cardinality]
446
+
447
+ keywords + options.fetch(:expected_keywords, [])
448
+ end
449
+
450
+ def call_method(**parameters)
451
+ described_class.new(**parameters)
452
+ end
453
+
454
+ it 'should define the constructor' do
455
+ expect(described_class)
456
+ .to be_constructible
457
+ .with(0).arguments
458
+ .and_keywords(*expected_keywords)
459
+ .and_any_keywords
460
+ end
461
+
462
+ include_contract 'should validate the parameters'
463
+ end
464
+ end
465
+
466
+ describe '#entity_class' do
467
+ include_examples 'should define reader', :entity_class
468
+
469
+ context 'when initialized with entity_class: a Class' do
470
+ let(:entity_class) { Grimoire }
471
+ let(:constructor_options) do
472
+ super()
473
+ .tap { |hsh| hsh.delete(:name) }
474
+ .merge(entity_class: entity_class)
475
+ end
476
+
477
+ it { expect(subject.entity_class).to be Grimoire }
478
+ end
479
+
480
+ context 'when initialized with entity_class: a scoped Class' do
481
+ let(:entity_class) { Spec::ScopedBook }
482
+ let(:constructor_options) do
483
+ super()
484
+ .tap { |hsh| hsh.delete(:name) }
485
+ .merge(entity_class: entity_class)
486
+ end
487
+
488
+ it { expect(subject.entity_class).to be Spec::ScopedBook }
489
+ end
490
+
491
+ context 'when initialized with entity_class: a String' do
492
+ let(:entity_class) { 'Grimoire' }
493
+ let(:constructor_options) do
494
+ super()
495
+ .tap { |hsh| hsh.delete(:name) }
496
+ .merge(entity_class: entity_class)
497
+ end
498
+
499
+ it { expect(subject.entity_class).to be Grimoire }
500
+ end
501
+
502
+ context 'when initialized with entity_class: a scoped String' do
503
+ let(:entity_class) { 'Spec::ScopedBook' }
504
+ let(:constructor_options) do
505
+ super()
506
+ .tap { |hsh| hsh.delete(:name) }
507
+ .merge(entity_class: entity_class)
508
+ end
509
+
510
+ it { expect(subject.entity_class).to be Spec::ScopedBook }
511
+ end
512
+
513
+ context 'when initialized with name: a String' do
514
+ let(:name) { 'books' }
515
+ let(:constructor_options) do
516
+ super().merge(name: name)
517
+ end
518
+ let(:expected) { options[:default_entity_class] || Book }
519
+
520
+ it { expect(subject.entity_class).to be expected }
521
+
522
+ context 'when initialized with entity_class: value' do
523
+ let(:entity_class) { Grimoire }
524
+ let(:constructor_options) do
525
+ super()
526
+ .tap { |hsh| hsh.delete(:name) }
527
+ .merge(entity_class: entity_class)
528
+ end
529
+
530
+ it { expect(subject.entity_class).to be Grimoire }
531
+ end
532
+
533
+ context 'when initialized with qualified_name: value' do
534
+ let(:qualified_name) { 'spec/scoped_books' }
535
+ let(:constructor_options) do
536
+ super().merge(qualified_name: qualified_name)
537
+ end
538
+ let(:expected) do
539
+ options[:default_entity_class] || Spec::ScopedBook
540
+ end
541
+
542
+ it { expect(subject.entity_class).to be expected }
543
+
544
+ context 'when initialized with entity_class: value' do
545
+ let(:entity_class) { Grimoire }
546
+ let(:constructor_options) do
547
+ super()
548
+ .tap { |hsh| hsh.delete(:name) }
549
+ .merge(entity_class: entity_class)
550
+ end
551
+
552
+ it { expect(subject.entity_class).to be Grimoire }
553
+ end
554
+ end
555
+ end
556
+
557
+ context 'when initialized with name: a Symbol' do
558
+ let(:name) { 'books' }
559
+ let(:constructor_options) do
560
+ super().merge(name: name)
561
+ end
562
+ let(:expected) { options[:default_entity_class] || Book }
563
+
564
+ it { expect(subject.entity_class).to be expected }
565
+
566
+ context 'when initialized with entity_class: value' do
567
+ let(:entity_class) { Grimoire }
568
+ let(:constructor_options) do
569
+ super()
570
+ .tap { |hsh| hsh.delete(:name) }
571
+ .merge(entity_class: entity_class)
572
+ end
573
+
574
+ it { expect(subject.entity_class).to be Grimoire }
575
+ end
576
+
577
+ context 'when initialized with qualified_name: value' do
578
+ let(:qualified_name) { 'spec/scoped_books' }
579
+ let(:constructor_options) do
580
+ super().merge(qualified_name: qualified_name)
581
+ end
582
+ let(:expected) do
583
+ options[:default_entity_class] || Spec::ScopedBook
584
+ end
585
+
586
+ it { expect(subject.entity_class).to be expected }
587
+
588
+ context 'when initialized with entity_class: value' do
589
+ let(:entity_class) { Grimoire }
590
+ let(:constructor_options) do
591
+ super()
592
+ .tap { |hsh| hsh.delete(:name) }
593
+ .merge(entity_class: entity_class)
594
+ end
595
+
596
+ it { expect(subject.entity_class).to be Grimoire }
597
+ end
598
+ end
599
+ end
600
+
601
+ context 'when initialized with qualified_name: a String' do
602
+ let(:qualified_name) { 'spec/scoped_books' }
603
+ let(:constructor_options) do
604
+ super().merge(qualified_name: qualified_name)
605
+ end
606
+ let(:expected) do
607
+ options[:default_entity_class] || Spec::ScopedBook
608
+ end
609
+
610
+ it { expect(subject.entity_class).to be expected }
611
+
612
+ context 'when initialized with entity_class: value' do
613
+ let(:entity_class) { Grimoire }
614
+ let(:constructor_options) do
615
+ super()
616
+ .tap { |hsh| hsh.delete(:name) }
617
+ .merge(entity_class: entity_class)
618
+ end
619
+
620
+ it { expect(subject.entity_class).to be Grimoire }
621
+ end
622
+ end
623
+
624
+ context 'when initialized with qualified_name: a Symbol' do
625
+ let(:qualified_name) { :'spec/scoped_books' }
626
+ let(:constructor_options) do
627
+ super().merge(qualified_name: qualified_name)
628
+ end
629
+ let(:expected) do
630
+ options[:default_entity_class] || Spec::ScopedBook
631
+ end
632
+
633
+ it { expect(subject.entity_class).to be expected }
634
+
635
+ context 'when initialized with entity_class: value' do
636
+ let(:entity_class) { Grimoire }
637
+ let(:constructor_options) do
638
+ super()
639
+ .tap { |hsh| hsh.delete(:name) }
640
+ .merge(entity_class: entity_class)
641
+ end
642
+
643
+ it { expect(subject.entity_class).to be Grimoire }
644
+ end
645
+ end
646
+ end
647
+
648
+ describe '#plural_name' do
649
+ include_examples 'should define reader', :plural_name
650
+
651
+ context 'when initialized with entity_class: a Class' do
652
+ let(:entity_class) { Grimoire }
653
+ let(:constructor_options) do
654
+ super()
655
+ .tap { |hsh| hsh.delete(:name) }
656
+ .merge(entity_class: entity_class)
657
+ end
658
+
659
+ it { expect(subject.plural_name).to be == 'grimoires' }
660
+
661
+ context 'when initialized with plural_name: value' do
662
+ let(:plural_name) { 'books' }
663
+ let(:constructor_options) do
664
+ super().merge(plural_name: plural_name)
665
+ end
666
+
667
+ it { expect(subject.plural_name).to be == plural_name }
668
+ end
669
+ end
670
+
671
+ context 'when initialized with entity_class: a scoped Class' do
672
+ let(:entity_class) { Spec::ScopedBook }
673
+ let(:constructor_options) do
674
+ super()
675
+ .tap { |hsh| hsh.delete(:name) }
676
+ .merge(entity_class: entity_class)
677
+ end
678
+
679
+ it { expect(subject.plural_name).to be == 'scoped_books' }
680
+
681
+ context 'when initialized with plural_name: value' do
682
+ let(:plural_name) { 'books' }
683
+ let(:constructor_options) do
684
+ super().merge(plural_name: plural_name)
685
+ end
686
+
687
+ it { expect(subject.plural_name).to be == plural_name }
688
+ end
689
+ end
690
+
691
+ context 'when initialized with entity_class: a String' do
692
+ let(:entity_class) { 'Grimoire' }
693
+ let(:constructor_options) do
694
+ super()
695
+ .tap { |hsh| hsh.delete(:name) }
696
+ .merge(entity_class: entity_class)
697
+ end
698
+
699
+ it { expect(subject.plural_name).to be == 'grimoires' }
700
+
701
+ context 'when initialized with plural_name: value' do
702
+ let(:plural_name) { 'books' }
703
+ let(:constructor_options) do
704
+ super().merge(plural_name: plural_name)
705
+ end
706
+
707
+ it { expect(subject.plural_name).to be == plural_name }
708
+ end
709
+ end
710
+
711
+ context 'when initialized with entity_class: a scoped String' do
712
+ let(:entity_class) { 'Spec::ScopedBook' }
713
+ let(:constructor_options) do
714
+ super()
715
+ .tap { |hsh| hsh.delete(:name) }
716
+ .merge(entity_class: entity_class)
717
+ end
718
+
719
+ it { expect(subject.plural_name).to be == 'scoped_books' }
720
+
721
+ context 'when initialized with plural_name: value' do
722
+ let(:plural_name) { 'books' }
723
+ let(:constructor_options) do
724
+ super().merge(plural_name: plural_name)
725
+ end
726
+
727
+ it { expect(subject.plural_name).to be == plural_name }
728
+ end
729
+ end
730
+
731
+ context 'when initialized with name: a String' do
732
+ let(:name) { 'grimoires' }
733
+ let(:constructor_options) do
734
+ super().merge(name: name)
735
+ end
736
+
737
+ it { expect(subject.plural_name).to be == 'grimoires' }
738
+
739
+ context 'when initialized with plural_name: value' do
740
+ let(:plural_name) { 'books' }
741
+ let(:constructor_options) do
742
+ super().merge(plural_name: plural_name)
743
+ end
744
+
745
+ it { expect(subject.plural_name).to be == plural_name }
746
+ end
747
+ end
748
+
749
+ context 'when initialized with name: a Symbol' do
750
+ let(:name) { :grimoires }
751
+ let(:constructor_options) do
752
+ super().merge(name: name)
753
+ end
754
+
755
+ it { expect(subject.plural_name).to be == 'grimoires' }
756
+
757
+ context 'when initialized with plural_name: value' do
758
+ let(:plural_name) { 'books' }
759
+ let(:constructor_options) do
760
+ super().merge(plural_name: plural_name)
761
+ end
762
+
763
+ it { expect(subject.plural_name).to be == plural_name }
764
+ end
765
+ end
766
+ end
767
+
768
+ describe '#singular_name' do
769
+ include_examples 'should define reader', :singular_name
770
+
771
+ context 'when initialized with entity_class: a Class' do
772
+ let(:entity_class) { Grimoire }
773
+ let(:constructor_options) do
774
+ super()
775
+ .tap { |hsh| hsh.delete(:name) }
776
+ .merge(entity_class: entity_class)
777
+ end
778
+
779
+ it { expect(subject.singular_name).to be == 'grimoire' }
780
+
781
+ context 'when initialized with singular_name: value' do
782
+ let(:singular_name) { 'book' }
783
+ let(:constructor_options) do
784
+ super().merge(singular_name: singular_name)
785
+ end
786
+
787
+ it { expect(subject.singular_name).to be == singular_name }
788
+ end
789
+ end
790
+
791
+ context 'when initialized with entity_class: a scoped Class' do
792
+ let(:entity_class) { Spec::ScopedBook }
793
+ let(:constructor_options) do
794
+ super()
795
+ .tap { |hsh| hsh.delete(:name) }
796
+ .merge(entity_class: entity_class)
797
+ end
798
+
799
+ it { expect(subject.singular_name).to be == 'scoped_book' }
800
+
801
+ context 'when initialized with singular_name: value' do
802
+ let(:singular_name) { 'book' }
803
+ let(:constructor_options) do
804
+ super().merge(singular_name: singular_name)
805
+ end
806
+
807
+ it { expect(subject.singular_name).to be == singular_name }
808
+ end
809
+ end
810
+
811
+ context 'when initialized with entity_class: a String' do
812
+ let(:entity_class) { 'Grimoire' }
813
+ let(:constructor_options) do
814
+ super()
815
+ .tap { |hsh| hsh.delete(:name) }
816
+ .merge(entity_class: entity_class)
817
+ end
818
+
819
+ it { expect(subject.singular_name).to be == 'grimoire' }
820
+
821
+ context 'when initialized with singular_name: value' do
822
+ let(:singular_name) { 'book' }
823
+ let(:constructor_options) do
824
+ super().merge(singular_name: singular_name)
825
+ end
826
+
827
+ it { expect(subject.singular_name).to be == singular_name }
828
+ end
829
+ end
830
+
831
+ context 'when initialized with entity_class: a scoped String' do
832
+ let(:entity_class) { 'Spec::ScopedBook' }
833
+ let(:constructor_options) do
834
+ super()
835
+ .tap { |hsh| hsh.delete(:name) }
836
+ .merge(entity_class: entity_class)
837
+ end
838
+
839
+ it { expect(subject.singular_name).to be == 'scoped_book' }
840
+
841
+ context 'when initialized with singular_name: value' do
842
+ let(:singular_name) { 'book' }
843
+ let(:constructor_options) do
844
+ super().merge(singular_name: singular_name)
845
+ end
846
+
847
+ it { expect(subject.singular_name).to be == singular_name }
848
+ end
849
+ end
850
+
851
+ context 'when initialized with name: a String' do
852
+ let(:name) { 'grimoires' }
853
+ let(:constructor_options) do
854
+ super().merge(name: name)
855
+ end
856
+
857
+ it { expect(subject.singular_name).to be == 'grimoire' }
858
+
859
+ context 'when initialized with singular_name: value' do
860
+ let(:singular_name) { 'book' }
861
+ let(:constructor_options) do
862
+ super().merge(singular_name: singular_name)
863
+ end
864
+
865
+ it { expect(subject.singular_name).to be == singular_name }
866
+ end
867
+ end
868
+
869
+ context 'when initialized with name: a Symbol' do
870
+ let(:name) { :grimoires }
871
+ let(:constructor_options) do
872
+ super().merge(name: name)
873
+ end
874
+
875
+ it { expect(subject.singular_name).to be == 'grimoire' }
876
+
877
+ context 'when initialized with singular_name: value' do
878
+ let(:singular_name) { 'book' }
879
+ let(:constructor_options) do
880
+ super().merge(singular_name: singular_name)
881
+ end
882
+
883
+ it { expect(subject.singular_name).to be == singular_name }
884
+ end
885
+ end
886
+ end
887
+
888
+ describe '#name' do
889
+ include_examples 'should define reader', :name
890
+
891
+ context 'when initialized with entity_class: a Class' do
892
+ let(:entity_class) { Grimoire }
893
+ let(:constructor_options) do
894
+ super()
895
+ .tap { |hsh| hsh.delete(:name) }
896
+ .merge(entity_class: entity_class)
897
+ end
898
+
899
+ it { expect(subject.name).to be == 'grimoires' }
900
+
901
+ context 'when initialized with name: value' do
902
+ let(:name) { 'books' }
903
+ let(:constructor_options) do
904
+ super().merge(name: name)
905
+ end
906
+
907
+ it { expect(subject.name).to be == name }
908
+ end
909
+ end
910
+
911
+ context 'when initialized with entity_class: a scoped Class' do
912
+ let(:entity_class) { Spec::ScopedBook }
913
+ let(:constructor_options) do
914
+ super()
915
+ .tap { |hsh| hsh.delete(:name) }
916
+ .merge(entity_class: entity_class)
917
+ end
918
+
919
+ it { expect(subject.name).to be == 'scoped_books' }
920
+
921
+ context 'when initialized with name: value' do
922
+ let(:name) { 'books' }
923
+ let(:constructor_options) do
924
+ super().merge(name: name)
925
+ end
926
+
927
+ it { expect(subject.name).to be == name }
928
+ end
929
+ end
930
+
931
+ context 'when initialized with entity_class: a String' do
932
+ let(:entity_class) { 'Grimoire' }
933
+ let(:constructor_options) do
934
+ super()
935
+ .tap { |hsh| hsh.delete(:name) }
936
+ .merge(entity_class: entity_class)
937
+ end
938
+
939
+ it { expect(subject.name).to be == 'grimoires' }
940
+
941
+ context 'when initialized with name: value' do
942
+ let(:name) { 'books' }
943
+ let(:constructor_options) do
944
+ super().merge(name: name)
945
+ end
946
+
947
+ it { expect(subject.name).to be == name }
948
+ end
949
+ end
950
+
951
+ context 'when initialized with entity_class: a scoped String' do
952
+ let(:entity_class) { 'Spec::ScopedBook' }
953
+ let(:constructor_options) do
954
+ super()
955
+ .tap { |hsh| hsh.delete(:name) }
956
+ .merge(entity_class: entity_class)
957
+ end
958
+
959
+ it { expect(subject.name).to be == 'scoped_books' }
960
+
961
+ context 'when initialized with name: value' do
962
+ let(:name) { 'books' }
963
+ let(:constructor_options) do
964
+ super().merge(name: name)
965
+ end
966
+
967
+ it { expect(subject.name).to be == name }
968
+ end
969
+ end
970
+
971
+ context 'when initialized with name: a String' do
972
+ let(:name) { 'books' }
973
+ let(:constructor_options) do
974
+ super().merge(name: name)
975
+ end
976
+
977
+ it { expect(subject.name).to be == name }
978
+ end
979
+
980
+ context 'when initialized with name: a Symbol' do
981
+ let(:name) { :books }
982
+ let(:constructor_options) do
983
+ super().merge(name: name)
984
+ end
985
+
986
+ it { expect(subject.name).to be == name.to_s }
987
+ end
988
+ end
989
+
990
+ describe '#options' do
991
+ include_examples 'should define reader', :options, -> { {} }
992
+
993
+ context 'when initialized with entity_class: value' do
994
+ let(:entity_class) { Grimoire }
995
+ let(:constructor_options) do
996
+ super()
997
+ .tap { |hsh| hsh.delete(:name) }
998
+ .merge(entity_class: entity_class)
999
+ end
1000
+
1001
+ it { expect(subject.options).to be == {} }
1002
+ end
1003
+
1004
+ context 'when initialized with singular_name: value' do
1005
+ let(:singular_name) { 'book' }
1006
+ let(:constructor_options) do
1007
+ super().merge(singular_name: singular_name)
1008
+ end
1009
+
1010
+ it { expect(subject.options).to be == {} }
1011
+ end
1012
+
1013
+ context 'when initialized with name: value' do
1014
+ let(:name) { 'books' }
1015
+ let(:constructor_options) do
1016
+ super().merge(name: name)
1017
+ end
1018
+
1019
+ it { expect(subject.options).to be == {} }
1020
+ end
1021
+
1022
+ context 'when initialized with qualified_name: value' do
1023
+ let(:qualified_name) { 'spec/scoped_books' }
1024
+ let(:constructor_options) do
1025
+ super().merge(qualified_name: qualified_name)
1026
+ end
1027
+
1028
+ it { expect(subject.options).to be == {} }
1029
+ end
1030
+
1031
+ context 'when initialized with options' do
1032
+ let(:options) { { custom_option: 'custom value' } }
1033
+ let(:constructor_options) do
1034
+ super().merge(options)
1035
+ end
1036
+
1037
+ it { expect(subject.options).to be == options }
1038
+ end
1039
+ end
1040
+
1041
+ describe '#qualified_name' do
1042
+ include_examples 'should define reader', :qualified_name
1043
+
1044
+ context 'when initialized with entity_class: a Class' do
1045
+ let(:entity_class) { Grimoire }
1046
+ let(:constructor_options) do
1047
+ super()
1048
+ .tap { |hsh| hsh.delete(:name) }
1049
+ .merge(entity_class: entity_class)
1050
+ end
1051
+
1052
+ it { expect(subject.qualified_name).to be == 'grimoires' }
1053
+
1054
+ context 'when initialized with qualified_name: value' do
1055
+ let(:qualified_name) { 'path/to/books' }
1056
+ let(:constructor_options) do
1057
+ super().merge(qualified_name: qualified_name)
1058
+ end
1059
+
1060
+ it { expect(subject.qualified_name).to be == qualified_name }
1061
+ end
1062
+ end
1063
+
1064
+ context 'when initialized with entity_class: a scoped Class' do
1065
+ let(:entity_class) { Spec::ScopedBook }
1066
+ let(:constructor_options) do
1067
+ super()
1068
+ .tap { |hsh| hsh.delete(:name) }
1069
+ .merge(entity_class: entity_class)
1070
+ end
1071
+
1072
+ it { expect(subject.qualified_name).to be == 'spec/scoped_books' }
1073
+
1074
+ context 'when initialized with qualified_name: value' do
1075
+ let(:qualified_name) { 'path/to/books' }
1076
+ let(:constructor_options) do
1077
+ super().merge(qualified_name: qualified_name)
1078
+ end
1079
+
1080
+ it { expect(subject.qualified_name).to be == qualified_name }
1081
+ end
1082
+ end
1083
+
1084
+ context 'when initialized with entity_class: a String' do
1085
+ let(:entity_class) { 'Grimoire' }
1086
+ let(:constructor_options) do
1087
+ super()
1088
+ .tap { |hsh| hsh.delete(:name) }
1089
+ .merge(entity_class: entity_class)
1090
+ end
1091
+
1092
+ it { expect(subject.qualified_name).to be == 'grimoires' }
1093
+
1094
+ context 'when initialized with qualified_name: value' do
1095
+ let(:qualified_name) { 'path/to/books' }
1096
+ let(:constructor_options) do
1097
+ super().merge(qualified_name: qualified_name)
1098
+ end
1099
+
1100
+ it { expect(subject.qualified_name).to be == qualified_name }
1101
+ end
1102
+ end
1103
+
1104
+ context 'when initialized with entity_class: a scoped String' do
1105
+ let(:entity_class) { 'Spec::ScopedBook' }
1106
+ let(:constructor_options) do
1107
+ super()
1108
+ .tap { |hsh| hsh.delete(:name) }
1109
+ .merge(entity_class: entity_class)
1110
+ end
1111
+
1112
+ it { expect(subject.qualified_name).to be == 'spec/scoped_books' }
1113
+
1114
+ context 'when initialized with qualified_name: value' do
1115
+ let(:qualified_name) { 'path/to/books' }
1116
+ let(:constructor_options) do
1117
+ super().merge(qualified_name: qualified_name)
1118
+ end
1119
+
1120
+ it { expect(subject.qualified_name).to be == qualified_name }
1121
+ end
1122
+ end
1123
+
1124
+ context 'when initialized with name: a String' do
1125
+ let(:name) { 'books' }
1126
+ let(:constructor_options) do
1127
+ super().merge(name: name)
1128
+ end
1129
+
1130
+ it { expect(subject.qualified_name).to be == name }
1131
+
1132
+ context 'when initialized with qualified_name: value' do
1133
+ let(:qualified_name) { 'path/to/books' }
1134
+ let(:constructor_options) do
1135
+ super().merge(qualified_name: qualified_name)
1136
+ end
1137
+
1138
+ it { expect(subject.qualified_name).to be == qualified_name }
1139
+ end
1140
+ end
1141
+
1142
+ context 'when initialized with name: a Symbol' do
1143
+ let(:name) { :books }
1144
+ let(:constructor_options) do
1145
+ super().merge(name: name)
1146
+ end
1147
+
1148
+ it { expect(subject.qualified_name).to be == name.to_s }
1149
+
1150
+ context 'when initialized with qualified_name: value' do
1151
+ let(:qualified_name) { 'path/to/books' }
1152
+ let(:constructor_options) do
1153
+ super().merge(qualified_name: qualified_name)
1154
+ end
1155
+
1156
+ it { expect(subject.qualified_name).to be == qualified_name }
1157
+ end
1158
+ end
1159
+
1160
+ context 'when initialized with qualified_name: a String' do
1161
+ let(:qualified_name) { 'path/to/books' }
1162
+ let(:constructor_options) do
1163
+ super().merge(qualified_name: qualified_name)
1164
+ end
1165
+
1166
+ it { expect(subject.qualified_name).to be == qualified_name }
1167
+ end
1168
+
1169
+ context 'when initialized with qualified_name: a Symbol' do
1170
+ let(:qualified_name) { :'path/to/books' }
1171
+ let(:constructor_options) do
1172
+ super().merge(qualified_name: qualified_name)
1173
+ end
1174
+
1175
+ it { expect(subject.qualified_name).to be == qualified_name.to_s }
1176
+ end
1177
+ end
1178
+ end
1179
+ end
1180
+
1181
+ # Contract validating a Relation's cardinality properties.
1182
+ module ShouldDefineCardinalityContract
1183
+ extend RSpec::SleepingKingStudios::Contract
1184
+
1185
+ # @!method apply(example_group)
1186
+ # Adds the contract to the example group.
1187
+ #
1188
+ # @param example_group [RSpec::Core::ExampleGroup] the example group to
1189
+ # which the contract is applied.
1190
+ contract do
1191
+ describe '.new' do
1192
+ describe 'with plural: an Object' do
1193
+ let(:constructor_options) do
1194
+ super().merge(plural: Object.new.freeze)
1195
+ end
1196
+ let(:error_message) do
1197
+ 'plural must be true or false'
1198
+ end
1199
+
1200
+ it 'should raise an exception' do
1201
+ expect { described_class.new(**constructor_options) }
1202
+ .to raise_error ArgumentError, error_message
1203
+ end
1204
+ end
1205
+
1206
+ describe 'with singular: an Object' do
1207
+ let(:constructor_options) do
1208
+ super().merge(singular: Object.new.freeze)
1209
+ end
1210
+ let(:error_message) do
1211
+ 'singular must be true or false'
1212
+ end
1213
+
1214
+ it 'should raise an exception' do
1215
+ expect { described_class.new(**constructor_options) }
1216
+ .to raise_error ArgumentError, error_message
1217
+ end
1218
+ end
1219
+
1220
+ describe 'with singular: nil and plural: value' do
1221
+ let(:constructor_options) do
1222
+ super().merge(plural: true, singular: nil)
1223
+ end
1224
+
1225
+ it 'should not raise an exception' do
1226
+ expect { described_class.new(**constructor_options) }
1227
+ .not_to raise_error
1228
+ end
1229
+ end
1230
+
1231
+ describe 'with singular: value and plural: nil' do
1232
+ let(:constructor_options) do
1233
+ super().merge(plural: nil, singular: true)
1234
+ end
1235
+
1236
+ it 'should not raise an exception' do
1237
+ expect { described_class.new(**constructor_options) }
1238
+ .not_to raise_error
1239
+ end
1240
+ end
1241
+
1242
+ describe 'with singular: value and plural: value' do
1243
+ let(:constructor_options) do
1244
+ super().merge(singular: true, plural: false)
1245
+ end
1246
+ let(:error_message) do
1247
+ 'ambiguous cardinality: initialized with parameters ' \
1248
+ 'plural: false and singular: true'
1249
+ end
1250
+
1251
+ it 'should raise an exception' do
1252
+ expect { described_class.new(**constructor_options) }
1253
+ .to raise_error ArgumentError, error_message
1254
+ end
1255
+ end
1256
+ end
1257
+
1258
+ describe '#plural?' do
1259
+ include_examples 'should define predicate', :plural?, true
1260
+
1261
+ context 'when initialized with plural: false' do
1262
+ let(:constructor_options) { super().merge(plural: false) }
1263
+
1264
+ it { expect(subject.plural?).to be false }
1265
+ end
1266
+
1267
+ context 'when initialized with plural: true' do
1268
+ let(:constructor_options) { super().merge(plural: true) }
1269
+
1270
+ it { expect(subject.plural?).to be true }
1271
+ end
1272
+
1273
+ context 'when initialized with singular: false' do
1274
+ let(:constructor_options) { super().merge(singular: false) }
1275
+
1276
+ it { expect(subject.plural?).to be true }
1277
+ end
1278
+
1279
+ context 'when initialized with singular: true' do
1280
+ let(:constructor_options) { super().merge(singular: true) }
1281
+
1282
+ it { expect(subject.plural?).to be false }
1283
+ end
1284
+ end
1285
+
1286
+ describe '#singular?' do
1287
+ include_examples 'should define predicate', :singular?, false
1288
+
1289
+ context 'when initialized with plural: false' do
1290
+ let(:constructor_options) { super().merge(plural: false) }
1291
+
1292
+ it { expect(subject.singular?).to be true }
1293
+ end
1294
+
1295
+ context 'when initialized with plural: true' do
1296
+ let(:constructor_options) { super().merge(plural: true) }
1297
+
1298
+ it { expect(subject.singular?).to be false }
1299
+ end
1300
+
1301
+ context 'when initialized with singular: false' do
1302
+ let(:constructor_options) { super().merge(singular: false) }
1303
+
1304
+ it { expect(subject.singular?).to be false }
1305
+ end
1306
+
1307
+ context 'when initialized with singular: true' do
1308
+ let(:constructor_options) { super().merge(singular: true) }
1309
+
1310
+ it { expect(subject.singular?).to be true }
1311
+ end
1312
+ end
1313
+ end
1314
+ end
1315
+
1316
+ # Contract validating a Relation's primary key properties.
1317
+ module ShouldDefinePrimaryKeysContract
1318
+ extend RSpec::SleepingKingStudios::Contract
1319
+
1320
+ # @!method apply(example_group)
1321
+ # Adds the contract to the example group.
1322
+ #
1323
+ # @param example_group [RSpec::Core::ExampleGroup] the example group to
1324
+ # which the contract is applied.
1325
+ contract do
1326
+ describe '#primary_key_name' do
1327
+ let(:expected_primary_key_name) do
1328
+ return super() if defined?(super())
1329
+
1330
+ constructor_options.fetch(:primary_key_name, 'id')
1331
+ end
1332
+
1333
+ include_examples 'should define reader',
1334
+ :primary_key_name,
1335
+ -> { expected_primary_key_name }
1336
+
1337
+ context 'when initialized with primary_key_name: a String' do
1338
+ let(:primary_key_name) { 'uuid' }
1339
+ let(:constructor_options) do
1340
+ super().merge(primary_key_name: primary_key_name)
1341
+ end
1342
+
1343
+ it { expect(subject.primary_key_name).to be == primary_key_name }
1344
+ end
1345
+
1346
+ context 'when initialized with primary_key_name: a Symbol' do
1347
+ let(:primary_key_name) { :uuid }
1348
+ let(:constructor_options) do
1349
+ super().merge(primary_key_name: primary_key_name)
1350
+ end
1351
+
1352
+ it 'should set the primary key name' do
1353
+ expect(subject.primary_key_name).to be == primary_key_name.to_s
1354
+ end
1355
+ end
1356
+ end
1357
+
1358
+ describe '#primary_key_type' do
1359
+ let(:expected_primary_key_type) do
1360
+ return super() if defined?(super())
1361
+
1362
+ constructor_options.fetch(:primary_key_type, Integer)
1363
+ end
1364
+
1365
+ include_examples 'should define reader',
1366
+ :primary_key_type,
1367
+ -> { expected_primary_key_type }
1368
+
1369
+ context 'when initialized with primary_key_type: value' do
1370
+ let(:primary_key_type) { String }
1371
+ let(:constructor_options) do
1372
+ super().merge(primary_key_type: primary_key_type)
1373
+ end
1374
+
1375
+ it { expect(subject.primary_key_type).to be == primary_key_type }
1376
+ end
1377
+ end
1378
+ end
1379
+ end
1380
+ end
1381
+ end