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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/lib/cuprum/collections/adaptable/collection.rb +18 -0
  4. data/lib/cuprum/collections/adaptable/command.rb +22 -0
  5. data/lib/cuprum/collections/adaptable/commands/abstract_assign_one.rb +27 -0
  6. data/lib/cuprum/collections/adaptable/commands/abstract_build_one.rb +25 -0
  7. data/lib/cuprum/collections/adaptable/commands/abstract_validate_one.rb +35 -0
  8. data/lib/cuprum/collections/adaptable/commands.rb +15 -0
  9. data/lib/cuprum/collections/adaptable/query.rb +64 -0
  10. data/lib/cuprum/collections/adaptable.rb +13 -0
  11. data/lib/cuprum/collections/adapter.rb +300 -0
  12. data/lib/cuprum/collections/adapters/data_adapter.rb +82 -0
  13. data/lib/cuprum/collections/adapters/entity_adapter.rb +76 -0
  14. data/lib/cuprum/collections/adapters/hash_adapter.rb +48 -0
  15. data/lib/cuprum/collections/adapters.rb +14 -0
  16. data/lib/cuprum/collections/basic/collection.rb +2 -20
  17. data/lib/cuprum/collections/basic/commands/destroy_one.rb +1 -1
  18. data/lib/cuprum/collections/basic/commands/find_many.rb +0 -31
  19. data/lib/cuprum/collections/basic/commands/find_matching.rb +0 -94
  20. data/lib/cuprum/collections/basic/commands/find_one.rb +0 -18
  21. data/lib/cuprum/collections/basic/commands/insert_one.rb +1 -1
  22. data/lib/cuprum/collections/basic/commands/update_one.rb +1 -1
  23. data/lib/cuprum/collections/basic/scopes/criteria_scope.rb +36 -21
  24. data/lib/cuprum/collections/basic.rb +6 -5
  25. data/lib/cuprum/collections/collection.rb +6 -0
  26. data/lib/cuprum/collections/collection_command.rb +1 -1
  27. data/lib/cuprum/collections/commands/abstract_find_many.rb +40 -3
  28. data/lib/cuprum/collections/commands/abstract_find_matching.rb +102 -0
  29. data/lib/cuprum/collections/commands/abstract_find_one.rb +23 -1
  30. data/lib/cuprum/collections/commands/associations/find_many.rb +1 -3
  31. data/lib/cuprum/collections/commands/associations/require_many.rb +1 -1
  32. data/lib/cuprum/collections/commands/find_one_matching.rb +10 -10
  33. data/lib/cuprum/collections/commands/query_command.rb +6 -4
  34. data/lib/cuprum/collections/commands/upsert.rb +0 -2
  35. data/lib/cuprum/collections/constraints/order/attributes_array.rb +5 -4
  36. data/lib/cuprum/collections/constraints/order/attributes_hash.rb +5 -4
  37. data/lib/cuprum/collections/constraints/order/sort_direction.rb +2 -2
  38. data/lib/cuprum/collections/constraints/ordering.rb +11 -9
  39. data/lib/cuprum/collections/constraints/query_hash.rb +2 -2
  40. data/lib/cuprum/collections/errors/abstract_find_error.rb +101 -23
  41. data/lib/cuprum/collections/errors/extra_attributes.rb +3 -3
  42. data/lib/cuprum/collections/errors/failed_validation.rb +3 -3
  43. data/lib/cuprum/collections/errors/missing_default_contract.rb +12 -4
  44. data/lib/cuprum/collections/queries.rb +4 -0
  45. data/lib/cuprum/collections/relation.rb +0 -2
  46. data/lib/cuprum/collections/relations/parameters.rb +120 -68
  47. data/lib/cuprum/collections/repository.rb +71 -6
  48. data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +23 -4
  49. data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +18 -0
  50. data/lib/cuprum/collections/rspec/contracts/scope_contracts.rb +51 -0
  51. data/lib/cuprum/collections/rspec/contracts/scopes/builder_contracts.rb +10 -0
  52. data/lib/cuprum/collections/rspec/contracts/scopes/composition_contracts.rb +8 -0
  53. data/lib/cuprum/collections/rspec/contracts/scopes/criteria_contracts.rb +18 -366
  54. data/lib/cuprum/collections/rspec/contracts/scopes/logical_contracts.rb +30 -0
  55. data/lib/cuprum/collections/rspec/contracts/scopes.rb +2 -0
  56. data/lib/cuprum/collections/rspec/contracts.rb +2 -10
  57. data/lib/cuprum/collections/rspec/deferred/adapter_examples.rb +1077 -0
  58. data/lib/cuprum/collections/rspec/deferred/collection_examples.rb +27 -7
  59. data/lib/cuprum/collections/rspec/deferred/commands/assign_one_examples.rb +4 -4
  60. data/lib/cuprum/collections/rspec/deferred/commands/build_one_examples.rb +2 -2
  61. data/lib/cuprum/collections/rspec/deferred/commands/destroy_one_examples.rb +2 -2
  62. data/lib/cuprum/collections/rspec/deferred/commands/find_many_examples.rb +5 -5
  63. data/lib/cuprum/collections/rspec/deferred/commands/find_matching_examples.rb +45 -12
  64. data/lib/cuprum/collections/rspec/deferred/commands/find_one_examples.rb +2 -2
  65. data/lib/cuprum/collections/rspec/deferred/commands/insert_one_examples.rb +1 -1
  66. data/lib/cuprum/collections/rspec/deferred/commands/update_one_examples.rb +1 -1
  67. data/lib/cuprum/collections/rspec/deferred/query_examples.rb +930 -0
  68. data/lib/cuprum/collections/rspec/deferred/relation_examples.rb +48 -17
  69. data/lib/cuprum/collections/rspec/deferred/repository_examples.rb +961 -0
  70. data/lib/cuprum/collections/rspec/deferred/scope_examples.rb +598 -0
  71. data/lib/cuprum/collections/rspec/deferred/scopes/all_examples.rb +391 -0
  72. data/lib/cuprum/collections/rspec/deferred/scopes/builder_examples.rb +857 -0
  73. data/lib/cuprum/collections/rspec/deferred/scopes/composition_examples.rb +93 -0
  74. data/lib/cuprum/collections/rspec/deferred/scopes/conjunction_examples.rb +438 -0
  75. data/lib/cuprum/collections/rspec/deferred/scopes/criteria_examples.rb +1941 -0
  76. data/lib/cuprum/collections/rspec/deferred/scopes/disjunction_examples.rb +415 -0
  77. data/lib/cuprum/collections/rspec/deferred/scopes/none_examples.rb +385 -0
  78. data/lib/cuprum/collections/rspec/deferred/scopes/parser_examples.rb +740 -0
  79. data/lib/cuprum/collections/rspec/deferred/scopes.rb +8 -0
  80. data/lib/cuprum/collections/scope.rb +2 -2
  81. data/lib/cuprum/collections/scopes/container.rb +5 -4
  82. data/lib/cuprum/collections/scopes/criteria/parser.rb +24 -48
  83. data/lib/cuprum/collections/scopes/criteria.rb +7 -6
  84. data/lib/cuprum/collections/version.rb +4 -4
  85. data/lib/cuprum/collections.rb +5 -1
  86. metadata +47 -11
  87. data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +0 -2127
  88. data/lib/cuprum/collections/rspec/contracts/basic.rb +0 -11
  89. data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +0 -387
  90. data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +0 -169
  91. data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +0 -1264
