cuprum-collections 0.5.1 → 0.6.0.rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -0
- data/lib/cuprum/collections/adaptable/collection.rb +18 -0
- data/lib/cuprum/collections/adaptable/command.rb +22 -0
- data/lib/cuprum/collections/adaptable/commands/abstract_assign_one.rb +27 -0
- data/lib/cuprum/collections/adaptable/commands/abstract_build_one.rb +25 -0
- data/lib/cuprum/collections/adaptable/commands/abstract_validate_one.rb +35 -0
- data/lib/cuprum/collections/adaptable/commands.rb +15 -0
- data/lib/cuprum/collections/adaptable/query.rb +64 -0
- data/lib/cuprum/collections/adaptable.rb +13 -0
- data/lib/cuprum/collections/adapter.rb +300 -0
- data/lib/cuprum/collections/adapters/data_adapter.rb +82 -0
- data/lib/cuprum/collections/adapters/entity_adapter.rb +76 -0
- data/lib/cuprum/collections/adapters/hash_adapter.rb +48 -0
- data/lib/cuprum/collections/adapters.rb +14 -0
- data/lib/cuprum/collections/basic/collection.rb +2 -20
- data/lib/cuprum/collections/basic/commands/destroy_one.rb +1 -1
- data/lib/cuprum/collections/basic/commands/find_many.rb +0 -31
- data/lib/cuprum/collections/basic/commands/find_matching.rb +0 -94
- data/lib/cuprum/collections/basic/commands/find_one.rb +0 -18
- data/lib/cuprum/collections/basic/commands/insert_one.rb +1 -1
- data/lib/cuprum/collections/basic/commands/update_one.rb +1 -1
- data/lib/cuprum/collections/basic/scopes/criteria_scope.rb +36 -21
- data/lib/cuprum/collections/basic.rb +6 -5
- data/lib/cuprum/collections/collection.rb +6 -0
- data/lib/cuprum/collections/collection_command.rb +1 -1
- data/lib/cuprum/collections/commands/abstract_find_many.rb +40 -3
- data/lib/cuprum/collections/commands/abstract_find_matching.rb +102 -0
- data/lib/cuprum/collections/commands/abstract_find_one.rb +23 -1
- data/lib/cuprum/collections/commands/associations/find_many.rb +1 -3
- data/lib/cuprum/collections/commands/associations/require_many.rb +1 -1
- data/lib/cuprum/collections/commands/find_one_matching.rb +10 -10
- data/lib/cuprum/collections/commands/query_command.rb +6 -4
- data/lib/cuprum/collections/commands/upsert.rb +0 -2
- data/lib/cuprum/collections/constraints/order/attributes_array.rb +5 -4
- data/lib/cuprum/collections/constraints/order/attributes_hash.rb +5 -4
- data/lib/cuprum/collections/constraints/order/sort_direction.rb +2 -2
- data/lib/cuprum/collections/constraints/ordering.rb +11 -9
- data/lib/cuprum/collections/constraints/query_hash.rb +2 -2
- data/lib/cuprum/collections/errors/abstract_find_error.rb +101 -23
- data/lib/cuprum/collections/errors/extra_attributes.rb +3 -3
- data/lib/cuprum/collections/errors/failed_validation.rb +3 -3
- data/lib/cuprum/collections/errors/missing_default_contract.rb +12 -4
- data/lib/cuprum/collections/queries.rb +4 -0
- data/lib/cuprum/collections/relation.rb +0 -2
- data/lib/cuprum/collections/relations/parameters.rb +120 -68
- data/lib/cuprum/collections/repository.rb +71 -6
- data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +23 -4
- data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +18 -0
- data/lib/cuprum/collections/rspec/contracts/scope_contracts.rb +51 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/builder_contracts.rb +10 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/composition_contracts.rb +8 -0
- data/lib/cuprum/collections/rspec/contracts/scopes/criteria_contracts.rb +18 -366
- data/lib/cuprum/collections/rspec/contracts/scopes/logical_contracts.rb +30 -0
- data/lib/cuprum/collections/rspec/contracts/scopes.rb +2 -0
- data/lib/cuprum/collections/rspec/contracts.rb +2 -10
- data/lib/cuprum/collections/rspec/deferred/adapter_examples.rb +1077 -0
- data/lib/cuprum/collections/rspec/deferred/collection_examples.rb +27 -7
- data/lib/cuprum/collections/rspec/deferred/commands/assign_one_examples.rb +4 -4
- data/lib/cuprum/collections/rspec/deferred/commands/build_one_examples.rb +2 -2
- data/lib/cuprum/collections/rspec/deferred/commands/destroy_one_examples.rb +2 -2
- data/lib/cuprum/collections/rspec/deferred/commands/find_many_examples.rb +5 -5
- data/lib/cuprum/collections/rspec/deferred/commands/find_matching_examples.rb +45 -12
- data/lib/cuprum/collections/rspec/deferred/commands/find_one_examples.rb +2 -2
- data/lib/cuprum/collections/rspec/deferred/commands/insert_one_examples.rb +1 -1
- data/lib/cuprum/collections/rspec/deferred/commands/update_one_examples.rb +1 -1
- data/lib/cuprum/collections/rspec/deferred/query_examples.rb +930 -0
- data/lib/cuprum/collections/rspec/deferred/relation_examples.rb +48 -17
- data/lib/cuprum/collections/rspec/deferred/repository_examples.rb +961 -0
- data/lib/cuprum/collections/rspec/deferred/scope_examples.rb +598 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/all_examples.rb +391 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/builder_examples.rb +857 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/composition_examples.rb +93 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/conjunction_examples.rb +438 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/criteria_examples.rb +1941 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/disjunction_examples.rb +415 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/none_examples.rb +385 -0
- data/lib/cuprum/collections/rspec/deferred/scopes/parser_examples.rb +740 -0
- data/lib/cuprum/collections/rspec/deferred/scopes.rb +8 -0
- data/lib/cuprum/collections/scope.rb +2 -2
- data/lib/cuprum/collections/scopes/container.rb +5 -4
- data/lib/cuprum/collections/scopes/criteria/parser.rb +24 -48
- data/lib/cuprum/collections/scopes/criteria.rb +7 -6
- data/lib/cuprum/collections/version.rb +4 -4
- data/lib/cuprum/collections.rb +5 -1
- metadata +47 -11
- data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +0 -2127
- data/lib/cuprum/collections/rspec/contracts/basic.rb +0 -11
- data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +0 -387
- data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +0 -169
- data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +0 -1264
|
@@ -0,0 +1,961 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rspec/sleeping_king_studios/deferred'
|
|
4
|
+
require 'rspec/sleeping_king_studios/matchers/core/have_aliased_method'
|
|
5
|
+
|
|
6
|
+
require 'cuprum/collections/rspec/deferred'
|
|
7
|
+
|
|
8
|
+
module Cuprum::Collections::RSpec::Deferred
|
|
9
|
+
# Deferred examples for validating Repository implementations.
|
|
10
|
+
module RepositoryExamples
|
|
11
|
+
include RSpec::SleepingKingStudios::Deferred::Provider
|
|
12
|
+
|
|
13
|
+
# Initializes the repository with collection data.
|
|
14
|
+
#
|
|
15
|
+
# The including example group must define a #build_collection(**options)
|
|
16
|
+
# method, which returns a valid collection instance for the repository.
|
|
17
|
+
#
|
|
18
|
+
# The default collections generated can be overriden by defining the
|
|
19
|
+
# #configured_collections memoized helper in the including example group.
|
|
20
|
+
deferred_context 'when the repository has many collections' do
|
|
21
|
+
include RSpec::SleepingKingStudios::Deferred::Dependencies
|
|
22
|
+
|
|
23
|
+
depends_on :build_collection,
|
|
24
|
+
'builds a valid collection for the repository'
|
|
25
|
+
|
|
26
|
+
let(:configured_collections) do
|
|
27
|
+
next super() if defined?(super())
|
|
28
|
+
|
|
29
|
+
[
|
|
30
|
+
{ name: 'authors' },
|
|
31
|
+
{ name: 'books', qualified_name: 'sources/books' },
|
|
32
|
+
{ name: 'publishers' }
|
|
33
|
+
]
|
|
34
|
+
end
|
|
35
|
+
let(:collections) do
|
|
36
|
+
configured_collections.to_h do |options|
|
|
37
|
+
collection = build_collection(**options)
|
|
38
|
+
|
|
39
|
+
[collection.qualified_name, collection]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
before(:example) do
|
|
44
|
+
collections.each_value { |collection| repository << collection }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Validates that the described class implements the Repository interface.
|
|
49
|
+
#
|
|
50
|
+
# The including example group must define a #build_collection(**options)
|
|
51
|
+
# method, which returns a valid collection instance for the repository.
|
|
52
|
+
deferred_examples 'should be a Repository' do |**deferred_options|
|
|
53
|
+
deferred_examples 'should create the collection' do
|
|
54
|
+
let(:configured_collection_class) do
|
|
55
|
+
return super() if defined?(super())
|
|
56
|
+
|
|
57
|
+
configured = deferred_options[:collection_class]
|
|
58
|
+
|
|
59
|
+
# :nocov:
|
|
60
|
+
configured = Object.const_get(configured) if configured.is_a?(String)
|
|
61
|
+
# :nocov:
|
|
62
|
+
|
|
63
|
+
configured
|
|
64
|
+
end
|
|
65
|
+
let(:configured_entity_class) do
|
|
66
|
+
return super() if defined?(super())
|
|
67
|
+
|
|
68
|
+
# :nocov:
|
|
69
|
+
expected =
|
|
70
|
+
if collection_options.key?(:entity_class)
|
|
71
|
+
collection_options[:entity_class]
|
|
72
|
+
elsif deferred_options.key?(:entity_class)
|
|
73
|
+
deferred_options[:entity_class]
|
|
74
|
+
else
|
|
75
|
+
qualified_name
|
|
76
|
+
.split('/')
|
|
77
|
+
.then { |ary| [*ary[0...-1], tools.str.singularize(ary[-1])] }
|
|
78
|
+
.map { |str| tools.str.camelize(str) }
|
|
79
|
+
.join('::')
|
|
80
|
+
end
|
|
81
|
+
# :nocov:
|
|
82
|
+
expected = Object.const_get(expected) if expected.is_a?(String)
|
|
83
|
+
|
|
84
|
+
expected
|
|
85
|
+
end
|
|
86
|
+
let(:configured_member_name) do
|
|
87
|
+
return super() if defined?(super())
|
|
88
|
+
|
|
89
|
+
tools.str.singularize(collection_name.to_s.split('/').last)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def tools
|
|
93
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'should create the collection' do
|
|
97
|
+
create_collection(safe: false)
|
|
98
|
+
|
|
99
|
+
expect(repository.key?(qualified_name)).to be true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'should return the collection' do
|
|
103
|
+
collection = create_collection(safe: false)
|
|
104
|
+
|
|
105
|
+
expect(collection).to be repository[qualified_name]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it { expect(collection).to be_a configured_collection_class }
|
|
109
|
+
|
|
110
|
+
it 'should set the entity class' do
|
|
111
|
+
expect(collection.entity_class).to be == configured_entity_class
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'should set the collection name' do
|
|
115
|
+
expect(collection.name).to be == collection_name.to_s
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'should set the member name' do
|
|
119
|
+
expect(collection.singular_name).to be == configured_member_name
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'should set the qualified name' do
|
|
123
|
+
expect(collection.qualified_name).to be == qualified_name
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'should set the collection options' do
|
|
127
|
+
expect(collection).to have_attributes(
|
|
128
|
+
primary_key_name:,
|
|
129
|
+
primary_key_type:
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
let(:valid_collection) do
|
|
135
|
+
next super() if defined?(super())
|
|
136
|
+
|
|
137
|
+
build_collection(name: 'widgets', qualified_name: 'scope/widgets')
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
describe '.new' do
|
|
141
|
+
it 'should yield the repository' do
|
|
142
|
+
yielded = nil
|
|
143
|
+
|
|
144
|
+
repository = described_class.new { |value| yielded = value }
|
|
145
|
+
|
|
146
|
+
expect(yielded).to be repository
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
describe '#[]' do
|
|
151
|
+
let(:error_class) do
|
|
152
|
+
described_class::UndefinedCollectionError
|
|
153
|
+
end
|
|
154
|
+
let(:error_message) do
|
|
155
|
+
"repository does not define collection #{collection_name.inspect}"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it { expect(repository).to respond_to(:[]).with(1).argument }
|
|
159
|
+
|
|
160
|
+
describe 'with nil' do
|
|
161
|
+
let(:collection_name) { nil }
|
|
162
|
+
|
|
163
|
+
it 'should raise an exception' do
|
|
164
|
+
expect { repository[collection_name] }
|
|
165
|
+
.to raise_error(error_class, error_message)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
describe 'with an object' do
|
|
170
|
+
let(:collection_name) { Object.new.freeze }
|
|
171
|
+
|
|
172
|
+
it 'should raise an exception' do
|
|
173
|
+
expect { repository[collection_name] }
|
|
174
|
+
.to raise_error(error_class, error_message)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
describe 'with an invalid string' do
|
|
179
|
+
let(:collection_name) { 'invalid_name' }
|
|
180
|
+
|
|
181
|
+
it 'should raise an exception' do
|
|
182
|
+
expect { repository[collection_name] }
|
|
183
|
+
.to raise_error(error_class, error_message)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
describe 'with an invalid symbol' do
|
|
188
|
+
let(:collection_name) { :invalid_name }
|
|
189
|
+
|
|
190
|
+
it 'should raise an exception' do
|
|
191
|
+
expect { repository[collection_name] }
|
|
192
|
+
.to raise_error(error_class, error_message)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
wrap_deferred 'when the repository has many collections' do
|
|
197
|
+
describe 'with an invalid string' do
|
|
198
|
+
let(:collection_name) { 'invalid_name' }
|
|
199
|
+
|
|
200
|
+
it 'should raise an exception' do
|
|
201
|
+
expect { repository[collection_name] }
|
|
202
|
+
.to raise_error(error_class, error_message)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
describe 'with an invalid symbol' do
|
|
207
|
+
let(:collection_name) { :invalid_name }
|
|
208
|
+
|
|
209
|
+
it 'should raise an exception' do
|
|
210
|
+
expect { repository[collection_name] }
|
|
211
|
+
.to raise_error(error_class, error_message)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
describe 'with a valid string' do
|
|
216
|
+
let(:collection) { collections.values.first }
|
|
217
|
+
let(:collection_name) { collections.keys.first }
|
|
218
|
+
|
|
219
|
+
it { expect(repository[collection_name]).to be collection }
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
describe 'with a valid symbol' do
|
|
223
|
+
let(:collection) { collections.values.first }
|
|
224
|
+
let(:collection_name) { collections.keys.first.intern }
|
|
225
|
+
|
|
226
|
+
it { expect(repository[collection_name]).to be collection }
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
describe '#add' do
|
|
232
|
+
let(:error_class) do
|
|
233
|
+
described_class::InvalidCollectionError
|
|
234
|
+
end
|
|
235
|
+
let(:error_message) do
|
|
236
|
+
"#{collection.inspect} is not a valid collection"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it 'should define the method' do
|
|
240
|
+
expect(repository)
|
|
241
|
+
.to respond_to(:add)
|
|
242
|
+
.with(1).argument
|
|
243
|
+
.and_keywords(:force)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
it 'should alias #add as #<<' do
|
|
247
|
+
expect(repository.method(:<<)).to be == repository.method(:add)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
describe 'with nil' do
|
|
251
|
+
let(:collection) { nil }
|
|
252
|
+
|
|
253
|
+
it 'should raise an exception' do
|
|
254
|
+
expect { repository.add(collection) }
|
|
255
|
+
.to raise_error(error_class, error_message)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
describe 'with an object' do
|
|
260
|
+
let(:collection) { Object.new.freeze }
|
|
261
|
+
|
|
262
|
+
it 'should raise an exception' do
|
|
263
|
+
expect { repository.add(collection) }
|
|
264
|
+
.to raise_error(error_class, error_message)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
describe 'with a collection' do
|
|
269
|
+
it { expect(repository.add(valid_collection)).to be repository }
|
|
270
|
+
|
|
271
|
+
it 'should add the collection to the repository' do
|
|
272
|
+
repository.add(valid_collection)
|
|
273
|
+
|
|
274
|
+
expect(repository[valid_collection.qualified_name])
|
|
275
|
+
.to be valid_collection
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
describe 'with force: true' do
|
|
279
|
+
it 'should add the collection to the repository' do
|
|
280
|
+
repository.add(valid_collection, force: true)
|
|
281
|
+
|
|
282
|
+
expect(repository[valid_collection.qualified_name])
|
|
283
|
+
.to be valid_collection
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
context 'when the collection already exists' do
|
|
288
|
+
let(:error_message) do
|
|
289
|
+
"collection #{valid_collection.qualified_name} already exists"
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
before(:example) do
|
|
293
|
+
allow(repository)
|
|
294
|
+
.to receive(:key?)
|
|
295
|
+
.with(valid_collection.qualified_name)
|
|
296
|
+
.and_return(true)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
it 'should raise an exception' do
|
|
300
|
+
expect { repository.add(valid_collection) }
|
|
301
|
+
.to raise_error(
|
|
302
|
+
described_class::DuplicateCollectionError,
|
|
303
|
+
error_message
|
|
304
|
+
)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it 'should not update the repository' do
|
|
308
|
+
begin
|
|
309
|
+
repository.add(valid_collection)
|
|
310
|
+
rescue described_class::DuplicateCollectionError
|
|
311
|
+
# Do nothing.
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
expect { repository[valid_collection.qualified_name] }
|
|
315
|
+
.to raise_error(
|
|
316
|
+
described_class::UndefinedCollectionError,
|
|
317
|
+
'repository does not define collection ' \
|
|
318
|
+
"#{valid_collection.qualified_name.inspect}"
|
|
319
|
+
)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
describe 'with force: true' do
|
|
323
|
+
it 'should add the collection to the repository' do
|
|
324
|
+
repository.add(valid_collection, force: true)
|
|
325
|
+
|
|
326
|
+
expect(repository[valid_collection.qualified_name])
|
|
327
|
+
.to be valid_collection
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
context 'when the repository is frozen' do
|
|
334
|
+
let(:error_message) do
|
|
335
|
+
"can't modify frozen #{described_class.name}"
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
before(:example) { repository.freeze }
|
|
339
|
+
|
|
340
|
+
it 'should raise an exception' do
|
|
341
|
+
expect { repository.add(valid_collection) }
|
|
342
|
+
.to raise_error FrozenError, error_message
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
describe '#create' do
|
|
348
|
+
let(:collection_name) { 'books' }
|
|
349
|
+
let(:qualified_name) { collection_name.to_s }
|
|
350
|
+
let(:primary_key_name) { 'id' }
|
|
351
|
+
let(:primary_key_type) { Integer }
|
|
352
|
+
let(:collection_options) { {} }
|
|
353
|
+
let(:collection) do
|
|
354
|
+
create_collection
|
|
355
|
+
|
|
356
|
+
repository[qualified_name]
|
|
357
|
+
end
|
|
358
|
+
let(:error_message) do
|
|
359
|
+
"#{described_class.name} is an abstract class. Define a " \
|
|
360
|
+
'repository subclass and implement the #build_collection method.'
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
define_method(:create_collection) \
|
|
364
|
+
do |force: false, safe: true, **options|
|
|
365
|
+
if safe
|
|
366
|
+
begin
|
|
367
|
+
repository.create(force:, **collection_options, **options)
|
|
368
|
+
rescue StandardError
|
|
369
|
+
# Do nothing.
|
|
370
|
+
end
|
|
371
|
+
else
|
|
372
|
+
repository.create(force:, **collection_options, **options)
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
it 'should define the method' do
|
|
377
|
+
expect(repository)
|
|
378
|
+
.to respond_to(:create)
|
|
379
|
+
.with(0).arguments
|
|
380
|
+
.and_keywords(:collection_name, :entity_class, :force)
|
|
381
|
+
.and_any_keywords
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
if deferred_options.fetch(:abstract, false)
|
|
385
|
+
it 'should raise an exception' do
|
|
386
|
+
expect { create_collection(safe: false) }
|
|
387
|
+
.to raise_error(
|
|
388
|
+
described_class::AbstractRepositoryError,
|
|
389
|
+
error_message
|
|
390
|
+
)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
next
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
describe 'with entity_class: a Class' do
|
|
397
|
+
let(:entity_class) { Book }
|
|
398
|
+
let(:collection_options) do
|
|
399
|
+
super().merge(entity_class:)
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
include_deferred 'should create the collection'
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
describe 'with entity_class: a String' do
|
|
406
|
+
let(:entity_class) { 'Book' }
|
|
407
|
+
let(:collection_options) do
|
|
408
|
+
super().merge(entity_class:)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
include_deferred 'should create the collection'
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
describe 'with name: a String' do
|
|
415
|
+
let(:collection_name) { 'books' }
|
|
416
|
+
let(:collection_options) do
|
|
417
|
+
super().merge(name: collection_name)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
include_deferred 'should create the collection'
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
describe 'with name: a Symbol' do
|
|
424
|
+
let(:collection_name) { :books }
|
|
425
|
+
let(:collection_options) do
|
|
426
|
+
super().merge(name: collection_name)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
include_deferred 'should create the collection'
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
describe 'with collection options' do
|
|
433
|
+
let(:primary_key_name) { 'uuid' }
|
|
434
|
+
let(:primary_key_type) { String }
|
|
435
|
+
let(:collection_options) do
|
|
436
|
+
super().merge(
|
|
437
|
+
name: collection_name,
|
|
438
|
+
primary_key_name:,
|
|
439
|
+
primary_key_type:
|
|
440
|
+
)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
include_deferred 'should create the collection'
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
context 'when the collection already exists' do
|
|
447
|
+
let(:collection_name) { 'books' }
|
|
448
|
+
let(:collection_options) do
|
|
449
|
+
super().merge(name: collection_name)
|
|
450
|
+
end
|
|
451
|
+
let(:error_message) do
|
|
452
|
+
"collection #{qualified_name} already exists"
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
before { create_collection(old: true) }
|
|
456
|
+
|
|
457
|
+
it 'should raise an exception' do
|
|
458
|
+
expect { create_collection(safe: false) }
|
|
459
|
+
.to raise_error(
|
|
460
|
+
described_class::DuplicateCollectionError,
|
|
461
|
+
error_message
|
|
462
|
+
)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
it 'should not update the repository' do
|
|
466
|
+
create_collection(old: false)
|
|
467
|
+
|
|
468
|
+
collection = repository[qualified_name]
|
|
469
|
+
|
|
470
|
+
expect(collection.options[:old]).to be true
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
describe 'with force: true' do
|
|
474
|
+
it 'should update the repository' do
|
|
475
|
+
create_collection(force: true, old: false)
|
|
476
|
+
|
|
477
|
+
collection = repository[qualified_name]
|
|
478
|
+
|
|
479
|
+
expect(collection.options[:old]).to be false
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
context 'when the repository is frozen' do
|
|
485
|
+
let(:collection_name) { 'books' }
|
|
486
|
+
let(:collection_options) do
|
|
487
|
+
super().merge(name: collection_name)
|
|
488
|
+
end
|
|
489
|
+
let(:error_message) do
|
|
490
|
+
"can't modify frozen #{described_class.name}"
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
before(:example) { repository.freeze }
|
|
494
|
+
|
|
495
|
+
it 'should raise an exception' do
|
|
496
|
+
expect { create_collection(safe: false) }
|
|
497
|
+
.to raise_error FrozenError, error_message
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
describe '#find' do
|
|
503
|
+
shared_examples 'should find the matching collection' do
|
|
504
|
+
context 'when the collection does not exist' do
|
|
505
|
+
let(:error_message) do
|
|
506
|
+
"repository does not define collection #{qualified_name.inspect}"
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
it 'should raise an exception' do
|
|
510
|
+
expect { repository.find(**collection_options) }.to raise_error(
|
|
511
|
+
described_class::UndefinedCollectionError,
|
|
512
|
+
error_message
|
|
513
|
+
)
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
next if deferred_options.fetch(:abstract, false)
|
|
518
|
+
|
|
519
|
+
context 'when the collection exists' do
|
|
520
|
+
include_deferred 'when the repository has many collections'
|
|
521
|
+
|
|
522
|
+
let(:collection) { collections.values.first }
|
|
523
|
+
let(:entity_class) { collection.entity_class }
|
|
524
|
+
let(:name) { collection.name }
|
|
525
|
+
let(:qualified_name) { collection.qualified_name }
|
|
526
|
+
|
|
527
|
+
it 'should find the collection' do
|
|
528
|
+
expect(repository.find(**collection_options)).to be == collection
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
let(:name) { 'books' }
|
|
534
|
+
let(:qualified_name) { name.to_s }
|
|
535
|
+
let(:collection_options) { {} }
|
|
536
|
+
|
|
537
|
+
it 'should define the method' do
|
|
538
|
+
expect(repository)
|
|
539
|
+
.to respond_to(:find)
|
|
540
|
+
.with(0).arguments
|
|
541
|
+
.and_any_keywords
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
describe 'with no parameters' do
|
|
545
|
+
let(:error_message) { "name or entity class can't be blank" }
|
|
546
|
+
|
|
547
|
+
it 'should raise an exception' do
|
|
548
|
+
expect { repository.find }
|
|
549
|
+
.to raise_error ArgumentError, error_message
|
|
550
|
+
end
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
if deferred_options.fetch(:find_by_entity_class, true)
|
|
554
|
+
describe 'with entity_class: a Class' do
|
|
555
|
+
let(:entity_class) { Book }
|
|
556
|
+
let(:collection_options) { super().merge(entity_class:) }
|
|
557
|
+
|
|
558
|
+
include_examples 'should find the matching collection'
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
describe 'with entity_class: a String' do
|
|
562
|
+
let(:entity_class) { 'Book' }
|
|
563
|
+
let(:collection_options) { super().merge(entity_class:) }
|
|
564
|
+
|
|
565
|
+
include_examples 'should find the matching collection'
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
describe 'with name: a String' do
|
|
570
|
+
let(:collection_options) { super().merge(name: name.to_s) }
|
|
571
|
+
|
|
572
|
+
include_examples 'should find the matching collection'
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
describe 'with name: a Symbol' do
|
|
576
|
+
let(:collection_options) { super().merge(name: name.intern) }
|
|
577
|
+
|
|
578
|
+
include_examples 'should find the matching collection'
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
describe 'with qualified_name: a String' do
|
|
582
|
+
let(:collection_options) do
|
|
583
|
+
super().merge(qualified_name: qualified_name.to_s)
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
include_examples 'should find the matching collection'
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
describe 'with qualified_name: a Symbol' do
|
|
590
|
+
let(:collection_options) do
|
|
591
|
+
super().merge(qualified_name: qualified_name.intern)
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
include_examples 'should find the matching collection'
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
describe 'with multiple parameters' do
|
|
598
|
+
let(:entity_class) { Book }
|
|
599
|
+
let(:collection_options) do
|
|
600
|
+
super().merge(entity_class:, qualified_name:)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
context 'when the collection does not exist' do
|
|
604
|
+
let(:error_message) do
|
|
605
|
+
"repository does not define collection #{qualified_name.inspect}"
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
it 'should raise an exception' do
|
|
609
|
+
expect { repository.find(**collection_options) }.to raise_error(
|
|
610
|
+
described_class::UndefinedCollectionError,
|
|
611
|
+
error_message
|
|
612
|
+
)
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
next if deferred_options.fetch(:abstract, false)
|
|
617
|
+
|
|
618
|
+
context 'when a partially-matching collection exists' do
|
|
619
|
+
include_deferred 'when the repository has many collections'
|
|
620
|
+
|
|
621
|
+
let(:collection) { collections.values.first }
|
|
622
|
+
let(:entity_class) { Spec::EntityClass }
|
|
623
|
+
let(:qualified_name) { collection.qualified_name }
|
|
624
|
+
let(:error_message) do
|
|
625
|
+
<<~TEXT.strip
|
|
626
|
+
collection "#{collection.qualified_name}" exists but does not match:
|
|
627
|
+
|
|
628
|
+
expected: #{{ entity_class: Spec::EntityClass }.inspect}
|
|
629
|
+
actual: #{{ entity_class: collection.entity_class }.inspect}
|
|
630
|
+
TEXT
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
example_class 'Spec::EntityClass'
|
|
634
|
+
|
|
635
|
+
it 'should raise an exception' do
|
|
636
|
+
expect { repository.find(**collection_options) }.to raise_error(
|
|
637
|
+
described_class::DuplicateCollectionError,
|
|
638
|
+
error_message
|
|
639
|
+
)
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
context 'when a matching collection exists' do
|
|
644
|
+
include_deferred 'when the repository has many collections'
|
|
645
|
+
|
|
646
|
+
let(:collection) { collections.values.first }
|
|
647
|
+
let(:entity_class) { collection.entity_class }
|
|
648
|
+
let(:qualified_name) { collection.qualified_name }
|
|
649
|
+
|
|
650
|
+
it 'should find the collection' do
|
|
651
|
+
expect(repository.find(**collection_options)).to be == collection
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
end
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
describe '#find_or_create' do
|
|
658
|
+
let(:collection_name) { 'books' }
|
|
659
|
+
let(:qualified_name) { collection_name.to_s }
|
|
660
|
+
let(:primary_key_name) { 'id' }
|
|
661
|
+
let(:primary_key_type) { Integer }
|
|
662
|
+
let(:collection_options) { {} }
|
|
663
|
+
let(:collection) do
|
|
664
|
+
create_collection
|
|
665
|
+
|
|
666
|
+
repository[qualified_name]
|
|
667
|
+
end
|
|
668
|
+
let(:error_message) do
|
|
669
|
+
"#{described_class.name} is an abstract class. Define a " \
|
|
670
|
+
'repository subclass and implement the #build_collection method.'
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
before(:example) do
|
|
674
|
+
allow(SleepingKingStudios::Tools::Toolbelt.instance.core_tools)
|
|
675
|
+
.to receive(:deprecate)
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
define_method :create_collection do |safe: true, **options|
|
|
679
|
+
if safe
|
|
680
|
+
begin
|
|
681
|
+
repository.find_or_create(**collection_options, **options)
|
|
682
|
+
rescue StandardError
|
|
683
|
+
# Do nothing.
|
|
684
|
+
end
|
|
685
|
+
else
|
|
686
|
+
repository.find_or_create(**collection_options, **options)
|
|
687
|
+
end
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
it 'should define the method' do
|
|
691
|
+
expect(repository)
|
|
692
|
+
.to respond_to(:find_or_create)
|
|
693
|
+
.with(0).arguments
|
|
694
|
+
.and_keywords(:entity_class)
|
|
695
|
+
.and_any_keywords
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
if deferred_options.fetch(:abstract, false)
|
|
699
|
+
let(:collection_options) { { name: collection_name } }
|
|
700
|
+
|
|
701
|
+
it 'should raise an exception' do
|
|
702
|
+
expect { create_collection(safe: false) }
|
|
703
|
+
.to raise_error(
|
|
704
|
+
described_class::AbstractRepositoryError,
|
|
705
|
+
error_message
|
|
706
|
+
)
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
next
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
it 'should print a deprecation warning' do
|
|
713
|
+
repository.find_or_create(qualified_name:)
|
|
714
|
+
|
|
715
|
+
expect(SleepingKingStudios::Tools::Toolbelt.instance.core_tools)
|
|
716
|
+
.to have_received(:deprecate)
|
|
717
|
+
.with(
|
|
718
|
+
"#{described_class.name}#find_or_create()",
|
|
719
|
+
message: 'Use #create or #find method.'
|
|
720
|
+
)
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
describe 'with entity_class: a Class' do
|
|
724
|
+
let(:entity_class) { Book }
|
|
725
|
+
let(:collection_options) do
|
|
726
|
+
super().merge(entity_class:)
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
include_deferred 'should create the collection'
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
describe 'with entity_class: a String' do
|
|
733
|
+
let(:entity_class) { Book }
|
|
734
|
+
let(:collection_options) do
|
|
735
|
+
super().merge(entity_class:)
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
include_deferred 'should create the collection'
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
describe 'with name: a String' do
|
|
742
|
+
let(:collection_name) { 'books' }
|
|
743
|
+
let(:collection_options) do
|
|
744
|
+
super().merge(name: collection_name)
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
include_deferred 'should create the collection'
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
describe 'with name: a Symbol' do
|
|
751
|
+
let(:collection_name) { :books }
|
|
752
|
+
let(:collection_options) do
|
|
753
|
+
super().merge(name: collection_name)
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
include_deferred 'should create the collection'
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
describe 'with collection options' do
|
|
760
|
+
let(:primary_key_name) { 'uuid' }
|
|
761
|
+
let(:primary_key_type) { String }
|
|
762
|
+
let(:qualified_name) { 'spec/scoped_books' }
|
|
763
|
+
let(:collection_options) do
|
|
764
|
+
super().merge(
|
|
765
|
+
name: collection_name,
|
|
766
|
+
primary_key_name:,
|
|
767
|
+
primary_key_type:,
|
|
768
|
+
qualified_name:
|
|
769
|
+
)
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
include_deferred 'should create the collection'
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
context 'when the collection already exists' do
|
|
776
|
+
let(:collection_name) { 'books' }
|
|
777
|
+
let(:collection_options) do
|
|
778
|
+
super().merge(name: collection_name)
|
|
779
|
+
end
|
|
780
|
+
let(:error_message) do
|
|
781
|
+
"collection #{qualified_name} already exists"
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
before { create_collection(old: true) }
|
|
785
|
+
|
|
786
|
+
describe 'with non-matching options' do
|
|
787
|
+
it 'should raise an exception' do
|
|
788
|
+
expect { create_collection(old: false, safe: false) }
|
|
789
|
+
.to raise_error(
|
|
790
|
+
described_class::DuplicateCollectionError,
|
|
791
|
+
error_message
|
|
792
|
+
)
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
it 'should not update the repository' do
|
|
796
|
+
create_collection(old: false)
|
|
797
|
+
|
|
798
|
+
collection = repository[qualified_name]
|
|
799
|
+
|
|
800
|
+
expect(collection.options[:old]).to be true
|
|
801
|
+
end
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
describe 'with matching options' do
|
|
805
|
+
it 'should return the collection' do
|
|
806
|
+
collection = create_collection(old: true)
|
|
807
|
+
|
|
808
|
+
expect(collection.options[:old]).to be true
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
context 'when the repository is frozen' do
|
|
814
|
+
let(:collection_name) { 'books' }
|
|
815
|
+
let(:collection_options) do
|
|
816
|
+
super().merge(name: collection_name)
|
|
817
|
+
end
|
|
818
|
+
let(:error_message) do
|
|
819
|
+
"can't modify frozen #{described_class.name}"
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
before(:example) { repository.freeze }
|
|
823
|
+
|
|
824
|
+
it 'should raise an exception' do
|
|
825
|
+
expect { create_collection(safe: false) }
|
|
826
|
+
.to raise_error FrozenError, error_message
|
|
827
|
+
end
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
describe '#key?' do
|
|
832
|
+
it { expect(repository).to respond_to(:key?).with(1).argument }
|
|
833
|
+
|
|
834
|
+
it { expect(repository.key?(nil)).to be false }
|
|
835
|
+
|
|
836
|
+
it { expect(repository.key?(Object.new.freeze)).to be false }
|
|
837
|
+
|
|
838
|
+
it { expect(repository.key?('invalid_name')).to be false }
|
|
839
|
+
|
|
840
|
+
it { expect(repository.key?(:invalid_name)).to be false }
|
|
841
|
+
|
|
842
|
+
wrap_deferred 'when the repository has many collections' do
|
|
843
|
+
it { expect(repository.key?('invalid_name')).to be false }
|
|
844
|
+
|
|
845
|
+
it { expect(repository.key?(:invalid_name)).to be false }
|
|
846
|
+
|
|
847
|
+
it { expect(repository.key?(collections.keys.first)).to be true }
|
|
848
|
+
|
|
849
|
+
it 'should include the key' do
|
|
850
|
+
expect(repository.key?(collections.keys.first.intern)).to be true
|
|
851
|
+
end
|
|
852
|
+
end
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
describe '#keys' do
|
|
856
|
+
include_examples 'should define reader', :keys, []
|
|
857
|
+
|
|
858
|
+
wrap_deferred 'when the repository has many collections' do
|
|
859
|
+
it { expect(repository.keys).to be == collections.keys }
|
|
860
|
+
end
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
describe '#remove' do
|
|
864
|
+
let(:qualified_name) { 'books' }
|
|
865
|
+
|
|
866
|
+
it 'should define the method' do
|
|
867
|
+
expect(repository)
|
|
868
|
+
.to respond_to(:remove)
|
|
869
|
+
.with(0).arguments
|
|
870
|
+
.and_keywords(:qualified_name)
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
describe 'with qualified_name: a String' do
|
|
874
|
+
let(:qualified_name) { super().to_s }
|
|
875
|
+
|
|
876
|
+
context 'when the collection does not exist' do
|
|
877
|
+
let(:error_message) do
|
|
878
|
+
"repository does not define collection #{qualified_name.inspect}"
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
it 'should raise an exception' do
|
|
882
|
+
expect { repository.remove(qualified_name:) }.to raise_error(
|
|
883
|
+
described_class::UndefinedCollectionError,
|
|
884
|
+
error_message
|
|
885
|
+
)
|
|
886
|
+
end
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
next if deferred_options.fetch(:abstract, false)
|
|
890
|
+
|
|
891
|
+
context 'when the collection exists' do
|
|
892
|
+
include_deferred 'when the repository has many collections'
|
|
893
|
+
|
|
894
|
+
let(:collection) { collections.values.first }
|
|
895
|
+
let(:qualified_name) { collection.qualified_name.to_s }
|
|
896
|
+
|
|
897
|
+
it 'should return the collection' do
|
|
898
|
+
expect(repository.remove(qualified_name:)).to be == collection
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
it 'should remove the collection from the repository' do
|
|
902
|
+
expect { repository.remove(qualified_name:) }
|
|
903
|
+
.to change(repository, :keys)
|
|
904
|
+
.to(satisfy { |keys| !keys.include?(qualified_name.to_s) })
|
|
905
|
+
end
|
|
906
|
+
end
|
|
907
|
+
end
|
|
908
|
+
|
|
909
|
+
describe 'with qualified_name: a Symbol' do
|
|
910
|
+
let(:qualified_name) { super().intern }
|
|
911
|
+
|
|
912
|
+
context 'when the collection does not exist' do
|
|
913
|
+
let(:error_message) do
|
|
914
|
+
'repository does not define collection ' \
|
|
915
|
+
"#{qualified_name.to_s.inspect}"
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
it 'should raise an exception' do
|
|
919
|
+
expect { repository.remove(qualified_name:) }.to raise_error(
|
|
920
|
+
described_class::UndefinedCollectionError,
|
|
921
|
+
error_message
|
|
922
|
+
)
|
|
923
|
+
end
|
|
924
|
+
end
|
|
925
|
+
|
|
926
|
+
next if deferred_options.fetch(:abstract, false)
|
|
927
|
+
|
|
928
|
+
context 'when the collection exists' do
|
|
929
|
+
include_deferred 'when the repository has many collections'
|
|
930
|
+
|
|
931
|
+
let(:collection) { collections.values.first }
|
|
932
|
+
let(:qualified_name) { collection.qualified_name.intern }
|
|
933
|
+
|
|
934
|
+
it 'should return the collection' do
|
|
935
|
+
expect(repository.remove(qualified_name:)).to be == collection
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
it 'should remove the collection from the repository' do
|
|
939
|
+
expect { repository.remove(qualified_name:) }
|
|
940
|
+
.to change(repository, :keys)
|
|
941
|
+
.to(satisfy { |keys| !keys.include?(qualified_name.to_s) })
|
|
942
|
+
end
|
|
943
|
+
end
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
context 'when the repository is frozen' do
|
|
947
|
+
let(:error_message) do
|
|
948
|
+
"can't modify frozen #{described_class.name}"
|
|
949
|
+
end
|
|
950
|
+
|
|
951
|
+
before(:example) { repository.freeze }
|
|
952
|
+
|
|
953
|
+
it 'should raise an exception' do
|
|
954
|
+
expect { repository.remove(qualified_name:) }
|
|
955
|
+
.to raise_error FrozenError, error_message
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
end
|
|
959
|
+
end
|
|
960
|
+
end
|
|
961
|
+
end
|