cuprum-collections 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -0
- data/DEVELOPMENT.md +2 -2
- data/README.md +13 -11
- data/lib/cuprum/collections/association.rb +256 -0
- data/lib/cuprum/collections/associations/belongs_to.rb +32 -0
- data/lib/cuprum/collections/associations/has_many.rb +23 -0
- data/lib/cuprum/collections/associations/has_one.rb +23 -0
- data/lib/cuprum/collections/associations.rb +10 -0
- data/lib/cuprum/collections/basic/collection.rb +39 -74
- data/lib/cuprum/collections/basic/commands/find_many.rb +1 -1
- data/lib/cuprum/collections/basic/commands/find_matching.rb +1 -1
- data/lib/cuprum/collections/basic/repository.rb +9 -33
- data/lib/cuprum/collections/basic.rb +1 -0
- data/lib/cuprum/collections/collection.rb +154 -0
- data/lib/cuprum/collections/commands/associations/find_many.rb +161 -0
- data/lib/cuprum/collections/commands/associations/require_many.rb +48 -0
- data/lib/cuprum/collections/commands/associations.rb +13 -0
- data/lib/cuprum/collections/commands/find_one_matching.rb +1 -1
- data/lib/cuprum/collections/commands.rb +1 -0
- data/lib/cuprum/collections/errors/abstract_find_error.rb +1 -1
- data/lib/cuprum/collections/relation.rb +401 -0
- data/lib/cuprum/collections/repository.rb +71 -4
- data/lib/cuprum/collections/resource.rb +65 -0
- data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +2137 -0
- data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +484 -0
- data/lib/cuprum/collections/rspec/contracts/basic.rb +11 -0
- data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +429 -0
- data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +1462 -0
- data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +1093 -0
- data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +1381 -0
- data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +605 -0
- data/lib/cuprum/collections/rspec/contracts.rb +23 -0
- data/lib/cuprum/collections/rspec/fixtures.rb +85 -82
- data/lib/cuprum/collections/rspec.rb +4 -1
- data/lib/cuprum/collections/version.rb +1 -1
- data/lib/cuprum/collections.rb +9 -4
- metadata +23 -19
- data/lib/cuprum/collections/base.rb +0 -11
- data/lib/cuprum/collections/basic/rspec/command_contract.rb +0 -392
- data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +0 -168
- data/lib/cuprum/collections/rspec/build_one_command_contract.rb +0 -93
- data/lib/cuprum/collections/rspec/collection_contract.rb +0 -190
- data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +0 -108
- data/lib/cuprum/collections/rspec/find_many_command_contract.rb +0 -407
- data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +0 -194
- data/lib/cuprum/collections/rspec/find_one_command_contract.rb +0 -157
- data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +0 -84
- data/lib/cuprum/collections/rspec/query_builder_contract.rb +0 -92
- data/lib/cuprum/collections/rspec/query_contract.rb +0 -650
- data/lib/cuprum/collections/rspec/querying_contract.rb +0 -298
- data/lib/cuprum/collections/rspec/repository_contract.rb +0 -235
- data/lib/cuprum/collections/rspec/update_one_command_contract.rb +0 -80
- data/lib/cuprum/collections/rspec/validate_one_command_contract.rb +0 -96
@@ -0,0 +1,2137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/rspec/contracts'
|
4
|
+
require 'cuprum/collections/rspec/contracts/relation_contracts'
|
5
|
+
|
6
|
+
module Cuprum::Collections::RSpec::Contracts
|
7
|
+
# Contracts for asserting on Association objects.
|
8
|
+
module AssociationContracts
|
9
|
+
# Contract validating the behavior of an Association.
|
10
|
+
module ShouldBeAnAssociationContract
|
11
|
+
extend RSpec::SleepingKingStudios::Contract
|
12
|
+
|
13
|
+
# @!method apply(example_group)
|
14
|
+
# Adds the contract to the example group.
|
15
|
+
#
|
16
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
17
|
+
# which the contract is applied.
|
18
|
+
contract do
|
19
|
+
include Cuprum::Collections::RSpec::Contracts::RelationContracts
|
20
|
+
|
21
|
+
example_class 'Author'
|
22
|
+
example_class 'Chapter'
|
23
|
+
example_class 'Writer'
|
24
|
+
|
25
|
+
include_contract 'should be a relation'
|
26
|
+
|
27
|
+
include_contract 'should disambiguate parameter',
|
28
|
+
:entity_class,
|
29
|
+
as: %i[association_class resource_class],
|
30
|
+
value: Grimoire
|
31
|
+
|
32
|
+
include_contract 'should disambiguate parameter',
|
33
|
+
:name,
|
34
|
+
as: %i[association_name resource_name]
|
35
|
+
|
36
|
+
include_contract 'should disambiguate parameter',
|
37
|
+
:singular_name,
|
38
|
+
as: :singular_resource_name
|
39
|
+
|
40
|
+
include_contract 'should define primary keys'
|
41
|
+
|
42
|
+
describe '#build_entities_query' do
|
43
|
+
it 'should define the method' do
|
44
|
+
expect(association)
|
45
|
+
.to respond_to(:build_entities_query)
|
46
|
+
.with_unlimited_arguments
|
47
|
+
.and_keywords(:allow_nil, :deduplicate)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#build_keys_query' do
|
52
|
+
it 'should define the method' do
|
53
|
+
expect(association)
|
54
|
+
.to respond_to(:build_keys_query)
|
55
|
+
.with_unlimited_arguments
|
56
|
+
.and_keywords(:allow_nil, :deduplicate)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#foreign_key_name' do
|
61
|
+
include_examples 'should define reader', :foreign_key_name
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#inverse' do
|
65
|
+
include_examples 'should define reader', :inverse, nil
|
66
|
+
|
67
|
+
context 'when initialized with inverse: value' do
|
68
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
69
|
+
let(:constructor_options) do
|
70
|
+
super().merge(inverse: inverse)
|
71
|
+
end
|
72
|
+
|
73
|
+
it { expect(subject.inverse).to be == inverse }
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with a copy with assigned inverse' do
|
77
|
+
subject { super().with_inverse(new_inverse) }
|
78
|
+
|
79
|
+
let(:new_inverse) { described_class.new(name: 'chapters') }
|
80
|
+
|
81
|
+
it { expect(subject.inverse).to be == new_inverse }
|
82
|
+
|
83
|
+
context 'when initialized with inverse: value' do
|
84
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
85
|
+
let(:constructor_options) do
|
86
|
+
super().merge(inverse: inverse)
|
87
|
+
end
|
88
|
+
|
89
|
+
it { expect(subject.inverse).to be == new_inverse }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#inverse_class' do
|
95
|
+
include_examples 'should define reader', :inverse_class, nil
|
96
|
+
|
97
|
+
context 'when initialized with inverse: value' do
|
98
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
99
|
+
let(:constructor_options) do
|
100
|
+
super().merge(inverse: inverse)
|
101
|
+
end
|
102
|
+
|
103
|
+
it { expect(subject.inverse_class).to be == Author }
|
104
|
+
|
105
|
+
context 'when initialized with inverse_class: a Class' do
|
106
|
+
let(:constructor_options) do
|
107
|
+
super().merge(inverse_class: Writer)
|
108
|
+
end
|
109
|
+
|
110
|
+
it { expect(subject.inverse_class).to be == Writer }
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when initialized with inverse_class: a String' do
|
114
|
+
let(:constructor_options) do
|
115
|
+
super().merge(inverse_class: 'Writer')
|
116
|
+
end
|
117
|
+
|
118
|
+
it { expect(subject.inverse_class).to be == Writer }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when initialized with inverse_class: a Class' do
|
123
|
+
let(:constructor_options) do
|
124
|
+
super().merge(inverse_class: Writer)
|
125
|
+
end
|
126
|
+
|
127
|
+
it { expect(subject.inverse_class).to be == Writer }
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when initialized with inverse_class: a String' do
|
131
|
+
let(:constructor_options) do
|
132
|
+
super().merge(inverse_class: 'Writer')
|
133
|
+
end
|
134
|
+
|
135
|
+
it { expect(subject.inverse_class).to be == Writer }
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'with a copy with assigned inverse' do
|
139
|
+
subject do
|
140
|
+
super().tap(&:inverse_class).with_inverse(new_inverse)
|
141
|
+
end
|
142
|
+
|
143
|
+
let(:new_inverse) { described_class.new(name: 'chapters') }
|
144
|
+
|
145
|
+
it { expect(subject.inverse_class).to be == Chapter }
|
146
|
+
|
147
|
+
context 'when initialized with inverse_class: a Class' do
|
148
|
+
let(:constructor_options) do
|
149
|
+
super().merge(inverse_class: Writer)
|
150
|
+
end
|
151
|
+
|
152
|
+
it { expect(subject.inverse_class).to be == Writer }
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'when initialized with inverse_class: a String' do
|
156
|
+
let(:constructor_options) do
|
157
|
+
super().merge(inverse_class: 'Writer')
|
158
|
+
end
|
159
|
+
|
160
|
+
it { expect(subject.inverse_class).to be == Writer }
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'when initialized with inverse: value' do
|
164
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
165
|
+
let(:constructor_options) do
|
166
|
+
super().merge(inverse: inverse)
|
167
|
+
end
|
168
|
+
|
169
|
+
it { expect(subject.inverse_class).to be == Chapter }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe '#inverse_key_name' do
|
175
|
+
include_examples 'should define reader', :inverse_key_name
|
176
|
+
end
|
177
|
+
|
178
|
+
describe '#inverse_name' do
|
179
|
+
include_examples 'should define reader', :inverse_name, nil
|
180
|
+
|
181
|
+
context 'when initialized with inverse: value' do
|
182
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
183
|
+
let(:constructor_options) do
|
184
|
+
super().merge(inverse: inverse)
|
185
|
+
end
|
186
|
+
|
187
|
+
it { expect(subject.inverse_name).to be == 'authors' }
|
188
|
+
|
189
|
+
context 'when initialized with inverse_name: a String' do
|
190
|
+
let(:constructor_options) do
|
191
|
+
super().merge(inverse_name: 'writers')
|
192
|
+
end
|
193
|
+
|
194
|
+
it { expect(subject.inverse_name).to be == 'writers' }
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'when initialized with inverse_name: a Symbol' do
|
198
|
+
let(:constructor_options) do
|
199
|
+
super().merge(inverse_name: :writers)
|
200
|
+
end
|
201
|
+
|
202
|
+
it { expect(subject.inverse_name).to be == 'writers' }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'when initialized with inverse_name: a String' do
|
207
|
+
let(:constructor_options) do
|
208
|
+
super().merge(inverse_name: 'writers')
|
209
|
+
end
|
210
|
+
|
211
|
+
it { expect(subject.inverse_name).to be == 'writers' }
|
212
|
+
end
|
213
|
+
|
214
|
+
context 'when initialized with inverse_name: a Symbol' do
|
215
|
+
let(:constructor_options) do
|
216
|
+
super().merge(inverse_name: :writers)
|
217
|
+
end
|
218
|
+
|
219
|
+
it { expect(subject.inverse_name).to be == 'writers' }
|
220
|
+
end
|
221
|
+
|
222
|
+
context 'with a copy with assigned inverse' do
|
223
|
+
subject do
|
224
|
+
super().tap(&:inverse_name).with_inverse(new_inverse)
|
225
|
+
end
|
226
|
+
|
227
|
+
let(:new_inverse) { described_class.new(name: 'chapters') }
|
228
|
+
|
229
|
+
it { expect(subject.inverse_name).to be == 'chapters' }
|
230
|
+
|
231
|
+
context 'when initialized with inverse: value' do
|
232
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
233
|
+
let(:constructor_options) do
|
234
|
+
super().merge(inverse: inverse)
|
235
|
+
end
|
236
|
+
|
237
|
+
it { expect(subject.inverse_name).to be == 'chapters' }
|
238
|
+
end
|
239
|
+
|
240
|
+
context 'when initialized with inverse_name: a String' do
|
241
|
+
let(:constructor_options) do
|
242
|
+
super().merge(inverse_name: 'writers')
|
243
|
+
end
|
244
|
+
|
245
|
+
it { expect(subject.inverse_name).to be == 'writers' }
|
246
|
+
end
|
247
|
+
|
248
|
+
context 'when initialized with inverse_name: a Symbol' do
|
249
|
+
let(:constructor_options) do
|
250
|
+
super().merge(inverse_name: :writers)
|
251
|
+
end
|
252
|
+
|
253
|
+
it { expect(subject.inverse_name).to be == 'writers' }
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
describe '#map_entities_to_keys' do
|
259
|
+
it 'should define the method' do
|
260
|
+
expect(subject)
|
261
|
+
.to respond_to(:map_entities_to_keys)
|
262
|
+
.with_unlimited_arguments
|
263
|
+
.and_keywords(:allow_nil, :deduplicate, :strict)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe '#options' do
|
268
|
+
context 'when initialized with inverse: value' do
|
269
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
270
|
+
let(:constructor_options) do
|
271
|
+
super().merge(inverse: inverse)
|
272
|
+
end
|
273
|
+
|
274
|
+
it { expect(subject.options).to be == {} }
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe '#plural?' do
|
279
|
+
include_examples 'should define predicate', :plural?
|
280
|
+
end
|
281
|
+
|
282
|
+
describe '#primary_key_query?' do
|
283
|
+
include_examples 'should define predicate', :primary_key_query?
|
284
|
+
end
|
285
|
+
|
286
|
+
describe '#query_key_name' do
|
287
|
+
include_examples 'should define reader', :query_key_name
|
288
|
+
end
|
289
|
+
|
290
|
+
describe '#singular_inverse_name' do
|
291
|
+
include_examples 'should define reader', :singular_inverse_name, nil
|
292
|
+
|
293
|
+
context 'when initialized with inverse: value' do
|
294
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
295
|
+
let(:constructor_options) do
|
296
|
+
super().merge(inverse: inverse)
|
297
|
+
end
|
298
|
+
|
299
|
+
it { expect(subject.singular_inverse_name).to be == 'author' }
|
300
|
+
|
301
|
+
context 'when initialized with singular_inverse_name: a String' do
|
302
|
+
let(:singular_inverse_name) { 'writer' }
|
303
|
+
let(:constructor_options) do
|
304
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
305
|
+
end
|
306
|
+
|
307
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
308
|
+
end
|
309
|
+
|
310
|
+
context 'when initialized with singular_inverse_name: a Symbol' do
|
311
|
+
let(:singular_inverse_name) { :writer }
|
312
|
+
let(:constructor_options) do
|
313
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
314
|
+
end
|
315
|
+
|
316
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
context 'when initialized with inverse_name: value' do
|
321
|
+
let(:inverse_name) { 'authors' }
|
322
|
+
let(:constructor_options) do
|
323
|
+
super().merge(inverse_name: inverse_name)
|
324
|
+
end
|
325
|
+
|
326
|
+
it { expect(subject.singular_inverse_name).to be == 'author' }
|
327
|
+
|
328
|
+
context 'when initialized with singular_inverse_name: a String' do
|
329
|
+
let(:singular_inverse_name) { 'writer' }
|
330
|
+
let(:constructor_options) do
|
331
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
332
|
+
end
|
333
|
+
|
334
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'when initialized with singular_inverse_name: a Symbol' do
|
338
|
+
let(:singular_inverse_name) { :writer }
|
339
|
+
let(:constructor_options) do
|
340
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
341
|
+
end
|
342
|
+
|
343
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context 'when initialized with singular_inverse_name: a String' do
|
348
|
+
let(:singular_inverse_name) { 'writer' }
|
349
|
+
let(:constructor_options) do
|
350
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
351
|
+
end
|
352
|
+
|
353
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
354
|
+
end
|
355
|
+
|
356
|
+
context 'when initialized with singular_inverse_name: a Symbol' do
|
357
|
+
let(:singular_inverse_name) { :writer }
|
358
|
+
let(:constructor_options) do
|
359
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
360
|
+
end
|
361
|
+
|
362
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
363
|
+
end
|
364
|
+
|
365
|
+
context 'with a copy with assigned inverse' do
|
366
|
+
subject do
|
367
|
+
super().tap(&:singular_inverse_name).with_inverse(new_inverse)
|
368
|
+
end
|
369
|
+
|
370
|
+
let(:new_inverse) { described_class.new(name: 'chapters') }
|
371
|
+
|
372
|
+
it { expect(subject.singular_inverse_name).to be == 'chapter' }
|
373
|
+
|
374
|
+
context 'when initialized with inverse: value' do
|
375
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
376
|
+
let(:constructor_options) do
|
377
|
+
super().merge(inverse: inverse)
|
378
|
+
end
|
379
|
+
|
380
|
+
it { expect(subject.singular_inverse_name).to be == 'chapter' }
|
381
|
+
end
|
382
|
+
|
383
|
+
context 'when initialized with inverse_name: value' do
|
384
|
+
let(:inverse_name) { 'authors' }
|
385
|
+
let(:constructor_options) do
|
386
|
+
super().merge(inverse_name: inverse_name)
|
387
|
+
end
|
388
|
+
|
389
|
+
it { expect(subject.singular_inverse_name).to be == 'chapter' }
|
390
|
+
|
391
|
+
context 'when initialized with singular_inverse_name: a String' do
|
392
|
+
let(:singular_inverse_name) { 'writer' }
|
393
|
+
let(:constructor_options) do
|
394
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
395
|
+
end
|
396
|
+
|
397
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
398
|
+
end
|
399
|
+
|
400
|
+
context 'when initialized with singular_inverse_name: a Symbol' do
|
401
|
+
let(:singular_inverse_name) { :writer }
|
402
|
+
let(:constructor_options) do
|
403
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
404
|
+
end
|
405
|
+
|
406
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
context 'when initialized with singular_inverse_name: a String' do
|
411
|
+
let(:singular_inverse_name) { 'writer' }
|
412
|
+
let(:constructor_options) do
|
413
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
414
|
+
end
|
415
|
+
|
416
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
417
|
+
end
|
418
|
+
|
419
|
+
context 'when initialized with singular_inverse_name: a Symbol' do
|
420
|
+
let(:singular_inverse_name) { :writer }
|
421
|
+
let(:constructor_options) do
|
422
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
423
|
+
end
|
424
|
+
|
425
|
+
it { expect(subject.singular_inverse_name).to be == 'writer' }
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
describe '#singular?' do
|
431
|
+
include_examples 'should define predicate', :singular?
|
432
|
+
end
|
433
|
+
|
434
|
+
describe '#with_inverse' do
|
435
|
+
it 'should define the method' do
|
436
|
+
expect(association).to respond_to(:with_inverse).with(1).argument
|
437
|
+
end
|
438
|
+
|
439
|
+
context 'with a copy with assigned inverse' do
|
440
|
+
let(:new_inverse) { described_class.new(name: 'chapters') }
|
441
|
+
let(:copy) { subject.with_inverse(new_inverse) }
|
442
|
+
|
443
|
+
it { expect(copy).to be_a described_class }
|
444
|
+
|
445
|
+
it { expect(copy.inverse).to be == new_inverse }
|
446
|
+
|
447
|
+
it { expect(subject.inverse).to be nil }
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
# Contract validating the behavior of a BelongsToAssociation.
|
454
|
+
module ShouldBeABelongsToAssociationContract
|
455
|
+
extend RSpec::SleepingKingStudios::Contract
|
456
|
+
|
457
|
+
# @!method apply(example_group)
|
458
|
+
# Adds the contract to the example group.
|
459
|
+
#
|
460
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
461
|
+
# which the contract is applied.
|
462
|
+
contract do
|
463
|
+
include Cuprum::Collections::RSpec::Contracts::RelationContracts
|
464
|
+
|
465
|
+
include_contract 'should be an association'
|
466
|
+
|
467
|
+
describe '#build_entities_query' do
|
468
|
+
let(:key) { subject.foreign_key_name }
|
469
|
+
let(:entities) { [] }
|
470
|
+
let(:options) { {} }
|
471
|
+
let(:query) do
|
472
|
+
association.build_entities_query(*entities, **options)
|
473
|
+
end
|
474
|
+
let(:evaluated) do
|
475
|
+
Spec::QueryBuilder.new.instance_exec(&query)
|
476
|
+
end
|
477
|
+
|
478
|
+
example_class 'Spec::Entity' do |klass|
|
479
|
+
klass.define_method(:initialize) do |**attributes|
|
480
|
+
attributes.each do |key, value|
|
481
|
+
instance_variable_set(:"@#{key}", value)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
klass.attr_reader :book_id
|
486
|
+
end
|
487
|
+
|
488
|
+
example_class 'Spec::QueryBuilder' do |klass|
|
489
|
+
klass.define_method(:one_of) { |values| { 'one_of' => values } }
|
490
|
+
end
|
491
|
+
|
492
|
+
describe 'with no entities' do
|
493
|
+
let(:entities) { [] }
|
494
|
+
|
495
|
+
it { expect(query).to be_a Proc }
|
496
|
+
|
497
|
+
it { expect(evaluated).to be == {} }
|
498
|
+
end
|
499
|
+
|
500
|
+
describe 'with one nil entity' do
|
501
|
+
let(:entities) { [nil] }
|
502
|
+
|
503
|
+
it { expect(evaluated).to be == {} }
|
504
|
+
end
|
505
|
+
|
506
|
+
describe 'with one invalid entity' do
|
507
|
+
let(:entities) { [Object.new.freeze] }
|
508
|
+
let(:error_message) do
|
509
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
510
|
+
end
|
511
|
+
|
512
|
+
it 'should raise an exception' do
|
513
|
+
expect { association.build_entities_query(*entities) }
|
514
|
+
.to raise_error ArgumentError, error_message
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
describe 'with one entity that responds to #[] and key: nil' do
|
519
|
+
let(:entities) { [{ key => nil }] }
|
520
|
+
|
521
|
+
it { expect(evaluated).to be == {} }
|
522
|
+
|
523
|
+
describe 'with allow_nil: true' do
|
524
|
+
let(:options) { super().merge(allow_nil: true) }
|
525
|
+
|
526
|
+
it { expect(evaluated).to be == { 'id' => nil } }
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
describe 'with one entity that responds to #[] and key: value' do
|
531
|
+
let(:entities) { [{ key => 0 }] }
|
532
|
+
|
533
|
+
it { expect(evaluated).to be == { 'id' => 0 } }
|
534
|
+
end
|
535
|
+
|
536
|
+
describe 'with one entity that responds to #id and key: nil' do
|
537
|
+
let(:entities) { [Spec::Entity.new(key => nil)] }
|
538
|
+
|
539
|
+
it { expect(evaluated).to be == {} }
|
540
|
+
|
541
|
+
describe 'with allow_nil: true' do
|
542
|
+
let(:options) { super().merge(allow_nil: true) }
|
543
|
+
|
544
|
+
it { expect(evaluated).to be == { 'id' => nil } }
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
describe 'with one entity that responds to #id and key: value' do
|
549
|
+
let(:entities) { [Spec::Entity.new(key => 0)] }
|
550
|
+
|
551
|
+
it { expect(evaluated).to be == { 'id' => 0 } }
|
552
|
+
end
|
553
|
+
|
554
|
+
describe 'with multiple entities' do
|
555
|
+
let(:entities) do
|
556
|
+
[
|
557
|
+
Spec::Entity.new(key => 0),
|
558
|
+
Spec::Entity.new(key => 1),
|
559
|
+
Spec::Entity.new(key => 2)
|
560
|
+
]
|
561
|
+
end
|
562
|
+
let(:expected) do
|
563
|
+
{ 'id' => { 'one_of' => [0, 1, 2] } }
|
564
|
+
end
|
565
|
+
|
566
|
+
it { expect(evaluated).to be == expected }
|
567
|
+
end
|
568
|
+
|
569
|
+
describe 'with multiple entities including nil' do
|
570
|
+
let(:entities) do
|
571
|
+
[
|
572
|
+
Spec::Entity.new(key => 0),
|
573
|
+
nil,
|
574
|
+
Spec::Entity.new(key => 1),
|
575
|
+
nil,
|
576
|
+
Spec::Entity.new(key => 2)
|
577
|
+
]
|
578
|
+
end
|
579
|
+
let(:expected) do
|
580
|
+
{ 'id' => { 'one_of' => [0, 1, 2] } }
|
581
|
+
end
|
582
|
+
|
583
|
+
it { expect(evaluated).to be == expected }
|
584
|
+
end
|
585
|
+
|
586
|
+
describe 'with multiple entities including nil ids' do
|
587
|
+
let(:entities) do
|
588
|
+
[
|
589
|
+
Spec::Entity.new(key => 0),
|
590
|
+
Spec::Entity.new(key => nil),
|
591
|
+
Spec::Entity.new(key => 1),
|
592
|
+
Spec::Entity.new(key => nil),
|
593
|
+
Spec::Entity.new(key => 2)
|
594
|
+
]
|
595
|
+
end
|
596
|
+
let(:expected) do
|
597
|
+
{ 'id' => { 'one_of' => [0, 1, 2] } }
|
598
|
+
end
|
599
|
+
|
600
|
+
it { expect(evaluated).to be == expected }
|
601
|
+
|
602
|
+
describe 'with allow_nil: true' do
|
603
|
+
let(:options) { super().merge(allow_nil: true) }
|
604
|
+
let(:expected) do
|
605
|
+
{ 'id' => { 'one_of' => [0, nil, 1, 2] } }
|
606
|
+
end
|
607
|
+
|
608
|
+
it { expect(evaluated).to be == expected }
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
describe 'with multiple entities including duplicate ids' do
|
613
|
+
let(:entities) do
|
614
|
+
[
|
615
|
+
Spec::Entity.new(key => 0),
|
616
|
+
Spec::Entity.new(key => 1),
|
617
|
+
Spec::Entity.new(key => 0),
|
618
|
+
Spec::Entity.new(key => 1),
|
619
|
+
Spec::Entity.new(key => 2)
|
620
|
+
]
|
621
|
+
end
|
622
|
+
let(:expected) do
|
623
|
+
{ 'id' => { 'one_of' => [0, 1, 2] } }
|
624
|
+
end
|
625
|
+
|
626
|
+
it { expect(evaluated).to be == expected }
|
627
|
+
|
628
|
+
describe 'with deduplicate: false' do
|
629
|
+
let(:options) { super().merge(deduplicate: false) }
|
630
|
+
let(:expected) do
|
631
|
+
{ 'id' => { 'one_of' => [0, 1, 0, 1, 2] } }
|
632
|
+
end
|
633
|
+
|
634
|
+
it { expect(evaluated).to be == expected }
|
635
|
+
end
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
describe '#build_keys_query' do
|
640
|
+
let(:keys) { [] }
|
641
|
+
let(:options) { {} }
|
642
|
+
let(:query) do
|
643
|
+
association.build_keys_query(*keys, **options)
|
644
|
+
end
|
645
|
+
let(:evaluated) do
|
646
|
+
Spec::QueryBuilder.new.instance_exec(&query)
|
647
|
+
end
|
648
|
+
|
649
|
+
example_class 'Spec::QueryBuilder' do |klass|
|
650
|
+
klass.define_method(:one_of) { |values| { 'one_of' => values } }
|
651
|
+
end
|
652
|
+
|
653
|
+
describe 'with no keys' do
|
654
|
+
let(:keys) { [] }
|
655
|
+
|
656
|
+
it { expect(query).to be_a Proc }
|
657
|
+
|
658
|
+
it { expect(evaluated).to be == {} }
|
659
|
+
end
|
660
|
+
|
661
|
+
describe 'with one nil key' do
|
662
|
+
let(:keys) { [nil] }
|
663
|
+
|
664
|
+
it { expect(evaluated).to be == {} }
|
665
|
+
|
666
|
+
describe 'with allow_nil: true' do
|
667
|
+
let(:options) { { allow_nil: true } }
|
668
|
+
|
669
|
+
it { expect(evaluated).to be == { 'id' => nil } }
|
670
|
+
end
|
671
|
+
end
|
672
|
+
|
673
|
+
describe 'with one non-nil key' do
|
674
|
+
let(:keys) { [0] }
|
675
|
+
|
676
|
+
it { expect(evaluated).to be == { 'id' => 0 } }
|
677
|
+
end
|
678
|
+
|
679
|
+
describe 'with many keys' do
|
680
|
+
let(:keys) { [0, 1, 2] }
|
681
|
+
let(:expected) { { 'id' => { 'one_of' => keys } } }
|
682
|
+
|
683
|
+
it { expect(evaluated).to be == expected }
|
684
|
+
end
|
685
|
+
|
686
|
+
describe 'with many keys including nil' do
|
687
|
+
let(:keys) { [0, nil, 2] }
|
688
|
+
let(:expected) { { 'id' => { 'one_of' => [0, 2] } } }
|
689
|
+
|
690
|
+
it { expect(evaluated).to be == expected }
|
691
|
+
|
692
|
+
describe 'with allow_nil: true' do
|
693
|
+
let(:options) { { allow_nil: true } }
|
694
|
+
let(:expected) do
|
695
|
+
{ 'id' => { 'one_of' => [0, nil, 2] } }
|
696
|
+
end
|
697
|
+
|
698
|
+
it { expect(evaluated).to be == expected }
|
699
|
+
end
|
700
|
+
end
|
701
|
+
|
702
|
+
describe 'with many non-unique keys' do
|
703
|
+
let(:keys) { [0, 1, 2, 1, 2] }
|
704
|
+
let(:expected) { { 'id' => { 'one_of' => keys.uniq } } }
|
705
|
+
|
706
|
+
it { expect(evaluated).to be == expected }
|
707
|
+
|
708
|
+
describe 'with deduplicate: false' do
|
709
|
+
let(:options) { super().merge(deduplicate: false) }
|
710
|
+
let(:expected) do
|
711
|
+
{ 'id' => { 'one_of' => [0, 1, 2, 1, 2] } }
|
712
|
+
end
|
713
|
+
|
714
|
+
it { expect(evaluated).to be == expected }
|
715
|
+
end
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
describe '#foreign_key_name' do
|
720
|
+
let(:expected) { "#{tools.str.singularize(name)}_id" }
|
721
|
+
|
722
|
+
def tools
|
723
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
724
|
+
end
|
725
|
+
|
726
|
+
it { expect(subject.foreign_key_name).to be == expected }
|
727
|
+
|
728
|
+
context 'when initialized with foreign_key_name: a String' do
|
729
|
+
let(:foreign_key_name) { 'writer_id' }
|
730
|
+
let(:constructor_options) do
|
731
|
+
super().merge(foreign_key_name: foreign_key_name)
|
732
|
+
end
|
733
|
+
|
734
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
735
|
+
end
|
736
|
+
|
737
|
+
context 'when initialized with foreign_key_name: a String' do
|
738
|
+
let(:foreign_key_name) { :writer_id }
|
739
|
+
let(:constructor_options) do
|
740
|
+
super().merge(foreign_key_name: foreign_key_name)
|
741
|
+
end
|
742
|
+
|
743
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
744
|
+
end
|
745
|
+
|
746
|
+
context 'when initialized with singular_name: value' do
|
747
|
+
let(:singular_name) { 'author' }
|
748
|
+
let(:constructor_options) do
|
749
|
+
super().merge(singular_name: singular_name)
|
750
|
+
end
|
751
|
+
|
752
|
+
it { expect(subject.foreign_key_name).to be == 'author_id' }
|
753
|
+
|
754
|
+
context 'when initialized with foreign_key_name: a String' do
|
755
|
+
let(:foreign_key_name) { 'writer_id' }
|
756
|
+
let(:constructor_options) do
|
757
|
+
super().merge(foreign_key_name: foreign_key_name)
|
758
|
+
end
|
759
|
+
|
760
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
761
|
+
end
|
762
|
+
|
763
|
+
context 'when initialized with foreign_key_name: a String' do
|
764
|
+
let(:foreign_key_name) { :writer_id }
|
765
|
+
let(:constructor_options) do
|
766
|
+
super().merge(foreign_key_name: foreign_key_name)
|
767
|
+
end
|
768
|
+
|
769
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
770
|
+
end
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
describe '#inverse_key_name' do
|
775
|
+
let(:expected) { "#{tools.str.singularize(name)}_id" }
|
776
|
+
|
777
|
+
def tools
|
778
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
779
|
+
end
|
780
|
+
|
781
|
+
it { expect(subject.inverse_key_name).to be == expected }
|
782
|
+
|
783
|
+
context 'when initialized with foreign_key_name: a String' do
|
784
|
+
let(:foreign_key_name) { 'writer_id' }
|
785
|
+
let(:constructor_options) do
|
786
|
+
super().merge(foreign_key_name: foreign_key_name)
|
787
|
+
end
|
788
|
+
|
789
|
+
it { expect(subject.inverse_key_name).to be == 'writer_id' }
|
790
|
+
end
|
791
|
+
|
792
|
+
context 'when initialized with foreign_key_name: a String' do
|
793
|
+
let(:foreign_key_name) { :writer_id }
|
794
|
+
let(:constructor_options) do
|
795
|
+
super().merge(foreign_key_name: foreign_key_name)
|
796
|
+
end
|
797
|
+
|
798
|
+
it { expect(subject.inverse_key_name).to be == 'writer_id' }
|
799
|
+
end
|
800
|
+
|
801
|
+
context 'when initialized with singular_name: value' do
|
802
|
+
let(:singular_name) { 'author' }
|
803
|
+
let(:constructor_options) do
|
804
|
+
super().merge(singular_name: singular_name)
|
805
|
+
end
|
806
|
+
|
807
|
+
it { expect(subject.inverse_key_name).to be == 'author_id' }
|
808
|
+
|
809
|
+
context 'when initialized with foreign_key_name: a String' do
|
810
|
+
let(:foreign_key_name) { 'writer_id' }
|
811
|
+
let(:constructor_options) do
|
812
|
+
super().merge(foreign_key_name: foreign_key_name)
|
813
|
+
end
|
814
|
+
|
815
|
+
it { expect(subject.inverse_key_name).to be == 'writer_id' }
|
816
|
+
end
|
817
|
+
|
818
|
+
context 'when initialized with foreign_key_name: a String' do
|
819
|
+
let(:foreign_key_name) { :writer_id }
|
820
|
+
let(:constructor_options) do
|
821
|
+
super().merge(foreign_key_name: foreign_key_name)
|
822
|
+
end
|
823
|
+
|
824
|
+
it { expect(subject.inverse_key_name).to be == 'writer_id' }
|
825
|
+
end
|
826
|
+
end
|
827
|
+
end
|
828
|
+
|
829
|
+
describe '#map_entities_to_keys' do
|
830
|
+
let(:key) { subject.foreign_key_name }
|
831
|
+
let(:entities) { [] }
|
832
|
+
let(:options) { {} }
|
833
|
+
let(:keys) do
|
834
|
+
association.map_entities_to_keys(*entities, **options)
|
835
|
+
end
|
836
|
+
|
837
|
+
example_class 'Spec::Entity' do |klass|
|
838
|
+
klass.define_method(:initialize) do |**attributes|
|
839
|
+
attributes.each do |key, value|
|
840
|
+
instance_variable_set(:"@#{key}", value)
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
klass.attr_reader :book_id
|
845
|
+
end
|
846
|
+
|
847
|
+
describe 'with no entities' do
|
848
|
+
let(:entities) { [] }
|
849
|
+
|
850
|
+
it { expect(keys).to be == [] }
|
851
|
+
end
|
852
|
+
|
853
|
+
describe 'with one nil entity' do
|
854
|
+
let(:entities) { [nil] }
|
855
|
+
|
856
|
+
it { expect(keys).to be == [] }
|
857
|
+
end
|
858
|
+
|
859
|
+
describe 'with one invalid entity' do
|
860
|
+
let(:entities) { [Object.new.freeze] }
|
861
|
+
let(:error_message) do
|
862
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
863
|
+
end
|
864
|
+
|
865
|
+
it 'should raise an exception' do
|
866
|
+
expect { association.map_entities_to_keys(*entities) }
|
867
|
+
.to raise_error ArgumentError, error_message
|
868
|
+
end
|
869
|
+
|
870
|
+
describe 'with strict: false' do
|
871
|
+
it 'should raise an exception' do
|
872
|
+
expect do
|
873
|
+
association.map_entities_to_keys(*entities, strict: false)
|
874
|
+
end
|
875
|
+
.to raise_error ArgumentError, error_message
|
876
|
+
end
|
877
|
+
end
|
878
|
+
end
|
879
|
+
|
880
|
+
describe 'with one Integer' do
|
881
|
+
let(:entities) { [0] }
|
882
|
+
let(:error_message) do
|
883
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
884
|
+
end
|
885
|
+
|
886
|
+
it 'should raise an exception' do
|
887
|
+
expect { association.map_entities_to_keys(*entities) }
|
888
|
+
.to raise_error ArgumentError, error_message
|
889
|
+
end
|
890
|
+
|
891
|
+
describe 'with strict: false' do
|
892
|
+
it 'should raise an exception' do
|
893
|
+
expect(
|
894
|
+
association.map_entities_to_keys(*entities, strict: false)
|
895
|
+
)
|
896
|
+
.to be == entities
|
897
|
+
end
|
898
|
+
end
|
899
|
+
|
900
|
+
context 'when initialized with primary_key_type: String' do
|
901
|
+
let(:constructor_options) do
|
902
|
+
super().merge(primary_key_type: String)
|
903
|
+
end
|
904
|
+
|
905
|
+
describe 'with strict: false' do
|
906
|
+
it 'should raise an exception' do
|
907
|
+
expect do
|
908
|
+
association.map_entities_to_keys(*entities, strict: false)
|
909
|
+
end
|
910
|
+
.to raise_error ArgumentError, error_message
|
911
|
+
end
|
912
|
+
end
|
913
|
+
end
|
914
|
+
end
|
915
|
+
|
916
|
+
describe 'with one String' do
|
917
|
+
let(:entities) { %w[0] }
|
918
|
+
let(:error_message) do
|
919
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
920
|
+
end
|
921
|
+
|
922
|
+
it 'should raise an exception' do
|
923
|
+
expect { association.map_entities_to_keys(*entities) }
|
924
|
+
.to raise_error ArgumentError, error_message
|
925
|
+
end
|
926
|
+
|
927
|
+
describe 'with strict: false' do
|
928
|
+
it 'should raise an exception' do
|
929
|
+
expect do
|
930
|
+
association.map_entities_to_keys(*entities, strict: false)
|
931
|
+
end
|
932
|
+
.to raise_error ArgumentError, error_message
|
933
|
+
end
|
934
|
+
end
|
935
|
+
|
936
|
+
context 'when initialized with primary_key_type: String' do
|
937
|
+
let(:constructor_options) do
|
938
|
+
super().merge(primary_key_type: String)
|
939
|
+
end
|
940
|
+
|
941
|
+
describe 'with strict: false' do
|
942
|
+
it 'should raise an exception' do
|
943
|
+
expect(
|
944
|
+
association.map_entities_to_keys(*entities, strict: false)
|
945
|
+
)
|
946
|
+
.to be == entities
|
947
|
+
end
|
948
|
+
end
|
949
|
+
end
|
950
|
+
end
|
951
|
+
|
952
|
+
describe 'with one entity that responds to #[] and key: nil' do
|
953
|
+
let(:entities) { [{ key => nil }] }
|
954
|
+
|
955
|
+
it { expect(keys).to be == [] }
|
956
|
+
|
957
|
+
describe 'with allow_nil: true' do
|
958
|
+
let(:options) { super().merge(allow_nil: true) }
|
959
|
+
|
960
|
+
it { expect(keys).to be == [nil] }
|
961
|
+
end
|
962
|
+
end
|
963
|
+
|
964
|
+
describe 'with one entity that responds to #[] and key: value' do
|
965
|
+
let(:entities) { [{ key => 0 }] }
|
966
|
+
|
967
|
+
it { expect(keys).to be == [0] }
|
968
|
+
end
|
969
|
+
|
970
|
+
describe 'with one entity that responds to #id and key: nil' do
|
971
|
+
let(:entities) { [Spec::Entity.new(key => nil)] }
|
972
|
+
|
973
|
+
it { expect(keys).to be == [] }
|
974
|
+
|
975
|
+
describe 'with allow_nil: true' do
|
976
|
+
let(:options) { super().merge(allow_nil: true) }
|
977
|
+
|
978
|
+
it { expect(keys).to be == [nil] }
|
979
|
+
end
|
980
|
+
end
|
981
|
+
|
982
|
+
describe 'with one entity that responds to #id and key: value' do
|
983
|
+
let(:entities) { [Spec::Entity.new(key => 0)] }
|
984
|
+
|
985
|
+
it { expect(keys).to be == [0] }
|
986
|
+
end
|
987
|
+
|
988
|
+
describe 'with multiple entities' do
|
989
|
+
let(:entities) do
|
990
|
+
[
|
991
|
+
Spec::Entity.new(key => 0),
|
992
|
+
Spec::Entity.new(key => 1),
|
993
|
+
Spec::Entity.new(key => 2)
|
994
|
+
]
|
995
|
+
end
|
996
|
+
|
997
|
+
it { expect(keys).to be == [0, 1, 2] }
|
998
|
+
end
|
999
|
+
|
1000
|
+
describe 'with multiple entities including nil' do
|
1001
|
+
let(:entities) do
|
1002
|
+
[
|
1003
|
+
Spec::Entity.new(key => 0),
|
1004
|
+
nil,
|
1005
|
+
Spec::Entity.new(key => 1),
|
1006
|
+
nil,
|
1007
|
+
Spec::Entity.new(key => 2)
|
1008
|
+
]
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
it { expect(keys).to be == [0, 1, 2] }
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
describe 'with multiple entities including nil ids' do
|
1015
|
+
let(:entities) do
|
1016
|
+
[
|
1017
|
+
Spec::Entity.new(key => 0),
|
1018
|
+
Spec::Entity.new(key => nil),
|
1019
|
+
Spec::Entity.new(key => 1),
|
1020
|
+
Spec::Entity.new(key => nil),
|
1021
|
+
Spec::Entity.new(key => 2)
|
1022
|
+
]
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
it { expect(keys).to be == [0, 1, 2] }
|
1026
|
+
|
1027
|
+
describe 'with allow_nil: true' do
|
1028
|
+
let(:options) { super().merge(allow_nil: true) }
|
1029
|
+
|
1030
|
+
it { expect(keys).to be == [0, nil, 1, 2] }
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
describe 'with multiple entities including duplicate ids' do
|
1035
|
+
let(:entities) do
|
1036
|
+
[
|
1037
|
+
Spec::Entity.new(key => 0),
|
1038
|
+
Spec::Entity.new(key => 1),
|
1039
|
+
Spec::Entity.new(key => 0),
|
1040
|
+
Spec::Entity.new(key => 1),
|
1041
|
+
Spec::Entity.new(key => 2)
|
1042
|
+
]
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
it { expect(keys).to be == [0, 1, 2] }
|
1046
|
+
|
1047
|
+
describe 'with deduplicate: false' do
|
1048
|
+
let(:options) { super().merge(deduplicate: false) }
|
1049
|
+
|
1050
|
+
it { expect(keys).to be == [0, 1, 0, 1, 2] }
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
describe 'with multiple Integers' do
|
1055
|
+
let(:entities) { [0, 1, 2] }
|
1056
|
+
let(:error_message) do
|
1057
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
it 'should raise an exception' do
|
1061
|
+
expect { association.map_entities_to_keys(*entities) }
|
1062
|
+
.to raise_error ArgumentError, error_message
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
describe 'with strict: false' do
|
1066
|
+
it 'should raise an exception' do
|
1067
|
+
expect(
|
1068
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1069
|
+
)
|
1070
|
+
.to be == entities
|
1071
|
+
end
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
context 'when initialized with primary_key_type: String' do
|
1075
|
+
let(:constructor_options) do
|
1076
|
+
super().merge(primary_key_type: String)
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
describe 'with strict: false' do
|
1080
|
+
it 'should raise an exception' do
|
1081
|
+
expect do
|
1082
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1083
|
+
end
|
1084
|
+
.to raise_error ArgumentError, error_message
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
end
|
1089
|
+
|
1090
|
+
describe 'with multiple Strings' do
|
1091
|
+
let(:entities) { %w[0 1 2] }
|
1092
|
+
let(:error_message) do
|
1093
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
it 'should raise an exception' do
|
1097
|
+
expect { association.map_entities_to_keys(*entities) }
|
1098
|
+
.to raise_error ArgumentError, error_message
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
describe 'with strict: false' do
|
1102
|
+
it 'should raise an exception' do
|
1103
|
+
expect do
|
1104
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1105
|
+
end
|
1106
|
+
.to raise_error ArgumentError, error_message
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
context 'when initialized with primary_key_type: String' do
|
1111
|
+
let(:constructor_options) do
|
1112
|
+
super().merge(primary_key_type: String)
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
describe 'with strict: false' do
|
1116
|
+
it 'should raise an exception' do
|
1117
|
+
expect(
|
1118
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1119
|
+
)
|
1120
|
+
.to be == entities
|
1121
|
+
end
|
1122
|
+
end
|
1123
|
+
end
|
1124
|
+
end
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
describe '#primary_key_query?' do
|
1128
|
+
it { expect(subject.primary_key_query?).to be true }
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
describe '#query_key_name' do
|
1132
|
+
it { expect(subject.query_key_name).to be == 'id' }
|
1133
|
+
|
1134
|
+
context 'when initialized with primary_key_name: a String' do
|
1135
|
+
let(:primary_key_name) { 'uuid' }
|
1136
|
+
let(:constructor_options) do
|
1137
|
+
super().merge(primary_key_name: primary_key_name)
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
it { expect(subject.query_key_name).to be == primary_key_name }
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
context 'when initialized with primary_key_name: a Symbol' do
|
1144
|
+
let(:primary_key_name) { :uuid }
|
1145
|
+
let(:constructor_options) do
|
1146
|
+
super().merge(primary_key_name: primary_key_name)
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
it 'should set the primary key name' do
|
1150
|
+
expect(subject.query_key_name).to be == primary_key_name.to_s
|
1151
|
+
end
|
1152
|
+
end
|
1153
|
+
end
|
1154
|
+
end
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
# Contract validating the behavior of a HasAssociation.
|
1158
|
+
module ShouldBeAHasAssociationContract
|
1159
|
+
extend RSpec::SleepingKingStudios::Contract
|
1160
|
+
|
1161
|
+
# @!method apply(example_group)
|
1162
|
+
# Adds the contract to the example group.
|
1163
|
+
#
|
1164
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
1165
|
+
# which the contract is applied.
|
1166
|
+
contract do
|
1167
|
+
include Cuprum::Collections::RSpec::Contracts::RelationContracts
|
1168
|
+
|
1169
|
+
include_contract 'should be an association'
|
1170
|
+
|
1171
|
+
describe '#build_entities_query' do
|
1172
|
+
let(:key) { subject.primary_key_name }
|
1173
|
+
let(:entities) { [] }
|
1174
|
+
let(:options) { {} }
|
1175
|
+
let(:query) do
|
1176
|
+
association.build_entities_query(*entities, **options)
|
1177
|
+
end
|
1178
|
+
let(:evaluated) do
|
1179
|
+
Spec::QueryBuilder.new.instance_exec(&query)
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
example_class 'Spec::Entity' do |klass|
|
1183
|
+
klass.define_method(:initialize) do |**attributes|
|
1184
|
+
attributes.each do |key, value|
|
1185
|
+
instance_variable_set(:"@#{key}", value)
|
1186
|
+
end
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
klass.attr_reader :id
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
example_class 'Spec::QueryBuilder' do |klass|
|
1193
|
+
klass.define_method(:one_of) { |values| { 'one_of' => values } }
|
1194
|
+
end
|
1195
|
+
|
1196
|
+
context 'when the foreign key name is blank' do
|
1197
|
+
let(:error_message) do
|
1198
|
+
"foreign key name can't be blank"
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
it 'should raise an exception' do
|
1202
|
+
expect { association.build_entities_query(*entities) }
|
1203
|
+
.to raise_error ArgumentError, error_message
|
1204
|
+
end
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
context 'when initialized with foreign_key_name: value' do
|
1208
|
+
let(:foreign_key_name) { 'author_id' }
|
1209
|
+
let(:constructor_options) do
|
1210
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
describe 'with no entities' do
|
1214
|
+
let(:entities) { [] }
|
1215
|
+
|
1216
|
+
it { expect(query).to be_a Proc }
|
1217
|
+
|
1218
|
+
it { expect(evaluated).to be == {} }
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
describe 'with one nil entity' do
|
1222
|
+
let(:entities) { [nil] }
|
1223
|
+
|
1224
|
+
it { expect(evaluated).to be == {} }
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
describe 'with one invalid entity' do
|
1228
|
+
let(:entities) { [Object.new.freeze] }
|
1229
|
+
let(:error_message) do
|
1230
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
it 'should raise an exception' do
|
1234
|
+
expect { association.build_entities_query(*entities) }
|
1235
|
+
.to raise_error ArgumentError, error_message
|
1236
|
+
end
|
1237
|
+
end
|
1238
|
+
|
1239
|
+
describe 'with one entity that responds to #[] and key: nil' do
|
1240
|
+
let(:entities) { [{ key => nil }] }
|
1241
|
+
|
1242
|
+
it { expect(evaluated).to be == {} }
|
1243
|
+
|
1244
|
+
describe 'with allow_nil: true' do
|
1245
|
+
let(:options) { super().merge(allow_nil: true) }
|
1246
|
+
|
1247
|
+
it { expect(evaluated).to be == { 'author_id' => nil } }
|
1248
|
+
end
|
1249
|
+
end
|
1250
|
+
|
1251
|
+
describe 'with one entity that responds to #[] and key: value' do
|
1252
|
+
let(:entities) { [{ key => 0 }] }
|
1253
|
+
|
1254
|
+
it { expect(evaluated).to be == { 'author_id' => 0 } }
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
describe 'with one entity that responds to #id and key: nil' do
|
1258
|
+
let(:entities) { [Spec::Entity.new(key => nil)] }
|
1259
|
+
|
1260
|
+
it { expect(evaluated).to be == {} }
|
1261
|
+
|
1262
|
+
describe 'with allow_nil: true' do
|
1263
|
+
let(:options) { super().merge(allow_nil: true) }
|
1264
|
+
|
1265
|
+
it { expect(evaluated).to be == { 'author_id' => nil } }
|
1266
|
+
end
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
describe 'with one entity that responds to #id and key: value' do
|
1270
|
+
let(:entities) { [Spec::Entity.new(key => 0)] }
|
1271
|
+
|
1272
|
+
it { expect(evaluated).to be == { 'author_id' => 0 } }
|
1273
|
+
end
|
1274
|
+
|
1275
|
+
describe 'with multiple entities' do
|
1276
|
+
let(:entities) do
|
1277
|
+
[
|
1278
|
+
Spec::Entity.new(key => 0),
|
1279
|
+
Spec::Entity.new(key => 1),
|
1280
|
+
Spec::Entity.new(key => 2)
|
1281
|
+
]
|
1282
|
+
end
|
1283
|
+
let(:expected) do
|
1284
|
+
{ 'author_id' => { 'one_of' => [0, 1, 2] } }
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
it { expect(evaluated).to be == expected }
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
describe 'with multiple entities including nil' do
|
1291
|
+
let(:entities) do
|
1292
|
+
[
|
1293
|
+
Spec::Entity.new(key => 0),
|
1294
|
+
nil,
|
1295
|
+
Spec::Entity.new(key => 1),
|
1296
|
+
nil,
|
1297
|
+
Spec::Entity.new(key => 2)
|
1298
|
+
]
|
1299
|
+
end
|
1300
|
+
let(:expected) do
|
1301
|
+
{ 'author_id' => { 'one_of' => [0, 1, 2] } }
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
it { expect(evaluated).to be == expected }
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
describe 'with multiple entities including nil ids' do
|
1308
|
+
let(:entities) do
|
1309
|
+
[
|
1310
|
+
Spec::Entity.new(key => 0),
|
1311
|
+
Spec::Entity.new(key => nil),
|
1312
|
+
Spec::Entity.new(key => 1),
|
1313
|
+
Spec::Entity.new(key => nil),
|
1314
|
+
Spec::Entity.new(key => 2)
|
1315
|
+
]
|
1316
|
+
end
|
1317
|
+
let(:expected) do
|
1318
|
+
{ 'author_id' => { 'one_of' => [0, 1, 2] } }
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
it { expect(evaluated).to be == expected }
|
1322
|
+
|
1323
|
+
describe 'with allow_nil: true' do
|
1324
|
+
let(:options) { super().merge(allow_nil: true) }
|
1325
|
+
let(:expected) do
|
1326
|
+
{ 'author_id' => { 'one_of' => [0, nil, 1, 2] } }
|
1327
|
+
end
|
1328
|
+
|
1329
|
+
it { expect(evaluated).to be == expected }
|
1330
|
+
end
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
describe 'with multiple entities including duplicate ids' do
|
1334
|
+
let(:entities) do
|
1335
|
+
[
|
1336
|
+
Spec::Entity.new(key => 0),
|
1337
|
+
Spec::Entity.new(key => 1),
|
1338
|
+
Spec::Entity.new(key => 0),
|
1339
|
+
Spec::Entity.new(key => 1),
|
1340
|
+
Spec::Entity.new(key => 2)
|
1341
|
+
]
|
1342
|
+
end
|
1343
|
+
let(:expected) do
|
1344
|
+
{ 'author_id' => { 'one_of' => [0, 1, 2] } }
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
it { expect(evaluated).to be == expected }
|
1348
|
+
|
1349
|
+
describe 'with deduplicate: false' do
|
1350
|
+
let(:options) { super().merge(deduplicate: false) }
|
1351
|
+
let(:expected) do
|
1352
|
+
{ 'author_id' => { 'one_of' => [0, 1, 0, 1, 2] } }
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
it { expect(evaluated).to be == expected }
|
1356
|
+
end
|
1357
|
+
end
|
1358
|
+
end
|
1359
|
+
end
|
1360
|
+
|
1361
|
+
describe '#build_keys_query' do
|
1362
|
+
let(:keys) { [] }
|
1363
|
+
let(:options) { {} }
|
1364
|
+
let(:query) do
|
1365
|
+
association.build_keys_query(*keys, **options)
|
1366
|
+
end
|
1367
|
+
let(:evaluated) do
|
1368
|
+
Spec::QueryBuilder.new.instance_exec(&query)
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
example_class 'Spec::QueryBuilder' do |klass|
|
1372
|
+
klass.define_method(:one_of) { |values| { 'one_of' => values } }
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
context 'when the foreign key name is blank' do
|
1376
|
+
let(:error_message) do
|
1377
|
+
"foreign key name can't be blank"
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
it 'should raise an exception' do
|
1381
|
+
expect { association.build_keys_query(*keys) }
|
1382
|
+
.to raise_error ArgumentError, error_message
|
1383
|
+
end
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
context 'when initialized with foreign_key_name: value' do
|
1387
|
+
let(:foreign_key_name) { 'author_id' }
|
1388
|
+
let(:constructor_options) do
|
1389
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1390
|
+
end
|
1391
|
+
|
1392
|
+
describe 'with no keys' do
|
1393
|
+
let(:keys) { [] }
|
1394
|
+
|
1395
|
+
it { expect(query).to be_a Proc }
|
1396
|
+
|
1397
|
+
it { expect(evaluated).to be == {} }
|
1398
|
+
end
|
1399
|
+
|
1400
|
+
describe 'with one nil key' do
|
1401
|
+
let(:keys) { [nil] }
|
1402
|
+
|
1403
|
+
it { expect(evaluated).to be == {} }
|
1404
|
+
|
1405
|
+
describe 'with allow_nil: true' do
|
1406
|
+
let(:options) { { allow_nil: true } }
|
1407
|
+
|
1408
|
+
it { expect(evaluated).to be == { 'author_id' => nil } }
|
1409
|
+
end
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
describe 'with one non-nil key' do
|
1413
|
+
let(:keys) { [0] }
|
1414
|
+
|
1415
|
+
it { expect(evaluated).to be == { 'author_id' => 0 } }
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
describe 'with many keys' do
|
1419
|
+
let(:keys) { [0, 1, 2] }
|
1420
|
+
let(:expected) { { 'author_id' => { 'one_of' => keys } } }
|
1421
|
+
|
1422
|
+
it { expect(evaluated).to be == expected }
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
describe 'with many keys including nil' do
|
1426
|
+
let(:keys) { [0, nil, 2] }
|
1427
|
+
let(:expected) { { 'author_id' => { 'one_of' => [0, 2] } } }
|
1428
|
+
|
1429
|
+
it { expect(evaluated).to be == expected }
|
1430
|
+
|
1431
|
+
describe 'with allow_nil: true' do
|
1432
|
+
let(:options) { { allow_nil: true } }
|
1433
|
+
let(:expected) do
|
1434
|
+
{ 'author_id' => { 'one_of' => [0, nil, 2] } }
|
1435
|
+
end
|
1436
|
+
|
1437
|
+
it { expect(evaluated).to be == expected }
|
1438
|
+
end
|
1439
|
+
end
|
1440
|
+
|
1441
|
+
describe 'with many non-unique keys' do
|
1442
|
+
let(:keys) { [0, 1, 2, 1, 2] }
|
1443
|
+
let(:expected) { { 'author_id' => { 'one_of' => keys.uniq } } }
|
1444
|
+
|
1445
|
+
it { expect(evaluated).to be == expected }
|
1446
|
+
|
1447
|
+
describe 'with deduplicate: false' do
|
1448
|
+
let(:options) { super().merge(deduplicate: false) }
|
1449
|
+
let(:expected) do
|
1450
|
+
{ 'author_id' => { 'one_of' => [0, 1, 2, 1, 2] } }
|
1451
|
+
end
|
1452
|
+
|
1453
|
+
it { expect(evaluated).to be == expected }
|
1454
|
+
end
|
1455
|
+
end
|
1456
|
+
end
|
1457
|
+
end
|
1458
|
+
|
1459
|
+
describe '#foreign_key_name' do
|
1460
|
+
it { expect(subject.foreign_key_name).to be nil }
|
1461
|
+
|
1462
|
+
context 'when initialized with foreign_key_name: a String' do
|
1463
|
+
let(:foreign_key_name) { 'writer_id' }
|
1464
|
+
let(:constructor_options) do
|
1465
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1466
|
+
end
|
1467
|
+
|
1468
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1469
|
+
end
|
1470
|
+
|
1471
|
+
context 'when initialized with foreign_key_name: a String' do
|
1472
|
+
let(:foreign_key_name) { :writer_id }
|
1473
|
+
let(:constructor_options) do
|
1474
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1475
|
+
end
|
1476
|
+
|
1477
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1478
|
+
end
|
1479
|
+
|
1480
|
+
context 'when initialized with inverse: value' do
|
1481
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
1482
|
+
let(:constructor_options) do
|
1483
|
+
super().merge(inverse: inverse)
|
1484
|
+
end
|
1485
|
+
|
1486
|
+
it { expect(subject.foreign_key_name).to be == 'author_id' }
|
1487
|
+
|
1488
|
+
context 'when initialized with foreign_key_name: a String' do
|
1489
|
+
let(:foreign_key_name) { 'writer_id' }
|
1490
|
+
let(:constructor_options) do
|
1491
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1492
|
+
end
|
1493
|
+
|
1494
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
context 'when initialized with foreign_key_name: a String' do
|
1498
|
+
let(:foreign_key_name) { :writer_id }
|
1499
|
+
let(:constructor_options) do
|
1500
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1501
|
+
end
|
1502
|
+
|
1503
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1504
|
+
end
|
1505
|
+
|
1506
|
+
context 'when initialized with inverse_name: value' do
|
1507
|
+
let(:inverse_name) { 'writers' }
|
1508
|
+
let(:constructor_options) do
|
1509
|
+
super().merge(inverse_name: inverse_name)
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
it { expect(subject.foreign_key_name).to be == 'author_id' }
|
1513
|
+
end
|
1514
|
+
|
1515
|
+
context 'when initialized with singular_inverse_name: value' do
|
1516
|
+
let(:singular_inverse_name) { 'writer' }
|
1517
|
+
let(:constructor_options) do
|
1518
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
1519
|
+
end
|
1520
|
+
|
1521
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1522
|
+
end
|
1523
|
+
end
|
1524
|
+
|
1525
|
+
context 'when initialized with inverse_name: value' do
|
1526
|
+
let(:inverse_name) { 'authors' }
|
1527
|
+
let(:constructor_options) do
|
1528
|
+
super().merge(inverse_name: inverse_name)
|
1529
|
+
end
|
1530
|
+
|
1531
|
+
it { expect(subject.foreign_key_name).to be == 'author_id' }
|
1532
|
+
|
1533
|
+
context 'when initialized with foreign_key_name: a String' do
|
1534
|
+
let(:foreign_key_name) { 'writer_id' }
|
1535
|
+
let(:constructor_options) do
|
1536
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1540
|
+
end
|
1541
|
+
|
1542
|
+
context 'when initialized with foreign_key_name: a String' do
|
1543
|
+
let(:foreign_key_name) { :writer_id }
|
1544
|
+
let(:constructor_options) do
|
1545
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1546
|
+
end
|
1547
|
+
|
1548
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1549
|
+
end
|
1550
|
+
end
|
1551
|
+
|
1552
|
+
context 'when initialized with singular_inverse_name: value' do
|
1553
|
+
let(:singular_inverse_name) { 'author' }
|
1554
|
+
let(:constructor_options) do
|
1555
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
1556
|
+
end
|
1557
|
+
|
1558
|
+
it { expect(subject.foreign_key_name).to be == 'author_id' }
|
1559
|
+
|
1560
|
+
context 'when initialized with foreign_key_name: a String' do
|
1561
|
+
let(:foreign_key_name) { 'writer_id' }
|
1562
|
+
let(:constructor_options) do
|
1563
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1564
|
+
end
|
1565
|
+
|
1566
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1567
|
+
end
|
1568
|
+
|
1569
|
+
context 'when initialized with foreign_key_name: a String' do
|
1570
|
+
let(:foreign_key_name) { :writer_id }
|
1571
|
+
let(:constructor_options) do
|
1572
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1573
|
+
end
|
1574
|
+
|
1575
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1576
|
+
end
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
context 'with a copy with assigned inverse' do
|
1580
|
+
subject do
|
1581
|
+
super().tap(&:foreign_key_name).with_inverse(new_inverse)
|
1582
|
+
end
|
1583
|
+
|
1584
|
+
let(:new_inverse) { described_class.new(name: 'chapters') }
|
1585
|
+
|
1586
|
+
it { expect(subject.foreign_key_name).to be == 'chapter_id' }
|
1587
|
+
|
1588
|
+
context 'when initialized with foreign_key_name: a String' do
|
1589
|
+
let(:foreign_key_name) { 'writer_id' }
|
1590
|
+
let(:constructor_options) do
|
1591
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1592
|
+
end
|
1593
|
+
|
1594
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1595
|
+
end
|
1596
|
+
|
1597
|
+
context 'when initialized with foreign_key_name: a String' do
|
1598
|
+
let(:foreign_key_name) { :writer_id }
|
1599
|
+
let(:constructor_options) do
|
1600
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1601
|
+
end
|
1602
|
+
|
1603
|
+
it { expect(subject.foreign_key_name).to be == 'writer_id' }
|
1604
|
+
end
|
1605
|
+
|
1606
|
+
context 'when initialized with inverse: value' do
|
1607
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
1608
|
+
let(:constructor_options) do
|
1609
|
+
super().merge(inverse: inverse)
|
1610
|
+
end
|
1611
|
+
|
1612
|
+
it { expect(subject.foreign_key_name).to be == 'chapter_id' }
|
1613
|
+
end
|
1614
|
+
|
1615
|
+
context 'when initialized with inverse_name: value' do
|
1616
|
+
let(:inverse_name) { 'authors' }
|
1617
|
+
let(:constructor_options) do
|
1618
|
+
super().merge(inverse_name: inverse_name)
|
1619
|
+
end
|
1620
|
+
|
1621
|
+
it { expect(subject.foreign_key_name).to be == 'chapter_id' }
|
1622
|
+
end
|
1623
|
+
|
1624
|
+
context 'when initialized with singular_inverse_name: value' do
|
1625
|
+
let(:singular_inverse_name) { 'author' }
|
1626
|
+
let(:constructor_options) do
|
1627
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
1628
|
+
end
|
1629
|
+
|
1630
|
+
it { expect(subject.foreign_key_name).to be == 'author_id' }
|
1631
|
+
end
|
1632
|
+
end
|
1633
|
+
end
|
1634
|
+
|
1635
|
+
describe '#inverse_key_name' do
|
1636
|
+
it { expect(subject.inverse_key_name).to be == 'id' }
|
1637
|
+
|
1638
|
+
context 'when initialized with primary_key_name: a String' do
|
1639
|
+
let(:primary_key_name) { 'uuid' }
|
1640
|
+
let(:constructor_options) do
|
1641
|
+
super().merge(primary_key_name: primary_key_name)
|
1642
|
+
end
|
1643
|
+
|
1644
|
+
it { expect(subject.inverse_key_name).to be == primary_key_name }
|
1645
|
+
end
|
1646
|
+
|
1647
|
+
context 'when initialized with primary_key_name: a Symbol' do
|
1648
|
+
let(:primary_key_name) { :uuid }
|
1649
|
+
let(:constructor_options) do
|
1650
|
+
super().merge(primary_key_name: primary_key_name)
|
1651
|
+
end
|
1652
|
+
|
1653
|
+
it 'should set the primary key name' do
|
1654
|
+
expect(subject.inverse_key_name).to be == primary_key_name.to_s
|
1655
|
+
end
|
1656
|
+
end
|
1657
|
+
end
|
1658
|
+
|
1659
|
+
describe '#map_entities_to_keys' do
|
1660
|
+
let(:key) { subject.primary_key_name }
|
1661
|
+
let(:entities) { [] }
|
1662
|
+
let(:options) { {} }
|
1663
|
+
let(:keys) { subject.map_entities_to_keys(*entities, **options) }
|
1664
|
+
|
1665
|
+
example_class 'Spec::Entity' do |klass|
|
1666
|
+
klass.define_method(:initialize) do |**attributes|
|
1667
|
+
attributes.each do |key, value|
|
1668
|
+
instance_variable_set(:"@#{key}", value)
|
1669
|
+
end
|
1670
|
+
end
|
1671
|
+
|
1672
|
+
klass.attr_reader :id
|
1673
|
+
end
|
1674
|
+
|
1675
|
+
describe 'with no entities' do
|
1676
|
+
let(:entities) { [] }
|
1677
|
+
|
1678
|
+
it { expect(keys).to be == [] }
|
1679
|
+
end
|
1680
|
+
|
1681
|
+
describe 'with one nil entity' do
|
1682
|
+
let(:entities) { [nil] }
|
1683
|
+
|
1684
|
+
it { expect(keys).to be == [] }
|
1685
|
+
end
|
1686
|
+
|
1687
|
+
describe 'with one invalid entity' do
|
1688
|
+
let(:entities) { [Object.new.freeze] }
|
1689
|
+
let(:error_message) do
|
1690
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
1691
|
+
end
|
1692
|
+
|
1693
|
+
it 'should raise an exception' do
|
1694
|
+
expect { association.map_entities_to_keys(*entities) }
|
1695
|
+
.to raise_error ArgumentError, error_message
|
1696
|
+
end
|
1697
|
+
end
|
1698
|
+
|
1699
|
+
describe 'with one Integer' do
|
1700
|
+
let(:entities) { [0] }
|
1701
|
+
let(:error_message) do
|
1702
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
1703
|
+
end
|
1704
|
+
|
1705
|
+
it 'should raise an exception' do
|
1706
|
+
expect { association.map_entities_to_keys(*entities) }
|
1707
|
+
.to raise_error ArgumentError, error_message
|
1708
|
+
end
|
1709
|
+
|
1710
|
+
describe 'with strict: false' do
|
1711
|
+
it 'should raise an exception' do
|
1712
|
+
expect(
|
1713
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1714
|
+
)
|
1715
|
+
.to be == entities
|
1716
|
+
end
|
1717
|
+
end
|
1718
|
+
|
1719
|
+
context 'when initialized with primary_key_type: String' do
|
1720
|
+
let(:constructor_options) do
|
1721
|
+
super().merge(primary_key_type: String)
|
1722
|
+
end
|
1723
|
+
|
1724
|
+
describe 'with strict: false' do
|
1725
|
+
it 'should raise an exception' do
|
1726
|
+
expect do
|
1727
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1728
|
+
end
|
1729
|
+
.to raise_error ArgumentError, error_message
|
1730
|
+
end
|
1731
|
+
end
|
1732
|
+
end
|
1733
|
+
end
|
1734
|
+
|
1735
|
+
describe 'with one String' do
|
1736
|
+
let(:entities) { %w[0] }
|
1737
|
+
let(:error_message) do
|
1738
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
1739
|
+
end
|
1740
|
+
|
1741
|
+
it 'should raise an exception' do
|
1742
|
+
expect { association.map_entities_to_keys(*entities) }
|
1743
|
+
.to raise_error ArgumentError, error_message
|
1744
|
+
end
|
1745
|
+
|
1746
|
+
describe 'with strict: false' do
|
1747
|
+
it 'should raise an exception' do
|
1748
|
+
expect do
|
1749
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1750
|
+
end
|
1751
|
+
.to raise_error ArgumentError, error_message
|
1752
|
+
end
|
1753
|
+
end
|
1754
|
+
|
1755
|
+
context 'when initialized with primary_key_type: String' do
|
1756
|
+
let(:constructor_options) do
|
1757
|
+
super().merge(primary_key_type: String)
|
1758
|
+
end
|
1759
|
+
|
1760
|
+
describe 'with strict: false' do
|
1761
|
+
it 'should raise an exception' do
|
1762
|
+
expect(
|
1763
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1764
|
+
)
|
1765
|
+
.to be == entities
|
1766
|
+
end
|
1767
|
+
end
|
1768
|
+
end
|
1769
|
+
end
|
1770
|
+
|
1771
|
+
describe 'with one entity that responds to #[] and key: nil' do
|
1772
|
+
let(:entities) { [{ key => nil }] }
|
1773
|
+
|
1774
|
+
it { expect(keys).to be == [] }
|
1775
|
+
|
1776
|
+
describe 'with allow_nil: true' do
|
1777
|
+
let(:options) { super().merge(allow_nil: true) }
|
1778
|
+
|
1779
|
+
it { expect(keys).to be == [nil] }
|
1780
|
+
end
|
1781
|
+
end
|
1782
|
+
|
1783
|
+
describe 'with one entity that responds to #[] and key: value' do
|
1784
|
+
let(:entities) { [{ key => 0 }] }
|
1785
|
+
|
1786
|
+
it { expect(keys).to be == [0] }
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
describe 'with one entity that responds to #id and key: nil' do
|
1790
|
+
let(:entities) { [Spec::Entity.new(key => nil)] }
|
1791
|
+
|
1792
|
+
it { expect(keys).to be == [] }
|
1793
|
+
|
1794
|
+
describe 'with allow_nil: true' do
|
1795
|
+
let(:options) { super().merge(allow_nil: true) }
|
1796
|
+
|
1797
|
+
it { expect(keys).to be == [nil] }
|
1798
|
+
end
|
1799
|
+
end
|
1800
|
+
|
1801
|
+
describe 'with one entity that responds to #id and key: value' do
|
1802
|
+
let(:entities) { [Spec::Entity.new(key => 0)] }
|
1803
|
+
|
1804
|
+
it { expect(keys).to be == [0] }
|
1805
|
+
end
|
1806
|
+
|
1807
|
+
describe 'with multiple entities' do
|
1808
|
+
let(:entities) do
|
1809
|
+
[
|
1810
|
+
Spec::Entity.new(key => 0),
|
1811
|
+
Spec::Entity.new(key => 1),
|
1812
|
+
Spec::Entity.new(key => 2)
|
1813
|
+
]
|
1814
|
+
end
|
1815
|
+
|
1816
|
+
it { expect(keys).to be == [0, 1, 2] }
|
1817
|
+
end
|
1818
|
+
|
1819
|
+
describe 'with multiple entities including nil' do
|
1820
|
+
let(:entities) do
|
1821
|
+
[
|
1822
|
+
Spec::Entity.new(key => 0),
|
1823
|
+
nil,
|
1824
|
+
Spec::Entity.new(key => 1),
|
1825
|
+
nil,
|
1826
|
+
Spec::Entity.new(key => 2)
|
1827
|
+
]
|
1828
|
+
end
|
1829
|
+
|
1830
|
+
it { expect(keys).to be == [0, 1, 2] }
|
1831
|
+
end
|
1832
|
+
|
1833
|
+
describe 'with multiple entities including nil ids' do
|
1834
|
+
let(:entities) do
|
1835
|
+
[
|
1836
|
+
Spec::Entity.new(key => 0),
|
1837
|
+
Spec::Entity.new(key => nil),
|
1838
|
+
Spec::Entity.new(key => 1),
|
1839
|
+
Spec::Entity.new(key => nil),
|
1840
|
+
Spec::Entity.new(key => 2)
|
1841
|
+
]
|
1842
|
+
end
|
1843
|
+
|
1844
|
+
it { expect(keys).to be == [0, 1, 2] }
|
1845
|
+
|
1846
|
+
describe 'with allow_nil: true' do
|
1847
|
+
let(:options) { super().merge(allow_nil: true) }
|
1848
|
+
|
1849
|
+
it { expect(keys).to be == [0, nil, 1, 2] }
|
1850
|
+
end
|
1851
|
+
end
|
1852
|
+
|
1853
|
+
describe 'with multiple entities including duplicate ids' do
|
1854
|
+
let(:entities) do
|
1855
|
+
[
|
1856
|
+
Spec::Entity.new(key => 0),
|
1857
|
+
Spec::Entity.new(key => 1),
|
1858
|
+
Spec::Entity.new(key => 0),
|
1859
|
+
Spec::Entity.new(key => 1),
|
1860
|
+
Spec::Entity.new(key => 2)
|
1861
|
+
]
|
1862
|
+
end
|
1863
|
+
|
1864
|
+
it { expect(keys).to be == [0, 1, 2] }
|
1865
|
+
|
1866
|
+
describe 'with deduplicate: false' do
|
1867
|
+
let(:options) { super().merge(deduplicate: false) }
|
1868
|
+
|
1869
|
+
it { expect(keys).to be == [0, 1, 0, 1, 2] }
|
1870
|
+
end
|
1871
|
+
end
|
1872
|
+
|
1873
|
+
describe 'with multiple Integers' do
|
1874
|
+
let(:entities) { [0, 1, 2] }
|
1875
|
+
let(:error_message) do
|
1876
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
1877
|
+
end
|
1878
|
+
|
1879
|
+
it 'should raise an exception' do
|
1880
|
+
expect { association.map_entities_to_keys(*entities) }
|
1881
|
+
.to raise_error ArgumentError, error_message
|
1882
|
+
end
|
1883
|
+
|
1884
|
+
describe 'with strict: false' do
|
1885
|
+
it 'should raise an exception' do
|
1886
|
+
expect(
|
1887
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1888
|
+
)
|
1889
|
+
.to be == entities
|
1890
|
+
end
|
1891
|
+
end
|
1892
|
+
|
1893
|
+
context 'when initialized with primary_key_type: String' do
|
1894
|
+
let(:constructor_options) do
|
1895
|
+
super().merge(primary_key_type: String)
|
1896
|
+
end
|
1897
|
+
|
1898
|
+
describe 'with strict: false' do
|
1899
|
+
it 'should raise an exception' do
|
1900
|
+
expect do
|
1901
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1902
|
+
end
|
1903
|
+
.to raise_error ArgumentError, error_message
|
1904
|
+
end
|
1905
|
+
end
|
1906
|
+
end
|
1907
|
+
end
|
1908
|
+
|
1909
|
+
describe 'with multiple Strings' do
|
1910
|
+
let(:entities) { %w[0 1 2] }
|
1911
|
+
let(:error_message) do
|
1912
|
+
"undefined method :[] or :#{key} for #{entities.first.inspect}"
|
1913
|
+
end
|
1914
|
+
|
1915
|
+
it 'should raise an exception' do
|
1916
|
+
expect { association.map_entities_to_keys(*entities) }
|
1917
|
+
.to raise_error ArgumentError, error_message
|
1918
|
+
end
|
1919
|
+
|
1920
|
+
describe 'with strict: false' do
|
1921
|
+
it 'should raise an exception' do
|
1922
|
+
expect do
|
1923
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1924
|
+
end
|
1925
|
+
.to raise_error ArgumentError, error_message
|
1926
|
+
end
|
1927
|
+
end
|
1928
|
+
|
1929
|
+
context 'when initialized with primary_key_type: String' do
|
1930
|
+
let(:constructor_options) do
|
1931
|
+
super().merge(primary_key_type: String)
|
1932
|
+
end
|
1933
|
+
|
1934
|
+
describe 'with strict: false' do
|
1935
|
+
it 'should raise an exception' do
|
1936
|
+
expect(
|
1937
|
+
association.map_entities_to_keys(*entities, strict: false)
|
1938
|
+
)
|
1939
|
+
.to be == entities
|
1940
|
+
end
|
1941
|
+
end
|
1942
|
+
end
|
1943
|
+
end
|
1944
|
+
end
|
1945
|
+
|
1946
|
+
describe '#primary_key_query?' do
|
1947
|
+
it { expect(subject.primary_key_query?).to be false }
|
1948
|
+
end
|
1949
|
+
|
1950
|
+
describe '#query_key_name' do
|
1951
|
+
context 'when the foreign key name is blank' do
|
1952
|
+
let(:error_message) do
|
1953
|
+
"foreign key name can't be blank"
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
it 'should raise an exception' do
|
1957
|
+
expect { association.query_key_name }
|
1958
|
+
.to raise_error ArgumentError, error_message
|
1959
|
+
end
|
1960
|
+
end
|
1961
|
+
|
1962
|
+
context 'when initialized with foreign_key_name: a String' do
|
1963
|
+
let(:foreign_key_name) { 'writer_id' }
|
1964
|
+
let(:constructor_options) do
|
1965
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1966
|
+
end
|
1967
|
+
|
1968
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
1969
|
+
end
|
1970
|
+
|
1971
|
+
context 'when initialized with foreign_key_name: a String' do
|
1972
|
+
let(:foreign_key_name) { :writer_id }
|
1973
|
+
let(:constructor_options) do
|
1974
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1975
|
+
end
|
1976
|
+
|
1977
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
1978
|
+
end
|
1979
|
+
|
1980
|
+
context 'when initialized with inverse: value' do
|
1981
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
1982
|
+
let(:constructor_options) do
|
1983
|
+
super().merge(inverse: inverse)
|
1984
|
+
end
|
1985
|
+
|
1986
|
+
it { expect(subject.query_key_name).to be == 'author_id' }
|
1987
|
+
|
1988
|
+
context 'when initialized with foreign_key_name: a String' do
|
1989
|
+
let(:foreign_key_name) { 'writer_id' }
|
1990
|
+
let(:constructor_options) do
|
1991
|
+
super().merge(foreign_key_name: foreign_key_name)
|
1992
|
+
end
|
1993
|
+
|
1994
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
1995
|
+
end
|
1996
|
+
|
1997
|
+
context 'when initialized with foreign_key_name: a String' do
|
1998
|
+
let(:foreign_key_name) { :writer_id }
|
1999
|
+
let(:constructor_options) do
|
2000
|
+
super().merge(foreign_key_name: foreign_key_name)
|
2001
|
+
end
|
2002
|
+
|
2003
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
2004
|
+
end
|
2005
|
+
|
2006
|
+
context 'when initialized with inverse_name: value' do
|
2007
|
+
let(:inverse_name) { 'writers' }
|
2008
|
+
let(:constructor_options) do
|
2009
|
+
super().merge(inverse_name: inverse_name)
|
2010
|
+
end
|
2011
|
+
|
2012
|
+
it { expect(subject.query_key_name).to be == 'author_id' }
|
2013
|
+
end
|
2014
|
+
|
2015
|
+
context 'when initialized with singular_inverse_name: value' do
|
2016
|
+
let(:singular_inverse_name) { 'writer' }
|
2017
|
+
let(:constructor_options) do
|
2018
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
2019
|
+
end
|
2020
|
+
|
2021
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
2022
|
+
end
|
2023
|
+
end
|
2024
|
+
|
2025
|
+
context 'when initialized with inverse_name: value' do
|
2026
|
+
let(:inverse_name) { 'authors' }
|
2027
|
+
let(:constructor_options) do
|
2028
|
+
super().merge(inverse_name: inverse_name)
|
2029
|
+
end
|
2030
|
+
|
2031
|
+
it { expect(subject.query_key_name).to be == 'author_id' }
|
2032
|
+
|
2033
|
+
context 'when initialized with foreign_key_name: a String' do
|
2034
|
+
let(:foreign_key_name) { 'writer_id' }
|
2035
|
+
let(:constructor_options) do
|
2036
|
+
super().merge(foreign_key_name: foreign_key_name)
|
2037
|
+
end
|
2038
|
+
|
2039
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
2040
|
+
end
|
2041
|
+
|
2042
|
+
context 'when initialized with foreign_key_name: a String' do
|
2043
|
+
let(:foreign_key_name) { :writer_id }
|
2044
|
+
let(:constructor_options) do
|
2045
|
+
super().merge(foreign_key_name: foreign_key_name)
|
2046
|
+
end
|
2047
|
+
|
2048
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
2049
|
+
end
|
2050
|
+
end
|
2051
|
+
|
2052
|
+
context 'when initialized with singular_inverse_name: value' do
|
2053
|
+
let(:singular_inverse_name) { 'author' }
|
2054
|
+
let(:constructor_options) do
|
2055
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
2056
|
+
end
|
2057
|
+
|
2058
|
+
it { expect(subject.query_key_name).to be == 'author_id' }
|
2059
|
+
|
2060
|
+
context 'when initialized with foreign_key_name: a String' do
|
2061
|
+
let(:foreign_key_name) { 'writer_id' }
|
2062
|
+
let(:constructor_options) do
|
2063
|
+
super().merge(foreign_key_name: foreign_key_name)
|
2064
|
+
end
|
2065
|
+
|
2066
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
2067
|
+
end
|
2068
|
+
|
2069
|
+
context 'when initialized with foreign_key_name: a String' do
|
2070
|
+
let(:foreign_key_name) { :writer_id }
|
2071
|
+
let(:constructor_options) do
|
2072
|
+
super().merge(foreign_key_name: foreign_key_name)
|
2073
|
+
end
|
2074
|
+
|
2075
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
2076
|
+
end
|
2077
|
+
end
|
2078
|
+
|
2079
|
+
context 'with a copy with assigned inverse' do
|
2080
|
+
subject do
|
2081
|
+
super().tap(&:foreign_key_name).with_inverse(new_inverse)
|
2082
|
+
end
|
2083
|
+
|
2084
|
+
let(:new_inverse) { described_class.new(name: 'chapters') }
|
2085
|
+
|
2086
|
+
it { expect(subject.query_key_name).to be == 'chapter_id' }
|
2087
|
+
|
2088
|
+
context 'when initialized with foreign_key_name: a String' do
|
2089
|
+
let(:foreign_key_name) { 'writer_id' }
|
2090
|
+
let(:constructor_options) do
|
2091
|
+
super().merge(foreign_key_name: foreign_key_name)
|
2092
|
+
end
|
2093
|
+
|
2094
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
2095
|
+
end
|
2096
|
+
|
2097
|
+
context 'when initialized with foreign_key_name: a String' do
|
2098
|
+
let(:foreign_key_name) { :writer_id }
|
2099
|
+
let(:constructor_options) do
|
2100
|
+
super().merge(foreign_key_name: foreign_key_name)
|
2101
|
+
end
|
2102
|
+
|
2103
|
+
it { expect(subject.query_key_name).to be == 'writer_id' }
|
2104
|
+
end
|
2105
|
+
|
2106
|
+
context 'when initialized with inverse: value' do
|
2107
|
+
let(:inverse) { described_class.new(name: 'authors') }
|
2108
|
+
let(:constructor_options) do
|
2109
|
+
super().merge(inverse: inverse)
|
2110
|
+
end
|
2111
|
+
|
2112
|
+
it { expect(subject.query_key_name).to be == 'chapter_id' }
|
2113
|
+
end
|
2114
|
+
|
2115
|
+
context 'when initialized with inverse_name: value' do
|
2116
|
+
let(:inverse_name) { 'authors' }
|
2117
|
+
let(:constructor_options) do
|
2118
|
+
super().merge(inverse_name: inverse_name)
|
2119
|
+
end
|
2120
|
+
|
2121
|
+
it { expect(subject.query_key_name).to be == 'chapter_id' }
|
2122
|
+
end
|
2123
|
+
|
2124
|
+
context 'when initialized with singular_inverse_name: value' do
|
2125
|
+
let(:singular_inverse_name) { 'author' }
|
2126
|
+
let(:constructor_options) do
|
2127
|
+
super().merge(singular_inverse_name: singular_inverse_name)
|
2128
|
+
end
|
2129
|
+
|
2130
|
+
it { expect(subject.query_key_name).to be == 'author_id' }
|
2131
|
+
end
|
2132
|
+
end
|
2133
|
+
end
|
2134
|
+
end
|
2135
|
+
end
|
2136
|
+
end
|
2137
|
+
end
|