@@ -0,0 +1,415 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/sleeping_king_studios/deferred'
4
+
5
+ require 'cuprum/collections/rspec/deferred/scope_examples'
6
+ require 'cuprum/collections/rspec/deferred/scopes'
7
+ require 'cuprum/collections/rspec/deferred/scopes/composition_examples'
8
+
9
+ module Cuprum::Collections::RSpec::Deferred::Scopes
10
+ # Deferred examples for asserting on Conjunction scope objects.
11
+ module DisjunctionExamples
12
+ include RSpec::SleepingKingStudios::Deferred::Provider
13
+ include Cuprum::Collections::RSpec::Deferred::ScopeExamples
14
+ include Cuprum::Collections::RSpec::Deferred::Scopes::CompositionExamples
15
+
16
+ deferred_examples 'should implement the DisjunctionScope methods' \
17
+ do |**deferred_options|
18
+ deferred_context 'when the scope has many child scopes' do
19
+ let(:scopes) do
20
+ [
21
+ build_scope({ 'author' => 'J.R.R. Tolkien' }),
22
+ build_scope({ 'series' => 'The Lord of the Rings' }),
23
+ build_scope do |scope|
24
+ { 'published_at' => scope.less_than('1955-01-01') }
25
+ end
26
+ ]
27
+ end
28
+ end
29
+
30
+ include_deferred 'should implement the Scope methods'
31
+
32
+ include_deferred 'should define child scopes'
33
+
34
+ include_deferred 'should compose Scopes as a DisjunctionScope'
35
+
36
+ describe '#as_json' do
37
+ let(:expected) do
38
+ {
39
+ 'scopes' => subject.scopes.map(&:as_json),
40
+ 'type' => subject.type
41
+ }
42
+ end
43
+
44
+ it { expect(subject.as_json).to be == expected }
45
+
46
+ wrap_deferred 'with scopes' do
47
+ it { expect(subject.as_json).to be == expected }
48
+ end
49
+ end
50
+
51
+ describe '#call' do
52
+ next if deferred_options.fetch(:abstract, false)
53
+
54
+ include_deferred 'should filter data by logical OR'
55
+ end
56
+
57
+ describe '#invert' do
58
+ let(:expected) do
59
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
60
+ scopes: subject.scopes.map(&:invert)
61
+ )
62
+ end
63
+
64
+ it { expect(subject.invert).to be == expected }
65
+
66
+ wrap_deferred 'with scopes' do
67
+ it { expect(subject.invert).to be == expected }
68
+ end
69
+ end
70
+
71
+ describe '#type' do
72
+ include_examples 'should define reader', :type, :disjunction
73
+ end
74
+ end
75
+
76
+ deferred_examples 'should compose Scopes as a DisjunctionScope' do
77
+ describe '#and' do
78
+ include_deferred 'with contexts for composable scopes'
79
+
80
+ it 'should define the method' do
81
+ expect(subject)
82
+ .to respond_to(:and)
83
+ .with(0..1).arguments
84
+ .and_a_block
85
+ end
86
+
87
+ it { expect(subject).to have_aliased_method(:and).as(:where) }
88
+
89
+ describe 'with a block' do
90
+ let(:block) { -> { { 'title' => 'A Wizard of Earthsea' } } }
91
+ let(:expected) do
92
+ wrapped = Cuprum::Collections::Scope.new(&block)
93
+
94
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
95
+ scopes: [subject, wrapped]
96
+ )
97
+ end
98
+
99
+ it { expect(subject.and(&block)).to be == expected }
100
+ end
101
+
102
+ describe 'with a hash' do
103
+ let(:value) { { 'title' => 'A Wizard of Earthsea' } }
104
+ let(:expected) do
105
+ wrapped = Cuprum::Collections::Scope.new(value)
106
+
107
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
108
+ scopes: [subject, wrapped]
109
+ )
110
+ end
111
+
112
+ it { expect(subject.and(value)).to be == expected }
113
+ end
114
+
115
+ wrap_deferred 'with an all scope' do
116
+ it { expect(subject.and(original)).to be subject }
117
+ end
118
+
119
+ wrap_deferred 'with a none scope' do
120
+ it { expect(subject.and(original)).to be == original }
121
+ end
122
+
123
+ wrap_deferred 'with an empty conjunction scope' do
124
+ it { expect(subject.and(original)).to be subject }
125
+ end
126
+
127
+ wrap_deferred 'with an empty criteria scope' do
128
+ it { expect(subject.and(original)).to be subject }
129
+ end
130
+
131
+ wrap_deferred 'with an empty disjunction scope' do
132
+ it { expect(subject.and(original)).to be subject }
133
+ end
134
+
135
+ wrap_deferred 'with a non-empty conjunction scope' do
136
+ let(:expected) do
137
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
138
+ scopes: [subject, *original.scopes]
139
+ )
140
+ end
141
+
142
+ it { expect(subject.and(original)).to be == expected }
143
+ end
144
+
145
+ wrap_deferred 'with a non-empty criteria scope' do
146
+ let(:expected) do
147
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
148
+ scopes: [subject, original]
149
+ )
150
+ end
151
+
152
+ it { expect(subject.and(original)).to be == expected }
153
+ end
154
+
155
+ wrap_deferred 'with a non-empty disjunction scope' do
156
+ let(:expected) do
157
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
158
+ scopes: [subject, original]
159
+ )
160
+ end
161
+
162
+ it { expect(subject.and(original)).to be == expected }
163
+ end
164
+ end
165
+
166
+ describe '#not' do
167
+ include_deferred 'with contexts for composable scopes'
168
+
169
+ it 'should define the method' do
170
+ expect(subject)
171
+ .to respond_to(:not)
172
+ .with(0..1).arguments
173
+ .and_a_block
174
+ end
175
+
176
+ describe 'with a block' do
177
+ let(:block) { -> { { 'title' => 'A Wizard of Earthsea' } } }
178
+ let(:block) { -> { { 'title' => 'A Wizard of Earthsea' } } }
179
+ let(:expected) do
180
+ wrapped = Cuprum::Collections::Scope.new(&block)
181
+
182
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
183
+ scopes: [subject, wrapped.invert]
184
+ )
185
+ end
186
+
187
+ it { expect(subject.not(&block)).to be == expected }
188
+ end
189
+
190
+ describe 'with a hash' do
191
+ let(:value) { { 'title' => 'A Wizard of Earthsea' } }
192
+ let(:expected) do
193
+ wrapped = Cuprum::Collections::Scope.new(value)
194
+
195
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
196
+ scopes: [subject, wrapped.invert]
197
+ )
198
+ end
199
+
200
+ it { expect(subject.not(value)).to be == expected }
201
+ end
202
+
203
+ wrap_deferred 'with an all scope' do
204
+ let(:expected) { Cuprum::Collections::Scopes::NoneScope.new }
205
+
206
+ it { expect(subject.not(original)).to be == expected }
207
+ end
208
+
209
+ wrap_deferred 'with a none scope' do
210
+ it { expect(subject.not(original)).to be subject }
211
+ end
212
+
213
+ wrap_deferred 'with an empty conjunction scope' do
214
+ it { expect(subject.not(original)).to be subject }
215
+ end
216
+
217
+ wrap_deferred 'with an empty criteria scope' do
218
+ it { expect(subject.not(original)).to be subject }
219
+ end
220
+
221
+ wrap_deferred 'with an empty disjunction scope' do
222
+ it { expect(subject.not(original)).to be subject }
223
+ end
224
+
225
+ wrap_deferred 'with a non-empty conjunction scope' do
226
+ let(:expected) do
227
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
228
+ scopes: [subject, original.invert]
229
+ )
230
+ end
231
+
232
+ it { expect(subject.not(original)).to be == expected }
233
+ end
234
+
235
+ wrap_deferred 'with a non-empty criteria scope' do
236
+ let(:expected) do
237
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
238
+ scopes: [subject, original.invert]
239
+ )
240
+ end
241
+
242
+ it { expect(subject.not(original)).to be == expected }
243
+ end
244
+
245
+ wrap_deferred 'with a non-empty disjunction scope' do
246
+ let(:expected) do
247
+ Cuprum::Collections::Scopes::ConjunctionScope.new(
248
+ scopes: [subject, *original.invert.scopes]
249
+ )
250
+ end
251
+
252
+ it { expect(subject.not(original)).to be == expected }
253
+ end
254
+ end
255
+
256
+ describe '#or' do
257
+ include_deferred 'with contexts for composable scopes'
258
+
259
+ describe 'with a block' do
260
+ let(:block) { -> { { 'title' => 'A Wizard of Earthsea' } } }
261
+ let(:expected) do
262
+ wrapped = Cuprum::Collections::Scope.new(&block)
263
+
264
+ Cuprum::Collections::Scopes::DisjunctionScope.new(
265
+ scopes: [*subject.scopes, wrapped]
266
+ )
267
+ end
268
+
269
+ it { expect(subject.or(&block)).to be == expected }
270
+ end
271
+
272
+ describe 'with a hash' do
273
+ let(:value) { { 'title' => 'A Wizard of Earthsea' } }
274
+ let(:expected) do
275
+ wrapped = Cuprum::Collections::Scope.new(value)
276
+
277
+ Cuprum::Collections::Scopes::DisjunctionScope.new(
278
+ scopes: [*subject.scopes, wrapped]
279
+ )
280
+ end
281
+
282
+ it { expect(subject.or(value)).to be == expected }
283
+ end
284
+
285
+ wrap_deferred 'with an all scope' do
286
+ it { expect(subject.or(original)).to be == original }
287
+ end
288
+
289
+ wrap_deferred 'with a none scope' do
290
+ it { expect(subject.or(original)).to be subject }
291
+ end
292
+
293
+ wrap_deferred 'with an empty conjunction scope' do
294
+ it { expect(subject.or(original)).to be subject }
295
+ end
296
+
297
+ wrap_deferred 'with an empty criteria scope' do
298
+ it { expect(subject.or(original)).to be subject }
299
+ end
300
+
301
+ wrap_deferred 'with an empty disjunction scope' do
302
+ it { expect(subject.or(original)).to be subject }
303
+ end
304
+
305
+ wrap_deferred 'with a non-empty conjunction scope' do
306
+ let(:expected) do
307
+ Cuprum::Collections::Scopes::DisjunctionScope.new(
308
+ scopes: [*subject.scopes, original]
309
+ )
310
+ end
311
+
312
+ it { expect(subject.or(original)).to be == expected }
313
+ end
314
+
315
+ wrap_deferred 'with a non-empty criteria scope' do
316
+ let(:expected) do
317
+ Cuprum::Collections::Scopes::DisjunctionScope.new(
318
+ scopes: [*subject.scopes, original]
319
+ )
320
+ end
321
+
322
+ it { expect(subject.or(original)).to be == expected }
323
+ end
324
+
325
+ wrap_deferred 'with a non-empty disjunction scope' do
326
+ let(:expected) do
327
+ Cuprum::Collections::Scopes::DisjunctionScope.new(
328
+ scopes: [*subject.scopes, *original.scopes]
329
+ )
330
+ end
331
+
332
+ it { expect(subject.or(original)).to be == expected }
333
+
334
+ wrap_deferred 'when the scope has many child scopes' do
335
+ it { expect(subject.or(original)).to be == expected }
336
+ end
337
+ end
338
+ end
339
+ end
340
+
341
+ deferred_examples 'should filter data by logical OR' do
342
+ deferred_context 'with data' do
343
+ let(:data) do
344
+ Cuprum::Collections::RSpec::Fixtures::BOOKS_FIXTURES
345
+ end
346
+ end
347
+
348
+ context 'when the scope has no child scopes' do
349
+ let(:scopes) { [] }
350
+
351
+ describe 'with empty data' do
352
+ let(:data) { [] }
353
+
354
+ it { expect(filtered_data).to be == [] }
355
+ end
356
+
357
+ wrap_deferred 'with data' do
358
+ it { expect(filtered_data).to be == [] }
359
+ end
360
+ end
361
+
362
+ context 'when the scope has one child scope' do
363
+ let(:scopes) do
364
+ [
365
+ build_scope({ 'author' => 'J.R.R. Tolkien' })
366
+ ]
367
+ end
368
+
369
+ describe 'with empty data' do
370
+ let(:data) { [] }
371
+
372
+ it { expect(filtered_data).to be == [] }
373
+ end
374
+
375
+ wrap_deferred 'with data' do
376
+ let(:expected) do
377
+ data.select { |item| item['author'] == 'J.R.R. Tolkien' }
378
+ end
379
+
380
+ it { expect(filtered_data).to match_array expected }
381
+ end
382
+ end
383
+
384
+ context 'when the scope has many child scopes' do
385
+ let(:scopes) do
386
+ [
387
+ build_scope({ 'author' => 'J.R.R. Tolkien' }),
388
+ build_scope({ 'series' => 'The Lord of the Rings' }),
389
+ build_scope do |scope|
390
+ { 'published_at' => scope.less_than('1955-01-01') }
391
+ end
392
+ ]
393
+ end
394
+
395
+ describe 'with empty data' do
396
+ let(:data) { [] }
397
+
398
+ it { expect(filtered_data).to be == [] }
399
+ end
400
+
401
+ wrap_deferred 'with data' do
402
+ let(:expected) do
403
+ data.select do |item|
404
+ item['author'] == 'J.R.R. Tolkien' ||
405
+ item['series'] == 'The Lord of the Rings' ||
406
+ item['published_at'] < '1955-01-01'
407
+ end
408
+ end
409
+
410
+ it { expect(filtered_data).to match_array expected }
411
+ end
412
+ end
413
+ end
414
+ end
415
+ end