cuprum-collections 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -0
  3. data/DEVELOPMENT.md +2 -2
  4. data/README.md +13 -11
  5. data/lib/cuprum/collections/association.rb +256 -0
  6. data/lib/cuprum/collections/associations/belongs_to.rb +32 -0
  7. data/lib/cuprum/collections/associations/has_many.rb +23 -0
  8. data/lib/cuprum/collections/associations/has_one.rb +23 -0
  9. data/lib/cuprum/collections/associations.rb +10 -0
  10. data/lib/cuprum/collections/basic/collection.rb +39 -74
  11. data/lib/cuprum/collections/basic/commands/find_many.rb +1 -1
  12. data/lib/cuprum/collections/basic/commands/find_matching.rb +1 -1
  13. data/lib/cuprum/collections/basic/repository.rb +9 -33
  14. data/lib/cuprum/collections/basic.rb +1 -0
  15. data/lib/cuprum/collections/collection.rb +154 -0
  16. data/lib/cuprum/collections/commands/associations/find_many.rb +161 -0
  17. data/lib/cuprum/collections/commands/associations/require_many.rb +48 -0
  18. data/lib/cuprum/collections/commands/associations.rb +13 -0
  19. data/lib/cuprum/collections/commands/find_one_matching.rb +1 -1
  20. data/lib/cuprum/collections/commands.rb +1 -0
  21. data/lib/cuprum/collections/errors/abstract_find_error.rb +1 -1
  22. data/lib/cuprum/collections/relation.rb +401 -0
  23. data/lib/cuprum/collections/repository.rb +71 -4
  24. data/lib/cuprum/collections/resource.rb +65 -0
  25. data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +2137 -0
  26. data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +484 -0
  27. data/lib/cuprum/collections/rspec/contracts/basic.rb +11 -0
  28. data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +429 -0
  29. data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +1462 -0
  30. data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +1093 -0
  31. data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +1381 -0
  32. data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +605 -0
  33. data/lib/cuprum/collections/rspec/contracts.rb +23 -0
  34. data/lib/cuprum/collections/rspec/fixtures.rb +85 -82
  35. data/lib/cuprum/collections/rspec.rb +4 -1
  36. data/lib/cuprum/collections/version.rb +1 -1
  37. data/lib/cuprum/collections.rb +9 -4
  38. metadata +23 -19
  39. data/lib/cuprum/collections/base.rb +0 -11
  40. data/lib/cuprum/collections/basic/rspec/command_contract.rb +0 -392
  41. data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +0 -168
  42. data/lib/cuprum/collections/rspec/build_one_command_contract.rb +0 -93
  43. data/lib/cuprum/collections/rspec/collection_contract.rb +0 -190
  44. data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +0 -108
  45. data/lib/cuprum/collections/rspec/find_many_command_contract.rb +0 -407
  46. data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +0 -194
  47. data/lib/cuprum/collections/rspec/find_one_command_contract.rb +0 -157
  48. data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +0 -84
  49. data/lib/cuprum/collections/rspec/query_builder_contract.rb +0 -92
  50. data/lib/cuprum/collections/rspec/query_contract.rb +0 -650
  51. data/lib/cuprum/collections/rspec/querying_contract.rb +0 -298
  52. data/lib/cuprum/collections/rspec/repository_contract.rb +0 -235
  53. data/lib/cuprum/collections/rspec/update_one_command_contract.rb +0 -80
  54. data/lib/cuprum/collections/rspec/validate_one_command_contract.rb +0 -96
