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