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,338 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/deferred'
|
4
|
+
|
5
|
+
require 'cuprum/collections/rspec/deferred'
|
6
|
+
require 'cuprum/collections/rspec/deferred/relation_examples'
|
7
|
+
|
8
|
+
module Cuprum::Collections::RSpec::Deferred
|
9
|
+
# Deferred examples for testing collections.
|
10
|
+
module CollectionExamples
|
11
|
+
include RSpec::SleepingKingStudios::Deferred::Provider
|
12
|
+
include Cuprum::Collections::RSpec::Deferred::RelationExamples
|
13
|
+
|
14
|
+
deferred_examples 'should be a Collection' do |**options|
|
15
|
+
include Cuprum::Collections::RSpec::Deferred::RelationExamples
|
16
|
+
|
17
|
+
deferred_examples 'should define the command' \
|
18
|
+
do |command_name, command_class_name = nil|
|
19
|
+
next if options[:abstract]
|
20
|
+
|
21
|
+
tools = SleepingKingStudios::Tools::Toolbelt.instance
|
22
|
+
class_name = tools.str.camelize(command_name)
|
23
|
+
|
24
|
+
describe "##{command_name}" do
|
25
|
+
let(:constructor_options) { defined?(super()) ? super() : {} }
|
26
|
+
let(:command) { build_command(collection) }
|
27
|
+
let(:command_class) do
|
28
|
+
(
|
29
|
+
command_class_name ||
|
30
|
+
"#{options[:commands_namespace]}::#{class_name}"
|
31
|
+
)
|
32
|
+
.then { |str| Object.const_get(str) }
|
33
|
+
end
|
34
|
+
|
35
|
+
define_method(:build_command) do |collection|
|
36
|
+
collection.send(command_name, **constructor_options)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should define the command' do
|
40
|
+
expect(collection)
|
41
|
+
.to respond_to(command_name)
|
42
|
+
.with(0).arguments
|
43
|
+
.and_any_keywords
|
44
|
+
end
|
45
|
+
|
46
|
+
it { expect(command).to be_a command_class }
|
47
|
+
|
48
|
+
it { expect(command.collection).to be subject }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
include_deferred 'should be a Relation',
|
53
|
+
constructor: false,
|
54
|
+
default_entity_class: options[:default_entity_class]
|
55
|
+
|
56
|
+
include_deferred 'should define Relation primary key'
|
57
|
+
|
58
|
+
include_deferred 'should define Relation scope',
|
59
|
+
default_scope: options[:default_scope]
|
60
|
+
|
61
|
+
include_deferred 'should define the command', :assign_one
|
62
|
+
|
63
|
+
include_deferred 'should define the command', :build_one
|
64
|
+
|
65
|
+
include_deferred 'should define the command', :destroy_one
|
66
|
+
|
67
|
+
include_deferred 'should define the command', :find_many
|
68
|
+
|
69
|
+
include_deferred 'should define the command', :find_matching
|
70
|
+
|
71
|
+
include_deferred 'should define the command', :find_one
|
72
|
+
|
73
|
+
include_deferred 'should define the command', :insert_one
|
74
|
+
|
75
|
+
include_deferred 'should define the command', :update_one
|
76
|
+
|
77
|
+
include_deferred 'should define the command', :validate_one
|
78
|
+
|
79
|
+
describe '#==' do
|
80
|
+
let(:other_options) { { name: } }
|
81
|
+
let(:other_collection) { described_class.new(**other_options) }
|
82
|
+
|
83
|
+
describe 'with nil' do
|
84
|
+
it { expect(collection == nil).to be false } # rubocop:disable Style/NilComparison
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'with an object' do
|
88
|
+
it { expect(collection == Object.new.freeze).to be false }
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'with a collection with non-matching properties' do
|
92
|
+
let(:other_options) { super().merge(custom_option: 'value') }
|
93
|
+
|
94
|
+
it { expect(collection == other_collection).to be false }
|
95
|
+
end
|
96
|
+
|
97
|
+
describe 'with a collection with matching properties' do
|
98
|
+
it { expect(collection == other_collection).to be true }
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'with another type of collection' do
|
102
|
+
let(:other_collection) do
|
103
|
+
Spec::OtherCollection.new(**other_options)
|
104
|
+
end
|
105
|
+
|
106
|
+
example_class 'Spec::OtherCollection',
|
107
|
+
Cuprum::Collections::Collection
|
108
|
+
|
109
|
+
it { expect(collection == other_collection).to be false }
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'when initialized with options' do
|
113
|
+
let(:constructor_options) do
|
114
|
+
super().merge(
|
115
|
+
qualified_name: 'spec/scoped_books',
|
116
|
+
singular_name: 'grimoire'
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
describe 'with a collection with non-matching properties' do
|
121
|
+
it { expect(collection == other_collection).to be false }
|
122
|
+
end
|
123
|
+
|
124
|
+
describe 'with a collection with matching properties' do
|
125
|
+
let(:other_options) do
|
126
|
+
super().merge(
|
127
|
+
qualified_name: 'spec/scoped_books',
|
128
|
+
singular_name: 'grimoire'
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
it { expect(collection == other_collection).to be true }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#count' do
|
138
|
+
it { expect(collection).to respond_to(:count).with(0).arguments }
|
139
|
+
|
140
|
+
it { expect(collection).to have_aliased_method(:count).as(:size) }
|
141
|
+
|
142
|
+
next if options[:abstract]
|
143
|
+
|
144
|
+
it { expect(collection.count).to be 0 }
|
145
|
+
|
146
|
+
wrap_context 'when the collection has many items' do
|
147
|
+
it { expect(collection.count).to be items.count }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '#matches?' do
|
152
|
+
def tools
|
153
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should define the method' do
|
157
|
+
expect(collection)
|
158
|
+
.to respond_to(:matches?)
|
159
|
+
.with(0).arguments
|
160
|
+
.and_any_keywords
|
161
|
+
end
|
162
|
+
|
163
|
+
describe 'with no options' do
|
164
|
+
it { expect(collection.matches?).to be true }
|
165
|
+
end
|
166
|
+
|
167
|
+
describe 'with non-matching entity class as a Class' do
|
168
|
+
let(:other_options) { { entity_class: Grimoire } }
|
169
|
+
|
170
|
+
it { expect(collection.matches?(**other_options)).to be false }
|
171
|
+
end
|
172
|
+
|
173
|
+
describe 'with non-matching entity class as a String' do
|
174
|
+
let(:other_options) { { entity_class: 'Grimoire' } }
|
175
|
+
|
176
|
+
it { expect(collection.matches?(**other_options)).to be false }
|
177
|
+
end
|
178
|
+
|
179
|
+
describe 'with non-matching name' do
|
180
|
+
it { expect(collection.matches?(name: 'grimoires')).to be false }
|
181
|
+
end
|
182
|
+
|
183
|
+
describe 'with non-matching primary key name' do
|
184
|
+
let(:other_options) { { primary_key_name: 'uuid' } }
|
185
|
+
|
186
|
+
it { expect(collection.matches?(**other_options)).to be false }
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'with non-matching primary key type' do
|
190
|
+
let(:other_options) { { primary_key_type: String } }
|
191
|
+
|
192
|
+
it { expect(collection.matches?(**other_options)).to be false }
|
193
|
+
end
|
194
|
+
|
195
|
+
describe 'with non-matching qualified name' do
|
196
|
+
let(:other_options) { { qualified_name: 'spec/scoped_books' } }
|
197
|
+
|
198
|
+
it { expect(collection.matches?(**other_options)).to be false }
|
199
|
+
end
|
200
|
+
|
201
|
+
describe 'with non-matching singular name' do
|
202
|
+
let(:other_options) { { singular_name: 'grimoire' } }
|
203
|
+
|
204
|
+
it { expect(collection.matches?(**other_options)).to be false }
|
205
|
+
end
|
206
|
+
|
207
|
+
describe 'with non-matching custom options' do
|
208
|
+
let(:other_options) { { custom_option: 'custom value' } }
|
209
|
+
|
210
|
+
it { expect(collection.matches?(**other_options)).to be false }
|
211
|
+
end
|
212
|
+
|
213
|
+
describe 'with partially-matching options' do
|
214
|
+
let(:other_options) do
|
215
|
+
{
|
216
|
+
name:,
|
217
|
+
singular_name: 'grimoire'
|
218
|
+
}
|
219
|
+
end
|
220
|
+
|
221
|
+
it { expect(collection.matches?(**other_options)).to be false }
|
222
|
+
end
|
223
|
+
|
224
|
+
describe 'with matching entity class as a Class' do
|
225
|
+
let(:configured_entity_class) do
|
226
|
+
options.fetch(:default_entity_class, Book)
|
227
|
+
end
|
228
|
+
let(:other_options) { { entity_class: configured_entity_class } }
|
229
|
+
|
230
|
+
it { expect(collection.matches?(**other_options)).to be true }
|
231
|
+
end
|
232
|
+
|
233
|
+
describe 'with matching entity class as a String' do
|
234
|
+
let(:configured_entity_class) do
|
235
|
+
options.fetch(:default_entity_class, Book)
|
236
|
+
end
|
237
|
+
let(:other_options) do
|
238
|
+
{ entity_class: configured_entity_class.to_s }
|
239
|
+
end
|
240
|
+
|
241
|
+
it { expect(collection.matches?(**other_options)).to be true }
|
242
|
+
end
|
243
|
+
|
244
|
+
describe 'with matching name' do
|
245
|
+
let(:other_options) { { name: } }
|
246
|
+
|
247
|
+
it { expect(collection.matches?(**other_options)).to be true }
|
248
|
+
end
|
249
|
+
|
250
|
+
describe 'with matching primary key name' do
|
251
|
+
let(:other_options) { { primary_key_name: 'id' } }
|
252
|
+
|
253
|
+
it { expect(collection.matches?(**other_options)).to be true }
|
254
|
+
end
|
255
|
+
|
256
|
+
describe 'with matching primary key type' do
|
257
|
+
let(:other_options) { { primary_key_type: Integer } }
|
258
|
+
|
259
|
+
it { expect(collection.matches?(**other_options)).to be true }
|
260
|
+
end
|
261
|
+
|
262
|
+
describe 'with matching qualified name' do
|
263
|
+
let(:other_options) { { qualified_name: name } }
|
264
|
+
|
265
|
+
it { expect(collection.matches?(**other_options)).to be true }
|
266
|
+
end
|
267
|
+
|
268
|
+
describe 'with matching singular name' do
|
269
|
+
let(:other_options) do
|
270
|
+
{ singular_name: tools.str.singularize(name) }
|
271
|
+
end
|
272
|
+
|
273
|
+
it { expect(collection.matches?(**other_options)).to be true }
|
274
|
+
end
|
275
|
+
|
276
|
+
describe 'with multiple matching options' do
|
277
|
+
let(:other_options) do
|
278
|
+
{
|
279
|
+
name:,
|
280
|
+
primary_key_name: 'id',
|
281
|
+
qualified_name: name
|
282
|
+
}
|
283
|
+
end
|
284
|
+
|
285
|
+
it { expect(collection.matches?(**other_options)).to be true }
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
describe '#query' do
|
290
|
+
let(:error_message) do
|
291
|
+
"#{described_class.name} is an abstract class. Define a " \
|
292
|
+
'collection subclass and implement the #query method.'
|
293
|
+
end
|
294
|
+
let(:default_order) { defined?(super()) ? super() : {} }
|
295
|
+
let(:query) { collection.query }
|
296
|
+
|
297
|
+
it { expect(collection).to respond_to(:query).with(0).arguments }
|
298
|
+
|
299
|
+
if options[:abstract]
|
300
|
+
it 'should raise an exception' do
|
301
|
+
expect { collection.query }
|
302
|
+
.to raise_error(
|
303
|
+
described_class::AbstractCollectionError,
|
304
|
+
error_message
|
305
|
+
)
|
306
|
+
end
|
307
|
+
else
|
308
|
+
it { expect(collection.query).to be_a query_class }
|
309
|
+
|
310
|
+
it 'should set the query options' do
|
311
|
+
query_options.each do |option, value|
|
312
|
+
expect(collection.query.send(option)).to be == value
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
it { expect(query.limit).to be nil }
|
317
|
+
|
318
|
+
it { expect(query.offset).to be nil }
|
319
|
+
|
320
|
+
it { expect(query.order).to be == default_order }
|
321
|
+
|
322
|
+
it { expect(query.scope).to be == subject.scope }
|
323
|
+
|
324
|
+
context 'when initialized with a scope' do
|
325
|
+
let(:initial_scope) do
|
326
|
+
Cuprum::Collections::Scope.new({ 'ok' => true })
|
327
|
+
end
|
328
|
+
let(:constructor_options) do
|
329
|
+
super().merge(scope: initial_scope)
|
330
|
+
end
|
331
|
+
|
332
|
+
it { expect(query.scope).to be == subject.scope }
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/rspec/deferred/parameter_validation_examples'
|
4
|
+
require 'rspec/sleeping_king_studios/deferred'
|
5
|
+
|
6
|
+
require 'cuprum/collections/rspec/deferred'
|
7
|
+
|
8
|
+
module Cuprum::Collections::RSpec::Deferred
|
9
|
+
# Deferred examples for testing collection commands.
|
10
|
+
module CommandExamples
|
11
|
+
include RSpec::SleepingKingStudios::Deferred::Provider
|
12
|
+
include Cuprum::RSpec::Deferred::ParameterValidationExamples
|
13
|
+
|
14
|
+
deferred_examples 'should implement the CollectionCommand methods' do
|
15
|
+
describe '#collection' do
|
16
|
+
include_examples 'should define reader', :collection, -> { collection }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#name' do
|
20
|
+
include_examples 'should define reader', :name, -> { collection.name }
|
21
|
+
|
22
|
+
it 'should alias the method' do
|
23
|
+
expect(subject).to have_aliased_method(:name).as(:collection_name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#primary_key_name' do
|
28
|
+
include_examples 'should define reader',
|
29
|
+
:primary_key_name,
|
30
|
+
-> { collection.primary_key_name }
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#primary_key_type' do
|
34
|
+
include_examples 'should define reader',
|
35
|
+
:primary_key_type,
|
36
|
+
-> { collection.primary_key_type }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#query' do
|
40
|
+
let(:mock_query) { instance_double(Cuprum::Collections::Query) }
|
41
|
+
|
42
|
+
before(:example) do
|
43
|
+
allow(collection).to receive(:query).and_return(mock_query)
|
44
|
+
end
|
45
|
+
|
46
|
+
it { expect(subject).to respond_to(:query).with(0).arguments }
|
47
|
+
|
48
|
+
it { expect(subject.query).to be mock_query }
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#singular_name' do
|
52
|
+
include_examples 'should define reader',
|
53
|
+
:singular_name,
|
54
|
+
-> { collection.singular_name }
|
55
|
+
|
56
|
+
it 'should alias the method' do
|
57
|
+
expect(subject)
|
58
|
+
.to have_aliased_method(:singular_name)
|
59
|
+
.as(:member_name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
deferred_examples 'should validate the attributes parameter' do
|
65
|
+
describe 'with an invalid attributes value' do
|
66
|
+
let(:attributes) { Object.new.freeze }
|
67
|
+
|
68
|
+
include_deferred 'should validate the parameter',
|
69
|
+
:attributes,
|
70
|
+
'sleeping_king_studios.tools.assertions.instance_of',
|
71
|
+
expected: Hash
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'with an attributes value with invalid keys' do
|
75
|
+
let(:attributes) do
|
76
|
+
{
|
77
|
+
nil => 'NilClass',
|
78
|
+
'string' => 'String',
|
79
|
+
symbol: 'Symbol'
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
include_deferred 'should validate the parameter',
|
84
|
+
'attributes[nil] key',
|
85
|
+
'sleeping_king_studios.tools.assertions.presence'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
deferred_examples 'should validate the primary key parameter' do
|
90
|
+
describe 'with an invalid primary key value' do
|
91
|
+
let(:primary_key) { Object.new.freeze }
|
92
|
+
let(:expected_error) do
|
93
|
+
Cuprum::Errors::InvalidParameters.new(
|
94
|
+
command_class: command.class,
|
95
|
+
failures: [
|
96
|
+
tools.assertions.error_message_for(
|
97
|
+
'sleeping_king_studios.tools.assertions.instance_of',
|
98
|
+
as: 'primary_key',
|
99
|
+
expected: collection.primary_key_type
|
100
|
+
)
|
101
|
+
]
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should return a failing result with InvalidParameters error' do
|
106
|
+
expect(call_command)
|
107
|
+
.to be_a_failing_result
|
108
|
+
.with_error(expected_error)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
deferred_examples 'should validate the primary keys parameter' do
|
114
|
+
describe 'with an invalid primary keys value' do
|
115
|
+
let(:primary_keys) { Object.new.freeze }
|
116
|
+
|
117
|
+
include_deferred 'should validate the parameter',
|
118
|
+
:primary_keys,
|
119
|
+
'sleeping_king_studios.tools.assertions.instance_of',
|
120
|
+
expected: Array
|
121
|
+
end
|
122
|
+
|
123
|
+
describe 'with an attributes value with invalid keys' do
|
124
|
+
let(:valid_primary_key_value) do
|
125
|
+
defined?(super()) ? super() : 0
|
126
|
+
end
|
127
|
+
let(:primary_keys) do
|
128
|
+
[
|
129
|
+
nil,
|
130
|
+
Object.new.freeze,
|
131
|
+
valid_primary_key_value
|
132
|
+
]
|
133
|
+
end
|
134
|
+
let(:expected_error) do
|
135
|
+
Cuprum::Errors::InvalidParameters.new(
|
136
|
+
command_class: command.class,
|
137
|
+
failures: [
|
138
|
+
tools.assertions.error_message_for(
|
139
|
+
'sleeping_king_studios.tools.assertions.instance_of',
|
140
|
+
as: 'primary_keys[0]',
|
141
|
+
expected: collection.primary_key_type
|
142
|
+
),
|
143
|
+
tools.assertions.error_message_for(
|
144
|
+
'sleeping_king_studios.tools.assertions.instance_of',
|
145
|
+
as: 'primary_keys[1]',
|
146
|
+
expected: collection.primary_key_type
|
147
|
+
)
|
148
|
+
]
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should return a failing result with InvalidParameters error' do
|
153
|
+
expect(call_command)
|
154
|
+
.to be_a_failing_result
|
155
|
+
.with_error(expected_error)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cuprum/collections/rspec/deferred/command_examples'
|
4
|
+
require 'cuprum/collections/rspec/deferred/commands'
|
5
|
+
|
6
|
+
module Cuprum::Collections::RSpec::Deferred::Commands
|
7
|
+
# Namespace for deferred example groups for validating AssignOne commands.
|
8
|
+
module AssignOneExamples
|
9
|
+
include RSpec::SleepingKingStudios::Deferred::Provider
|
10
|
+
|
11
|
+
deferred_examples 'should implement the AssignOne command' \
|
12
|
+
do |allow_extra_attributes: true|
|
13
|
+
describe '#call' do
|
14
|
+
include Cuprum::Collections::RSpec::Deferred::CommandExamples
|
15
|
+
|
16
|
+
shared_examples 'should assign the attributes' do
|
17
|
+
it { expect(result).to be_a_passing_result }
|
18
|
+
|
19
|
+
it { expect(result.value).to be_a entity.class }
|
20
|
+
|
21
|
+
it { expect(result.value).to be == expected_value }
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:attributes) { {} }
|
25
|
+
let(:result) do
|
26
|
+
command.call(attributes:, entity:)
|
27
|
+
end
|
28
|
+
let(:expected_attributes) do
|
29
|
+
initial_attributes.merge(attributes)
|
30
|
+
end
|
31
|
+
let(:expected_value) do
|
32
|
+
defined?(super()) ? super() : expected_attributes
|
33
|
+
end
|
34
|
+
|
35
|
+
def call_command
|
36
|
+
command.call(attributes:, entity:)
|
37
|
+
end
|
38
|
+
|
39
|
+
include_deferred 'should validate the attributes parameter'
|
40
|
+
|
41
|
+
if defined_deferred_examples? 'should validate the entity'
|
42
|
+
include_deferred 'should validate the entity'
|
43
|
+
else
|
44
|
+
# :nocov:
|
45
|
+
pending \
|
46
|
+
'the command should validate the entity parameter, but entity ' \
|
47
|
+
'validation is not defined - implement a "should validate the ' \
|
48
|
+
'entity" deferred example group to resolve this warning'
|
49
|
+
# :nocov:
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'with an empty attributes hash' do
|
53
|
+
let(:attributes) { {} }
|
54
|
+
|
55
|
+
include_examples 'should assign the attributes'
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'with an attributes hash with partial attributes' do
|
59
|
+
let(:attributes) { { title: 'Gideon the Ninth' } }
|
60
|
+
|
61
|
+
include_examples 'should assign the attributes'
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'with an attributes hash with full attributes' do
|
65
|
+
let(:attributes) do
|
66
|
+
{
|
67
|
+
title: 'Gideon the Ninth',
|
68
|
+
author: 'Tamsyn Muir',
|
69
|
+
series: 'The Locked Tomb',
|
70
|
+
category: 'Horror'
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
include_examples 'should assign the attributes'
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'with an attributes hash with extra attributes' do
|
78
|
+
let(:attributes) do
|
79
|
+
{
|
80
|
+
title: 'The Book of Lost Tales',
|
81
|
+
audiobook: true
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
if allow_extra_attributes
|
86
|
+
include_examples 'should assign the attributes'
|
87
|
+
else
|
88
|
+
# :nocov:
|
89
|
+
let(:valid_attributes) do
|
90
|
+
defined?(super()) ? super() : expected_attributes.keys
|
91
|
+
end
|
92
|
+
let(:expected_error) do
|
93
|
+
Cuprum::Collections::Errors::ExtraAttributes.new(
|
94
|
+
entity_class: entity.class,
|
95
|
+
extra_attributes: %w[audiobook],
|
96
|
+
valid_attributes:
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should return a failing result' do
|
101
|
+
expect(result).to be_a_failing_result.with_error(expected_error)
|
102
|
+
end
|
103
|
+
# :nocov:
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when the entity has existing attributes' do
|
108
|
+
let(:initial_attributes) do
|
109
|
+
# :nocov:
|
110
|
+
if defined?(super())
|
111
|
+
super().merge(fixtures_data.first)
|
112
|
+
else
|
113
|
+
fixtures_data.first
|
114
|
+
end
|
115
|
+
# :nocov:
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'with an empty attributes hash' do
|
119
|
+
let(:attributes) { {} }
|
120
|
+
|
121
|
+
include_examples 'should assign the attributes'
|
122
|
+
end
|
123
|
+
|
124
|
+
describe 'with an attributes hash with partial attributes' do
|
125
|
+
let(:attributes) { { title: 'Gideon the Ninth' } }
|
126
|
+
|
127
|
+
include_examples 'should assign the attributes'
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'with an attributes hash with full attributes' do
|
131
|
+
let(:attributes) do
|
132
|
+
{
|
133
|
+
title: 'Gideon the Ninth',
|
134
|
+
author: 'Tamsyn Muir',
|
135
|
+
series: 'The Locked Tomb',
|
136
|
+
category: 'Horror'
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
include_examples 'should assign the attributes'
|
141
|
+
end
|
142
|
+
|
143
|
+
describe 'with an attributes hash with extra attributes' do
|
144
|
+
let(:attributes) do
|
145
|
+
{
|
146
|
+
title: 'The Book of Lost Tales',
|
147
|
+
audiobook: true
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
if allow_extra_attributes
|
152
|
+
include_examples 'should assign the attributes'
|
153
|
+
else
|
154
|
+
# :nocov:
|
155
|
+
let(:valid_attributes) do
|
156
|
+
defined?(super()) ? super() : expected_attributes.keys
|
157
|
+
end
|
158
|
+
let(:expected_error) do
|
159
|
+
Cuprum::Collections::Errors::ExtraAttributes.new(
|
160
|
+
entity_class: entity.class,
|
161
|
+
extra_attributes: %w[audiobook],
|
162
|
+
valid_attributes:
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should return a failing result' do
|
167
|
+
expect(result)
|
168
|
+
.to be_a_failing_result
|
169
|
+
.with_error(expected_error)
|
170
|
+
end
|
171
|
+
# :nocov:
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|