@@ -1,392 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cuprum/collections/basic/rspec'
4
-
5
- module Cuprum::Collections::Basic::RSpec
6
- # Contract validating the behavior of a basic command implementation.
7
- COMMAND_CONTRACT = lambda do
8
- describe '.subclass' do
9
- let(:subclass) { described_class.subclass }
10
- let(:constructor_options) do
11
- {
12
- collection_name: 'books',
13
- data: data,
14
- optional_key: 'optional value'
15
- }
16
- end
17
-
18
- it 'should define the class method' do
19
- expect(described_class)
20
- .to respond_to(:subclass)
21
- .with(0).arguments
22
- .and_any_keywords
23
- end
24
-
25
- it { expect(subclass).to be_a Class }
26
-
27
- it { expect(subclass).to be < described_class }
28
-
29
- it 'should define the constructor' do
30
- expect(subclass)
31
- .to respond_to(:new)
32
- .with(0).arguments
33
- .and_any_keywords
34
- end
35
-
36
- it 'should return the collection name' do
37
- expect(subclass.new(**constructor_options).collection_name)
38
- .to be collection_name
39
- end
40
-
41
- it 'should return the data' do
42
- expect(subclass.new(**constructor_options).data)
43
- .to be data
44
- end
45
-
46
- it 'should return the options' do
47
- expect(subclass.new(**constructor_options).options)
48
- .to be == { optional_key: 'optional value' }
49
- end
50
-
51
- describe 'with options' do
52
- let(:default_options) do
53
- {
54
- collection_name: 'books',
55
- custom_key: 'custom value'
56
- }
57
- end
58
- let(:constructor_options) do
59
- {
60
- data: data,
61
- optional_key: 'optional value'
62
- }
63
- end
64
- let(:subclass) { described_class.subclass(**default_options) }
65
-
66
- it { expect(subclass).to be_a Class }
67
-
68
- it { expect(subclass).to be < described_class }
69
-
70
- it 'should define the constructor' do
71
- expect(subclass)
72
- .to respond_to(:new)
73
- .with(0).arguments
74
- .and_any_keywords
75
- end
76
-
77
- it 'should return the collection name' do
78
- expect(subclass.new(**constructor_options).collection_name)
79
- .to be collection_name
80
- end
81
-
82
- it 'should return the data' do
83
- expect(subclass.new(**constructor_options).data)
84
- .to be data
85
- end
86
-
87
- it 'should return the options' do
88
- expect(subclass.new(**constructor_options).options)
89
- .to be == {
90
- custom_key: 'custom value',
91
- optional_key: 'optional value'
92
- }
93
- end
94
- end
95
- end
96
-
97
- describe '#collection_name' do
98
- include_examples 'should have reader',
99
- :collection_name,
100
- -> { collection_name }
101
-
102
- context 'when initialized with collection_name: symbol' do
103
- let(:collection_name) { :books }
104
-
105
- it { expect(command.collection_name).to be == collection_name.to_s }
106
- end
107
- end
108
-
109
- describe '#data' do
110
- include_examples 'should define reader', :data, -> { data }
111
- end
112
-
113
- describe '#default_contract' do
114
- include_examples 'should define reader', :default_contract, nil
115
-
116
- context 'when initialized with a default contract' do
117
- let(:default_contract) { Stannum::Contract.new }
118
- let(:constructor_options) do
119
- super().merge(default_contract: default_contract)
120
- end
121
-
122
- it { expect(command.default_contract).to be default_contract }
123
- end
124
- end
125
-
126
- describe '#member_name' do
127
- def tools
128
- SleepingKingStudios::Tools::Toolbelt.instance
129
- end
130
-
131
- include_examples 'should have reader',
132
- :member_name,
133
- -> { tools.str.singularize(collection_name) }
134
-
135
- context 'when initialized with collection_name: value' do
136
- let(:collection_name) { :books }
137
-
138
- it 'should return the singular collection name' do
139
- expect(command.member_name)
140
- .to be == tools.str.singularize(collection_name.to_s)
141
- end
142
- end
143
-
144
- context 'when initialized with member_name: string' do
145
- let(:member_name) { 'tome' }
146
- let(:constructor_options) { super().merge(member_name: member_name) }
147
-
148
- it 'should return the singular collection name' do
149
- expect(command.member_name).to be member_name
150
- end
151
- end
152
-
153
- context 'when initialized with member_name: symbol' do
154
- let(:member_name) { :tome }
155
- let(:constructor_options) { super().merge(member_name: member_name) }
156
-
157
- it 'should return the singular collection name' do
158
- expect(command.member_name).to be == member_name.to_s
159
- end
160
- end
161
- end
162
-
163
- describe '#options' do
164
- let(:expected_options) do
165
- defined?(super()) ? super() : constructor_options
166
- end
167
-
168
- include_examples 'should define reader',
169
- :options,
170
- -> { be == expected_options }
171
-
172
- context 'when initialized with options' do
173
- let(:constructor_options) { super().merge({ key: 'value' }) }
174
- let(:expected_options) { super().merge({ key: 'value' }) }
175
-
176
- it { expect(command.options).to be == expected_options }
177
- end
178
- end
179
-
180
- describe '#primary_key_name' do
181
- include_examples 'should define reader', :primary_key_name, :id
182
-
183
- context 'when initialized with a primary key name' do
184
- let(:primary_key_name) { :uuid }
185
- let(:constructor_options) do
186
- super().merge({ primary_key_name: primary_key_name })
187
- end
188
-
189
- it { expect(command.primary_key_name).to be == primary_key_name }
190
- end
191
- end
192
-
193
- describe '#primary_key_type' do
194
- include_examples 'should define reader', :primary_key_type, Integer
195
-
196
- context 'when initialized with a primary key type' do
197
- let(:primary_key_type) { String }
198
- let(:constructor_options) do
199
- super().merge({ primary_key_type: primary_key_type })
200
- end
201
-
202
- it { expect(command.primary_key_type).to be == primary_key_type }
203
- end
204
- end
205
-
206
- describe '#validate_primary_key' do
207
- let(:primary_key_type) { Integer }
208
- let(:expected_error) do
209
- type = primary_key_type
210
- contract = Stannum::Contracts::ParametersContract.new do
211
- keyword :primary_key, type
212
- end
213
- errors = contract.errors_for(
214
- {
215
- arguments: [],
216
- block: nil,
217
- keywords: { primary_key: nil }
218
- }
219
- )
220
-
221
- Cuprum::Collections::Errors::InvalidParameters.new(
222
- command: command,
223
- errors: errors
224
- )
225
- end
226
-
227
- it 'should define the private method' do
228
- expect(command)
229
- .to respond_to(:validate_primary_key, true)
230
- .with(1).argument
231
- end
232
-
233
- describe 'with nil' do
234
- it 'should return a failing result' do
235
- expect(command.send(:validate_primary_key, nil))
236
- .to be_a_failing_result
237
- .with_error(expected_error)
238
- end
239
- end
240
-
241
- describe 'with an Object' do
242
- it 'should return a failing result' do
243
- expect(command.send(:validate_primary_key, Object.new.freeze))
244
- .to be_a_failing_result
245
- .with_error(expected_error)
246
- end
247
- end
248
-
249
- describe 'with a String' do
250
- it 'should return a failing result' do
251
- expect(command.send(:validate_primary_key, '12345'))
252
- .to be_a_failing_result
253
- .with_error(expected_error)
254
- end
255
- end
256
-
257
- describe 'with an Integer' do
258
- it 'should not return a result' do
259
- expect(command.send(:validate_primary_key, 12_345)).not_to be_a_result
260
- end
261
- end
262
-
263
- context 'when initialized with a primary key type' do
264
- let(:primary_key_type) { String }
265
- let(:constructor_options) do
266
- super().merge({ primary_key_type: primary_key_type })
267
- end
268
-
269
- describe 'with an Integer' do
270
- it 'should return a failing result' do
271
- expect(command.send(:validate_primary_key, 12_345))
272
- .to be_a_failing_result
273
- .with_error(expected_error)
274
- end
275
- end
276
-
277
- describe 'with a String' do
278
- it 'should not return a result' do
279
- expect(command.send(:validate_primary_key, '12345'))
280
- .not_to be_a_result
281
- end
282
- end
283
- end
284
- end
285
-
286
- describe '#validate_primary_keys' do
287
- let(:primary_keys) { nil }
288
- let(:primary_key_type) { Integer }
289
- let(:expected_error) do
290
- type = primary_key_type
291
- contract = Stannum::Contracts::ParametersContract.new do
292
- keyword :primary_keys,
293
- Stannum::Constraints::Types::ArrayType.new(item_type: type)
294
- end
295
- errors = contract.errors_for(
296
- {
297
- arguments: [],
298
- block: nil,
299
- keywords: { primary_keys: primary_keys }
300
- }
301
- )
302
-
303
- Cuprum::Collections::Errors::InvalidParameters.new(
304
- command: command,
305
- errors: errors
306
- )
307
- end
308
-
309
- it 'should define the private method' do
310
- expect(command)
311
- .to respond_to(:validate_primary_keys, true)
312
- .with(1).argument
313
- end
314
-
315
- describe 'with nil' do
316
- it 'should return a failing result' do
317
- expect(command.send(:validate_primary_keys, nil))
318
- .to be_a_failing_result
319
- .with_error(expected_error)
320
- end
321
- end
322
-
323
- describe 'with an Object' do
324
- it 'should return a failing result' do
325
- expect(command.send(:validate_primary_keys, Object.new.freeze))
326
- .to be_a_failing_result
327
- .with_error(expected_error)
328
- end
329
- end
330
-
331
- describe 'with a String' do
332
- it 'should return a failing result' do
333
- expect(command.send(:validate_primary_keys, '12345'))
334
- .to be_a_failing_result
335
- .with_error(expected_error)
336
- end
337
- end
338
-
339
- describe 'with an Integer' do
340
- it 'should return a failing result' do
341
- expect(command.send(:validate_primary_keys, 12_345))
342
- .to be_a_failing_result
343
- .with_error(expected_error)
344
- end
345
- end
346
-
347
- describe 'with an empty Array' do
348
- it 'should not return a result' do
349
- expect(command.send(:validate_primary_keys, []))
350
- .not_to be_a_result
351
- end
352
- end
353
-
354
- describe 'with an Array with nil values' do
355
- let(:primary_keys) { Array.new(3, nil) }
356
-
357
- it 'should return a failing result' do
358
- expect(command.send(:validate_primary_keys, primary_keys))
359
- .to be_a_failing_result
360
- .with_error(expected_error)
361
- end
362
- end
363
-
364
- describe 'with an Array with Object values' do
365
- let(:primary_keys) { Array.new(3) { Object.new.freeze } }
366
-
367
- it 'should return a failing result' do
368
- expect(command.send(:validate_primary_keys, primary_keys))
369
- .to be_a_failing_result
370
- .with_error(expected_error)
371
- end
372
- end
373
-
374
- describe 'with an Array with String values' do
375
- let(:primary_keys) { %w[ichi ni san] }
376
-
377
- it 'should return a failing result' do
378
- expect(command.send(:validate_primary_keys, primary_keys))
379
- .to be_a_failing_result
380
- .with_error(expected_error)
381
- end
382
- end
383
-
384
- describe 'with an Array with Integer values' do
385
- it 'should not return a result' do
386
- expect(command.send(:validate_primary_keys, [0, 1, 2]))
387
- .not_to be_a_result
388
- end
389
- end
390
- end
391
- end
392
- end
@@ -1,168 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'stannum/constraints/presence'
4
- require 'stannum/constraints/types/hash_with_indifferent_keys'
5
- require 'stannum/rspec/validate_parameter'
6
-
7
- require 'cuprum/collections/rspec'
8
-
9
- module Cuprum::Collections::RSpec
10
- # Contract validating the behavior of an Assign command implementation.
11
- ASSIGN_ONE_COMMAND_CONTRACT = lambda do |allow_extra_attributes:|
12
- describe '#call' do
13
- shared_examples 'should assign the attributes' do
14
- it { expect(result).to be_a_passing_result }
15
-
16
- it { expect(result.value).to be_a entity.class }
17
-
18
- it { expect(result.value).to be == expected_value }
19
- end
20
-
21
- let(:attributes) { {} }
22
- let(:result) { command.call(attributes: attributes, entity: entity) }
23
- let(:expected_attributes) do
24
- initial_attributes.merge(attributes)
25
- end
26
- let(:expected_value) do
27
- defined?(super()) ? super() : expected_attributes
28
- end
29
-
30
- it 'should validate the :attributes keyword' do
31
- expect(command)
32
- .to validate_parameter(:call, :attributes)
33
- .using_constraint(
34
- Stannum::Constraints::Types::HashWithIndifferentKeys.new
35
- )
36
- end
37
-
38
- it 'should validate the :entity keyword' do
39
- expect(command)
40
- .to validate_parameter(:call, :entity)
41
- .using_constraint(entity_type)
42
- .with_parameters(attributes: {}, entity: nil)
43
- end
44
-
45
- describe 'with an empty attributes hash' do
46
- let(:attributes) { {} }
47
-
48
- include_examples 'should assign the attributes'
49
- end
50
-
51
- describe 'with an attributes hash with partial attributes' do
52
- let(:attributes) { { title: 'Gideon the Ninth' } }
53
-
54
- include_examples 'should assign the attributes'
55
- end
56
-
57
- describe 'with an attributes hash with full attributes' do
58
- let(:attributes) do
59
- {
60
- title: 'Gideon the Ninth',
61
- author: 'Tamsyn Muir',
62
- series: 'The Locked Tomb',
63
- category: 'Horror'
64
- }
65
- end
66
-
67
- include_examples 'should assign the attributes'
68
- end
69
-
70
- describe 'with an attributes hash with extra attributes' do
71
- let(:attributes) do
72
- {
73
- title: 'The Book of Lost Tales',
74
- audiobook: true
75
- }
76
- end
77
-
78
- if allow_extra_attributes
79
- include_examples 'should assign the attributes'
80
- else
81
- # :nocov:
82
- let(:valid_attributes) do
83
- defined?(super()) ? super() : expected_attributes.keys
84
- end
85
- let(:expected_error) do
86
- Cuprum::Collections::Errors::ExtraAttributes.new(
87
- entity_class: entity.class,
88
- extra_attributes: %w[audiobook],
89
- valid_attributes: valid_attributes
90
- )
91
- end
92
-
93
- it 'should return a failing result' do
94
- expect(result).to be_a_failing_result.with_error(expected_error)
95
- end
96
- # :nocov:
97
- end
98
- end
99
-
100
- context 'when the entity has existing attributes' do
101
- let(:initial_attributes) do
102
- # :nocov:
103
- if defined?(super())
104
- super().merge(fixtures_data.first)
105
- else
106
- fixtures_data.first
107
- end
108
- # :nocov:
109
- end
110
-
111
- describe 'with an empty attributes hash' do
112
- let(:attributes) { {} }
113
-
114
- include_examples 'should assign the attributes'
115
- end
116
-
117
- describe 'with an attributes hash with partial attributes' do
118
- let(:attributes) { { title: 'Gideon the Ninth' } }
119
-
120
- include_examples 'should assign the attributes'
121
- end
122
-
123
- describe 'with an attributes hash with full attributes' do
124
- let(:attributes) do
125
- {
126
- title: 'Gideon the Ninth',
127
- author: 'Tamsyn Muir',
128
- series: 'The Locked Tomb',
129
- category: 'Horror'
130
- }
131
- end
132
-
133
- include_examples 'should assign the attributes'
134
- end
135
-
136
- describe 'with an attributes hash with extra attributes' do
137
- let(:attributes) do
138
- {
139
- title: 'The Book of Lost Tales',
140
- audiobook: true
141
- }
142
- end
143
-
144
- if allow_extra_attributes
145
- include_examples 'should assign the attributes'
146
- else
147
- # :nocov:
148
- let(:valid_attributes) do
149
- defined?(super()) ? super() : expected_attributes.keys
150
- end
151
- let(:expected_error) do
152
- Cuprum::Collections::Errors::ExtraAttributes.new(
153
- entity_class: entity.class,
154
- extra_attributes: %w[audiobook],
155
- valid_attributes: valid_attributes
156
- )
157
- end
158
-
159
- it 'should return a failing result' do
160
- expect(result).to be_a_failing_result.with_error(expected_error)
161
- end
162
- # :nocov:
163
- end
164
- end
165
- end
166
- end
167
- end
168
- end
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'stannum/constraints/types/hash_with_indifferent_keys'
4
- require 'stannum/rspec/validate_parameter'
5
-
6
- require 'cuprum/collections/rspec'
7
-
8
- module Cuprum::Collections::RSpec
9
- # Contract validating the behavior of a Build command implementation.
10
- BUILD_ONE_COMMAND_CONTRACT = lambda do |allow_extra_attributes:|
11
- include Stannum::RSpec::Matchers
12
-
13
- describe '#call' do
14
- shared_examples 'should build the entity' do
15
- it { expect(result).to be_a_passing_result }
16
-
17
- it { expect(result.value).to be == expected_value }
18
- end
19
-
20
- let(:attributes) { {} }
21
- let(:result) { command.call(attributes: attributes) }
22
- let(:expected_attributes) do
23
- attributes
24
- end
25
- let(:expected_value) do
26
- defined?(super()) ? super() : attributes
27
- end
28
-
29
- it 'should validate the :attributes keyword' do
30
- expect(command)
31
- .to validate_parameter(:call, :attributes)
32
- .using_constraint(
33
- Stannum::Constraints::Types::HashWithIndifferentKeys.new
34
- )
35
- end
36
-
37
- describe 'with an empty attributes hash' do
38
- let(:attributes) { {} }
39
-
40
- include_examples 'should build the entity'
41
- end
42
-
43
- describe 'with an attributes hash with partial attributes' do
44
- let(:attributes) { { title: 'Gideon the Ninth' } }
45
-
46
- include_examples 'should build the entity'
47
- end
48
-
49
- describe 'with an attributes hash with full attributes' do
50
- let(:attributes) do
51
- {
52
- title: 'Gideon the Ninth',
53
- author: 'Tamsyn Muir',
54
- series: 'The Locked Tomb',
55
- category: 'Horror'
56
- }
57
- end
58
-
59
- include_examples 'should build the entity'
60
- end
61
-
62
- describe 'with an attributes hash with extra attributes' do
63
- let(:attributes) do
64
- {
65
- title: 'The Book of Lost Tales',
66
- audiobook: true
67
- }
68
- end
69
-
70
- if allow_extra_attributes
71
- include_examples 'should build the entity'
72
- else
73
- # :nocov:
74
- let(:valid_attributes) do
75
- defined?(super()) ? super() : expected_attributes.keys
76
- end
77
- let(:expected_error) do
78
- Cuprum::Collections::Errors::ExtraAttributes.new(
79
- entity_class: entity_type,
80
- extra_attributes: %w[audiobook],
81
- valid_attributes: valid_attributes
82
- )
83
- end
84
-
85
- it 'should return a failing result' do
86
- expect(result).to be_a_failing_result.with_error(expected_error)
87
- end
88
- # :nocov:
89
- end
90
- end
91
- end
92
- end
93
- end