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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -0
  3. data/README.md +5 -5
  4. data/lib/cuprum/collections/association.rb +9 -28
  5. data/lib/cuprum/collections/associations/belongs_to.rb +1 -8
  6. data/lib/cuprum/collections/associations/has_many.rb +1 -10
  7. data/lib/cuprum/collections/associations/has_one.rb +1 -10
  8. data/lib/cuprum/collections/basic/collection.rb +56 -49
  9. data/lib/cuprum/collections/basic/command.rb +22 -88
  10. data/lib/cuprum/collections/basic/commands/assign_one.rb +2 -6
  11. data/lib/cuprum/collections/basic/commands/build_one.rb +1 -4
  12. data/lib/cuprum/collections/basic/commands/destroy_one.rb +4 -8
  13. data/lib/cuprum/collections/basic/commands/find_many.rb +4 -24
  14. data/lib/cuprum/collections/basic/commands/find_matching.rb +5 -21
  15. data/lib/cuprum/collections/basic/commands/find_one.rb +3 -20
  16. data/lib/cuprum/collections/basic/commands/insert_one.rb +3 -6
  17. data/lib/cuprum/collections/basic/commands/update_one.rb +3 -6
  18. data/lib/cuprum/collections/basic/commands/validate_one.rb +13 -18
  19. data/lib/cuprum/collections/basic/query.rb +26 -40
  20. data/lib/cuprum/collections/basic/repository.rb +4 -3
  21. data/lib/cuprum/collections/basic/scopes/all_scope.rb +25 -0
  22. data/lib/cuprum/collections/basic/scopes/base.rb +32 -0
  23. data/lib/cuprum/collections/basic/scopes/builder.rb +39 -0
  24. data/lib/cuprum/collections/basic/scopes/conjunction_scope.rb +20 -0
  25. data/lib/cuprum/collections/basic/scopes/criteria_scope.rb +62 -0
  26. data/lib/cuprum/collections/basic/scopes/disjunction_scope.rb +20 -0
  27. data/lib/cuprum/collections/basic/scopes/none_scope.rb +33 -0
  28. data/lib/cuprum/collections/basic/scopes.rb +23 -0
  29. data/lib/cuprum/collections/basic.rb +1 -0
  30. data/lib/cuprum/collections/collection.rb +24 -82
  31. data/lib/cuprum/collections/collection_command.rb +116 -0
  32. data/lib/cuprum/collections/commands/abstract_find_many.rb +11 -21
  33. data/lib/cuprum/collections/commands/abstract_find_matching.rb +43 -24
  34. data/lib/cuprum/collections/commands/abstract_find_one.rb +7 -10
  35. data/lib/cuprum/collections/commands/associations/find_many.rb +3 -8
  36. data/lib/cuprum/collections/commands/associations/require_many.rb +5 -5
  37. data/lib/cuprum/collections/commands/create.rb +3 -3
  38. data/lib/cuprum/collections/commands/find_one_matching.rb +6 -6
  39. data/lib/cuprum/collections/commands/query_command.rb +19 -0
  40. data/lib/cuprum/collections/commands/update.rb +3 -3
  41. data/lib/cuprum/collections/commands/upsert.rb +10 -10
  42. data/lib/cuprum/collections/commands.rb +1 -0
  43. data/lib/cuprum/collections/constraints/ordering.rb +2 -2
  44. data/lib/cuprum/collections/errors/abstract_find_error.rb +25 -42
  45. data/lib/cuprum/collections/errors/extra_attributes.rb +3 -3
  46. data/lib/cuprum/collections/errors/failed_validation.rb +2 -2
  47. data/lib/cuprum/collections/errors/invalid_parameters.rb +2 -2
  48. data/lib/cuprum/collections/errors/invalid_query.rb +10 -16
  49. data/lib/cuprum/collections/errors/missing_default_contract.rb +1 -1
  50. data/lib/cuprum/collections/errors/unknown_operator.rb +1 -1
  51. data/lib/cuprum/collections/queries.rb +31 -0
  52. data/lib/cuprum/collections/query.rb +50 -62
  53. data/lib/cuprum/collections/relation.rb +5 -383
  54. data/lib/cuprum/collections/relations/cardinality.rb +66 -0
  55. data/lib/cuprum/collections/relations/options.rb +18 -0
  56. data/lib/cuprum/collections/relations/parameters.rb +217 -0
  57. data/lib/cuprum/collections/relations/primary_keys.rb +23 -0
  58. data/lib/cuprum/collections/relations/scope.rb +65 -0
  59. data/lib/cuprum/collections/relations.rb +14 -0
  60. data/lib/cuprum/collections/repository.rb +5 -5
  61. data/lib/cuprum/collections/resource.rb +10 -41
  62. data/lib/cuprum/collections/rspec/contracts/association_contracts.rb +80 -90
  63. data/lib/cuprum/collections/rspec/contracts/collection_contracts.rb +69 -111
  64. data/lib/cuprum/collections/rspec/contracts/command_contracts.rb +42 -1335
  65. data/lib/cuprum/collections/rspec/contracts/query_contracts.rb +352 -531
  66. data/lib/cuprum/collections/rspec/contracts/relation_contracts.rb +74 -191
  67. data/lib/cuprum/collections/rspec/contracts/repository_contracts.rb +13 -13
  68. data/lib/cuprum/collections/rspec/contracts/scope_contracts.rb +1029 -0
  69. data/lib/cuprum/collections/rspec/contracts/scopes/builder_contracts.rb +856 -0
  70. data/lib/cuprum/collections/rspec/contracts/scopes/composition_contracts.rb +1430 -0
  71. data/lib/cuprum/collections/rspec/contracts/scopes/criteria_contracts.rb +2217 -0
  72. data/lib/cuprum/collections/rspec/contracts/scopes/logical_contracts.rb +297 -0
  73. data/lib/cuprum/collections/rspec/contracts/scopes.rb +13 -0
  74. data/lib/cuprum/collections/rspec/contracts.rb +2 -0
  75. data/lib/cuprum/collections/rspec/deferred/association_examples.rb +2098 -0
  76. data/lib/cuprum/collections/rspec/deferred/collection_examples.rb +338 -0
  77. data/lib/cuprum/collections/rspec/deferred/command_examples.rb +160 -0
  78. data/lib/cuprum/collections/rspec/deferred/commands/assign_one_examples.rb +178 -0
  79. data/lib/cuprum/collections/rspec/deferred/commands/build_one_examples.rb +94 -0
  80. data/lib/cuprum/collections/rspec/deferred/commands/destroy_one_examples.rb +118 -0
  81. data/lib/cuprum/collections/rspec/deferred/commands/find_many_examples.rb +307 -0
  82. data/lib/cuprum/collections/rspec/deferred/commands/find_matching_examples.rb +143 -0
  83. data/lib/cuprum/collections/rspec/deferred/commands/find_one_examples.rb +116 -0
  84. data/lib/cuprum/collections/rspec/deferred/commands/insert_one_examples.rb +103 -0
  85. data/lib/cuprum/collections/rspec/deferred/commands/update_one_examples.rb +99 -0
  86. data/lib/cuprum/collections/rspec/deferred/commands/validate_one_examples.rb +117 -0
  87. data/lib/cuprum/collections/rspec/deferred/commands.rb +8 -0
  88. data/lib/cuprum/collections/rspec/deferred/relation_examples.rb +1437 -0
  89. data/lib/cuprum/collections/rspec/deferred/resource_examples.rb +26 -0
  90. data/lib/cuprum/collections/rspec/deferred.rb +8 -0
  91. data/lib/cuprum/collections/scope.rb +29 -0
  92. data/lib/cuprum/collections/scopes/all.rb +51 -0
  93. data/lib/cuprum/collections/scopes/all_scope.rb +18 -0
  94. data/lib/cuprum/collections/scopes/base.rb +79 -0
  95. data/lib/cuprum/collections/scopes/builder.rb +39 -0
  96. data/lib/cuprum/collections/scopes/building.rb +221 -0
  97. data/lib/cuprum/collections/scopes/composition.rb +162 -0
  98. data/lib/cuprum/collections/scopes/conjunction.rb +44 -0
  99. data/lib/cuprum/collections/scopes/conjunction_scope.rb +12 -0
  100. data/lib/cuprum/collections/scopes/container.rb +65 -0
  101. data/lib/cuprum/collections/scopes/criteria/parser.rb +241 -0
  102. data/lib/cuprum/collections/scopes/criteria.rb +206 -0
  103. data/lib/cuprum/collections/scopes/criteria_scope.rb +12 -0
  104. data/lib/cuprum/collections/scopes/disjunction.rb +45 -0
  105. data/lib/cuprum/collections/scopes/disjunction_scope.rb +12 -0
  106. data/lib/cuprum/collections/scopes/none.rb +62 -0
  107. data/lib/cuprum/collections/scopes/none_scope.rb +18 -0
  108. data/lib/cuprum/collections/scopes.rb +23 -0
  109. data/lib/cuprum/collections/version.rb +2 -2
  110. data/lib/cuprum/collections.rb +14 -9
  111. metadata +61 -15
  112. data/lib/cuprum/collections/basic/query_builder.rb +0 -69
  113. data/lib/cuprum/collections/command.rb +0 -26
  114. data/lib/cuprum/collections/queries/parse.rb +0 -22
  115. data/lib/cuprum/collections/queries/parse_block.rb +0 -206
  116. data/lib/cuprum/collections/queries/parse_strategy.rb +0 -91
  117. data/lib/cuprum/collections/query_builder.rb +0 -61
  118. data/lib/cuprum/collections/rspec/contracts/basic/command_contracts.rb +0 -484
@@ -17,43 +17,53 @@ module Cuprum::Collections::RSpec::Contracts
17
17
  OPERATORS = Cuprum::Collections::Queries::Operators
18
18
  private_constant :OPERATORS
19
19
 
20
- # @!method apply(example_group, operators:)
20
+ # @!method apply(example_group, abstract: false)
21
21
  # Adds the contract to the example group.
22
22
  #
23
23
  # @param example_group [RSpec::Core::ExampleGroup] the example group to
24
24
  # which the contract is applied.
25
- # @param operators [Array<Symbol>] the expected operators.
26
- contract do |operators: OPERATORS.values|
25
+ # @param abstract [Boolean] if true, the query does not implement
26
+ # methods for operating on a collection. Defaults to false.
27
+ contract do |abstract: false|
27
28
  include Cuprum::Collections::RSpec::Contracts::QueryContracts
28
29
 
29
- operators = Set.new(operators.map(&:to_sym))
30
-
31
- include_contract 'with query contexts'
30
+ shared_context 'when initialized with a scope' do
31
+ let(:initial_scope) do
32
+ Cuprum::Collections::Scope.new do |scope|
33
+ { 'published_at' => scope.less_than('1973-01-01') }
34
+ end
35
+ end
36
+ let(:filtered_data) do
37
+ super().select { |item| item['published_at'] < '1973-01-01' }
38
+ end
39
+ end
32
40
 
33
41
  shared_context 'when the query has composed filters' do
34
42
  let(:scoped_query) do
35
43
  super()
36
44
  .where { { author: 'Ursula K. LeGuin' } }
37
- .where { { series: not_equal('Earthsea') } }
45
+ .where { |scope| { series: scope.not_equal('Earthsea') } }
38
46
  end
39
- let(:matching_data) do
47
+ let(:filtered_data) do
40
48
  super()
41
49
  .select { |item| item['author'] == 'Ursula K. LeGuin' }
42
50
  .reject { |item| item['series'] == 'Earthsea' }
43
51
  end
44
52
  end
45
53
 
54
+ let(:filter) { nil }
55
+ let(:limit) { nil }
56
+ let(:offset) { nil }
57
+ let(:order) { nil }
46
58
  let(:scoped_query) do
47
- # :nocov:
48
59
  scoped =
49
60
  if filter.is_a?(Proc)
50
- query.where(&filter)
51
- elsif !filter.nil?
52
- query.where(filter)
61
+ subject.where(&filter)
62
+ elsif filter
63
+ subject.where(filter)
53
64
  else
54
- query
65
+ subject
55
66
  end
56
- # :nocov:
57
67
  scoped = scoped.limit(limit) if limit
58
68
  scoped = scoped.offset(offset) if offset
59
69
  scoped = scoped.order(order) if order
@@ -66,19 +76,11 @@ module Cuprum::Collections::RSpec::Contracts
66
76
  end
67
77
 
68
78
  describe '#count' do
69
- let(:data) { [] }
70
- let(:matching_data) { data }
71
- let(:expected_data) do
72
- defined?(super()) ? super() : matching_data
73
- end
74
-
75
79
  it { expect(query).to respond_to(:count).with(0).arguments }
76
80
 
77
- it { expect(query.count).to be == expected_data.count }
81
+ next if abstract
78
82
 
79
- wrap_context 'when the query has composed filters' do
80
- it { expect(scoped_query.count).to be == expected_data.count }
81
- end
83
+ it { expect(query.count).to be 0 }
82
84
 
83
85
  context 'when the collection data changes' do
84
86
  let(:item) { BOOKS_FIXTURES.first }
@@ -89,16 +91,26 @@ module Cuprum::Collections::RSpec::Contracts
89
91
  add_item_to_collection(item)
90
92
  end
91
93
 
92
- it { expect(query.count).to be == expected_data.count }
94
+ it { expect(query.count).to be 0 }
93
95
  end
94
96
 
95
97
  context 'when the collection has many items' do
96
98
  let(:data) { BOOKS_FIXTURES }
97
99
 
98
- it { expect(query.count).to be == expected_data.count }
99
-
100
- wrap_context 'when the query has composed filters' do
100
+ include_contract 'should query the collection', :ignore_order do
101
101
  it { expect(scoped_query.count).to be == expected_data.count }
102
+
103
+ wrap_context 'when the query has composed filters' do
104
+ it { expect(scoped_query.count).to be == expected_data.count }
105
+ end
106
+
107
+ wrap_context 'when initialized with a scope' do
108
+ it { expect(scoped_query.count).to be == expected_data.count }
109
+
110
+ wrap_context 'when the query has composed filters' do
111
+ it { expect(scoped_query.count).to be == expected_data.count }
112
+ end
113
+ end
102
114
  end
103
115
 
104
116
  context 'when the collection data changes' do
@@ -116,75 +128,12 @@ module Cuprum::Collections::RSpec::Contracts
116
128
  end
117
129
  end
118
130
 
119
- describe '#criteria' do
120
- include_examples 'should have reader', :criteria, []
121
-
122
- wrap_context 'when the query has where: a simple block filter' do
123
- let(:expected) { [['author', :equal, 'Ursula K. LeGuin']] }
124
-
125
- it { expect(scoped_query.criteria).to be == expected }
126
- end
127
-
128
- wrap_context 'when the query has where: a complex block filter' do
129
- let(:expected) do
130
- [
131
- ['author', :equal, 'Ursula K. LeGuin'],
132
- ['series', :not_equal, 'Earthsea']
133
- ]
134
- end
135
-
136
- if operators.include?(OPERATORS::EQUAL) &&
137
- operators.include?(OPERATORS::NOT_EQUAL)
138
- it { expect(scoped_query.criteria).to be == expected }
139
- else
140
- # :nocov:
141
- pending
142
- # :nocov:
143
- end
144
- end
145
-
146
- wrap_context 'when the query has composed filters' do
147
- let(:expected) do
148
- [
149
- ['author', :equal, 'Ursula K. LeGuin'],
150
- ['series', :not_equal, 'Earthsea']
151
- ]
152
- end
153
-
154
- it { expect(scoped_query.criteria).to be == expected }
155
- end
156
-
157
- wrap_context 'when the query has where: an equal block filter' do
158
- let(:expected) { [['author', :equal, 'Ursula K. LeGuin']] }
159
-
160
- if operators.include?(OPERATORS::EQUAL)
161
- it { expect(scoped_query.criteria).to be == expected }
162
- else
163
- # :nocov:
164
- pending
165
- # :nocov:
166
- end
167
- end
168
-
169
- wrap_context 'when the query has where: a not_equal block filter' do
170
- let(:expected) { [['author', :not_equal, 'Ursula K. LeGuin']] }
171
-
172
- if operators.include?(OPERATORS::NOT_EQUAL)
173
- it { expect(scoped_query.criteria).to be == expected }
174
- else
175
- # :nocov:
176
- pending
177
- # :nocov:
178
- end
179
- end
180
- end
181
-
182
131
  describe '#each' do
183
132
  shared_examples 'should enumerate the matching data' do
184
133
  describe 'with no arguments' do
185
134
  it { expect(scoped_query.each).to be_a Enumerator }
186
135
 
187
- it { expect(scoped_query.each.count).to be == matching_data.size }
136
+ it { expect(scoped_query.each.count).to be == expected_data.size }
188
137
 
189
138
  it { expect(scoped_query.each.to_a).to deep_match expected_data }
190
139
  end
@@ -197,26 +146,12 @@ module Cuprum::Collections::RSpec::Contracts
197
146
  end
198
147
  end
199
148
 
200
- let(:data) { [] }
201
- let(:matching_data) { data }
202
- let(:expected_data) do
203
- defined?(super()) ? super() : matching_data
204
- end
149
+ next if abstract
205
150
 
206
151
  it { expect(query).to respond_to(:each).with(0).arguments }
207
152
 
208
153
  include_examples 'should enumerate the matching data'
209
154
 
210
- include_contract 'should perform queries',
211
- block: lambda {
212
- include_examples 'should enumerate the matching data'
213
- },
214
- operators: operators
215
-
216
- wrap_context 'when the query has composed filters' do
217
- include_examples 'should enumerate the matching data'
218
- end
219
-
220
155
  context 'when the collection data changes' do
221
156
  let(:item) { BOOKS_FIXTURES.first }
222
157
 
@@ -232,16 +167,20 @@ module Cuprum::Collections::RSpec::Contracts
232
167
  context 'when the collection has many items' do
233
168
  let(:data) { BOOKS_FIXTURES }
234
169
 
235
- include_examples 'should enumerate the matching data'
170
+ include_contract 'should query the collection' do
171
+ include_examples 'should enumerate the matching data'
236
172
 
237
- include_contract 'should perform queries',
238
- block: lambda {
173
+ wrap_context 'when the query has composed filters' do
239
174
  include_examples 'should enumerate the matching data'
240
- },
241
- operators: operators
175
+ end
242
176
 
243
- wrap_context 'when the query has composed filters' do
244
- include_examples 'should enumerate the matching data'
177
+ wrap_context 'when initialized with a scope' do
178
+ include_examples 'should enumerate the matching data'
179
+
180
+ wrap_context 'when the query has composed filters' do
181
+ include_examples 'should enumerate the matching data'
182
+ end
183
+ end
245
184
  end
246
185
 
247
186
  context 'when the collection data changes' do
@@ -261,39 +200,35 @@ module Cuprum::Collections::RSpec::Contracts
261
200
 
262
201
  describe '#exists?' do
263
202
  shared_examples 'should check the existence of matching data' do
264
- it { expect(query.exists?).to be == !matching_data.empty? }
203
+ let(:data) { [] }
204
+ let(:expected_data) { defined?(super()) ? super() : data }
205
+
206
+ it { expect(query.exists?).to be == !expected_data.empty? }
265
207
  end
266
208
 
267
- let(:data) { [] }
268
- let(:matching_data) { data }
209
+ next if abstract
269
210
 
270
211
  include_examples 'should define predicate', :exists?
271
212
 
272
213
  include_examples 'should check the existence of matching data'
273
214
 
274
- include_contract 'should perform queries',
275
- block: lambda {
276
- include_examples 'should check the existence of matching data'
277
- },
278
- operators: operators
279
-
280
- wrap_context 'when the query has composed filters' do
281
- include_examples 'should check the existence of matching data'
282
- end
283
-
284
215
  context 'when the collection has many items' do
285
216
  let(:data) { BOOKS_FIXTURES }
286
217
 
287
- include_examples 'should check the existence of matching data'
218
+ include_contract 'should query the collection', :ignore_order do
219
+ include_examples 'should check the existence of matching data'
288
220
 
289
- include_contract 'should perform queries',
290
- block: lambda {
221
+ wrap_context 'when the query has composed filters' do
291
222
  include_examples 'should check the existence of matching data'
292
- },
293
- operators: operators
223
+ end
294
224
 
295
- wrap_context 'when the query has composed filters' do
296
- include_examples 'should check the existence of matching data'
225
+ wrap_context 'when initialized with a scope' do
226
+ include_examples 'should check the existence of matching data'
227
+
228
+ wrap_context 'when the query has composed filters' do
229
+ include_examples 'should check the existence of matching data'
230
+ end
231
+ end
297
232
  end
298
233
  end
299
234
  end
@@ -495,6 +430,8 @@ module Cuprum::Collections::RSpec::Contracts
495
430
 
496
431
  it { expect(query.reset).not_to be query }
497
432
 
433
+ next if abstract
434
+
498
435
  it { expect(query.reset.to_a).to be == query.to_a }
499
436
 
500
437
  context 'when the collection data changes' do
@@ -539,52 +476,93 @@ module Cuprum::Collections::RSpec::Contracts
539
476
  end
540
477
  end
541
478
 
542
- describe '#to_a' do
543
- let(:data) { [] }
544
- let(:matching_data) { data }
545
- let(:expected_data) do
546
- defined?(super()) ? super() : matching_data
547
- end
479
+ describe '#scope' do
480
+ include_examples 'should define reader', :scope
548
481
 
549
- it { expect(query).to respond_to(:to_a).with(0).arguments }
482
+ it { expect(query.scope).to be_a Cuprum::Collections::Scopes::Base }
483
+
484
+ it { expect(query.scope.type).to be :all }
485
+
486
+ wrap_context 'when initialized with a scope' do
487
+ let(:expected) do
488
+ Cuprum::Collections::Scope.new do |scope|
489
+ {
490
+ 'published_at' => scope.less_than('1973-01-01')
491
+ }
492
+ end
493
+ end
550
494
 
551
- it { expect(query.to_a).to deep_match expected_data }
495
+ it { expect(scoped_query.scope).to be == expected }
496
+
497
+ wrap_context 'when the query has composed filters' do
498
+ let(:expected) do
499
+ Cuprum::Collections::Scope.new do |scope|
500
+ {
501
+ 'published_at' => scope.less_than('1973-01-01'),
502
+ 'author' => 'Ursula K. LeGuin',
503
+ 'series' => scope.not_equal('Earthsea')
504
+ }
505
+ end
506
+ end
552
507
 
553
- include_contract 'should perform queries',
554
- block: lambda {
555
- it { expect(scoped_query.to_a).to deep_match expected_data }
556
- },
557
- operators: operators
508
+ it { expect(scoped_query.scope).to be == expected }
509
+ end
510
+ end
558
511
 
559
512
  wrap_context 'when the query has composed filters' do
560
- it { expect(scoped_query.to_a).to deep_match expected_data }
513
+ let(:expected) do
514
+ Cuprum::Collections::Scope.new do |scope|
515
+ {
516
+ 'author' => 'Ursula K. LeGuin',
517
+ 'series' => scope.not_equal('Earthsea')
518
+ }
519
+ end
520
+ end
521
+
522
+ it { expect(scoped_query.scope).to be == expected }
561
523
  end
524
+ end
525
+
526
+ describe '#to_a' do
527
+ let(:data) { [] }
528
+ let(:queried_data) { scoped_query.to_a }
529
+ let(:expected_data) { defined?(super()) ? super() : data }
530
+
531
+ it { expect(query).to respond_to(:to_a).with(0).arguments }
532
+
533
+ next if abstract
534
+
535
+ it { expect(queried_data).to be == [] }
562
536
 
563
537
  context 'when the collection data changes' do
564
538
  let(:item) { BOOKS_FIXTURES.first }
565
539
 
566
540
  before(:example) do
567
- query.to_a # Cache query results.
541
+ scoped_query.to_a # Cache query results.
568
542
 
569
543
  add_item_to_collection(item)
570
544
  end
571
545
 
572
- it { expect(query.to_a).to deep_match expected_data }
546
+ it { expect(queried_data).to be == [] }
573
547
  end
574
548
 
575
549
  context 'when the collection has many items' do
576
550
  let(:data) { BOOKS_FIXTURES }
577
551
 
578
- it { expect(query.to_a).to deep_match expected_data }
552
+ include_contract 'should query the collection' do
553
+ it { expect(queried_data).to be == expected_data }
554
+
555
+ wrap_context 'when the query has composed filters' do
556
+ it { expect(queried_data).to be == expected_data }
557
+ end
579
558
 
580
- include_contract 'should perform queries',
581
- block: lambda {
582
- it { expect(scoped_query.to_a).to deep_match expected_data }
583
- },
584
- operators: operators
559
+ wrap_context 'when initialized with a scope' do
560
+ it { expect(queried_data).to be == expected_data }
585
561
 
586
- wrap_context 'when the query has composed filters' do
587
- it { expect(scoped_query.to_a).to deep_match expected_data }
562
+ wrap_context 'when the query has composed filters' do
563
+ it { expect(queried_data).to be == expected_data }
564
+ end
565
+ end
588
566
  end
589
567
 
590
568
  context 'when the collection data changes' do
@@ -592,181 +570,195 @@ module Cuprum::Collections::RSpec::Contracts
592
570
  let(:item) { BOOKS_FIXTURES.last }
593
571
 
594
572
  before(:example) do
595
- query.to_a # Cache query results.
573
+ scoped_query.to_a # Cache query results.
596
574
 
597
575
  add_item_to_collection(item)
598
576
  end
599
577
 
600
- it { expect(query.to_a).to deep_match expected_data }
578
+ it { expect(queried_data).to deep_match expected_data }
601
579
  end
602
580
  end
603
581
  end
604
582
 
605
583
  describe '#where' do
606
- let(:block) { -> { { title: 'The Caves of Steel' } } }
584
+ let(:block) { -> { { title: 'Gideon the Ninth' } } }
607
585
 
608
586
  it 'should define the method' do
609
- expect(query)
587
+ expect(subject)
610
588
  .to respond_to(:where)
611
589
  .with(0..1).arguments
612
- .and_keywords(:strategy)
613
590
  .and_a_block
614
591
  end
615
592
 
616
- describe 'with no arguments' do
617
- it { expect(query.where).to be_a described_class }
593
+ it { expect(subject.where(&block)).to be_a described_class }
618
594
 
619
- it { expect(query.where).not_to be query }
620
- end
595
+ it { expect(subject.where(&block)).not_to be subject }
621
596
 
622
- describe 'with a block' do
623
- it { expect(query.where(&block)).to be_a described_class }
597
+ it 'should set the scope' do
598
+ expect(subject.where(&block).scope)
599
+ .to be_a Cuprum::Collections::Scopes::Base
600
+ end
624
601
 
625
- it { expect(query.where(&block)).not_to be query }
602
+ it 'should not change the original query scope' do
603
+ expect { subject.where(&block) }
604
+ .not_to change(subject, :scope)
626
605
  end
627
606
 
628
- describe 'with a valid strategy' do
629
- it 'should return a query instance' do
630
- expect(query.where(strategy: :block, &block))
631
- .to be_a described_class
607
+ context 'when the query does not have a scope' do
608
+ let(:expected) do
609
+ Cuprum::Collections::Scope.new({ 'title' => 'Gideon the Ninth' })
632
610
  end
633
611
 
634
- it { expect(query.where(strategy: :block, &block)).not_to be query }
635
- end
612
+ describe 'with a block' do
613
+ let(:block) { -> { { 'title' => 'Gideon the Ninth' } } }
636
614
 
637
- describe 'with parameters that do not match a strategy' do
638
- let(:error_class) do
639
- Cuprum::Collections::QueryBuilder::ParseError
615
+ it { expect(subject.where(&block).scope).to be == expected }
640
616
  end
641
- let(:error_message) { 'unable to parse query with strategy nil' }
642
617
 
643
- it 'should raise an exception' do
644
- expect { query.where(%w[ichi ni san]) }
645
- .to raise_error error_class, error_message
646
- end
647
- end
618
+ describe 'with a hash' do
619
+ let(:value) { { 'title' => 'Gideon the Ninth' } }
648
620
 
649
- describe 'with an invalid strategy' do
650
- let(:error_class) do
651
- Cuprum::Collections::QueryBuilder::ParseError
621
+ it { expect(subject.where(value).scope).to be == expected }
652
622
  end
653
- let(:error_message) do
654
- 'unable to parse query with strategy :random'
623
+
624
+ describe 'with a basic scope' do
625
+ let(:value) do
626
+ Cuprum::Collections::Scope
627
+ .new({ 'title' => 'Gideon the Ninth' })
628
+ end
629
+
630
+ it { expect(subject.where(value).scope).to be == value }
655
631
  end
656
632
 
657
- it 'should raise an exception' do
658
- expect { query.where(strategy: :random) }
659
- .to raise_error error_class, error_message
633
+ describe 'with a complex scope' do
634
+ let(:value) do
635
+ Cuprum::Collections::Scope
636
+ .new({ 'title' => 'Gideon the Ninth' })
637
+ .or({ 'title' => 'Harrow the Ninth' })
638
+ end
639
+
640
+ it { expect(subject.where(value).scope).to be == value }
660
641
  end
661
642
  end
662
643
 
663
- describe 'with invalid parameters for a strategy' do
664
- let(:error_class) do
665
- Cuprum::Collections::QueryBuilder::ParseError
644
+ context 'when the query has a scope' do
645
+ let(:initial_scope) do
646
+ Cuprum::Collections::Scope.new({ 'author' => 'Tamsyn Muir' })
666
647
  end
667
- let(:error_message) { 'unable to parse query with strategy :block' }
648
+ let(:expected) do
649
+ operators = Cuprum::Collections::Queries::Operators
668
650
 
669
- it 'should raise an exception' do
670
- expect { query.where(strategy: :block) }
671
- .to raise_error error_class, error_message
651
+ [
652
+ [
653
+ 'author',
654
+ operators::EQUAL,
655
+ 'Tamsyn Muir'
656
+ ],
657
+ [
658
+ 'title',
659
+ operators::EQUAL,
660
+ 'Gideon the Ninth'
661
+ ]
662
+ ]
672
663
  end
673
- end
674
- end
675
- end
676
- end
677
-
678
- # Contract validating the behavior of a QueryBuilder implementation.
679
- module ShouldBeAQueryBuilderContract
680
- extend RSpec::SleepingKingStudios::Contract
681
664
 
682
- # @!method apply(example_group)
683
- # Adds the contract to the example group.
684
- #
685
- # @param example_group [RSpec::Core::ExampleGroup] the example group to
686
- # which the contract is applied.
687
- contract do
688
- describe '#base_query' do
689
- include_examples 'should define reader',
690
- :base_query,
691
- -> { base_query }
692
- end
665
+ describe 'with a block' do
666
+ let(:block) { -> { { 'title' => 'Gideon the Ninth' } } }
667
+ let(:scope) { subject.where(&block).scope }
693
668
 
694
- describe '#call' do
695
- let(:criteria) { [['title', :equal, 'The Naked Sun']] }
696
- let(:expected) { criteria }
697
- let(:filter) { { title: 'The Naked Sun' } }
698
- let(:strategy) { :custom }
699
- let(:parser) do
700
- instance_double(
701
- Cuprum::Collections::Queries::Parse,
702
- call: Cuprum::Result.new(value: criteria)
703
- )
704
- end
705
- let(:query) do
706
- builder.call(strategy: strategy, where: filter)
707
- end
669
+ it { expect(scope).to be_a Cuprum::Collections::Scopes::Base }
708
670
 
709
- before(:example) do
710
- allow(Cuprum::Collections::Queries::Parse)
711
- .to receive(:new)
712
- .and_return(parser)
713
- end
671
+ it { expect(scope.type).to be :criteria }
714
672
 
715
- it 'should define the method' do
716
- expect(builder).to respond_to(:call)
717
- .with(0).arguments
718
- .and_keywords(:strategy, :where)
719
- end
673
+ it { expect(scope.criteria).to be == expected }
674
+ end
720
675
 
721
- it 'should parse the criteria' do
722
- builder.call(strategy: strategy, where: filter)
676
+ describe 'with a value' do
677
+ let(:value) { { 'title' => 'Gideon the Ninth' } }
678
+ let(:scope) { subject.where(value).scope }
723
679
 
724
- expect(parser)
725
- .to have_received(:call)
726
- .with(strategy: strategy, where: filter)
727
- end
680
+ it { expect(scope).to be_a Cuprum::Collections::Scopes::Base }
728
681
 
729
- it { expect(query).to be_a base_query.class }
682
+ it { expect(scope.type).to be :criteria }
730
683
 
731
- it { expect(query).not_to be base_query }
684
+ it { expect(scope.criteria).to be == expected }
685
+ end
732
686
 
733
- it { expect(query.criteria).to be == expected }
687
+ describe 'with a basic scope' do
688
+ let(:value) do
689
+ Cuprum::Collections::Scope
690
+ .new({ 'title' => 'Gideon the Ninth' })
691
+ end
692
+ let(:scope) { subject.where(value).scope }
734
693
 
735
- describe 'with strategy: :unsafe' do
736
- let(:strategy) { :unsafe }
737
- let(:filter) { criteria }
694
+ it { expect(scope).to be_a Cuprum::Collections::Scopes::Base }
738
695
 
739
- it 'should not parse the criteria' do
740
- builder.call(strategy: strategy, where: filter)
696
+ it { expect(scope.type).to be :criteria }
741
697
 
742
- expect(parser).not_to have_received(:call)
698
+ it { expect(scope.criteria).to be == expected }
743
699
  end
744
700
 
745
- it { expect(query.criteria).to be == expected }
746
- end
701
+ describe 'with a complex scope' do
702
+ let(:value) do
703
+ Cuprum::Collections::Scope
704
+ .new({ 'title' => 'Gideon the Ninth' })
705
+ .or({ 'title' => 'Harrow the Ninth' })
706
+ end
707
+ let(:scope) { subject.where(value).scope }
708
+ let(:outer) { scope.scopes.last }
709
+ let(:expected) do
710
+ operators = Cuprum::Collections::Queries::Operators
711
+
712
+ [
713
+ [
714
+ 'author',
715
+ operators::EQUAL,
716
+ 'Tamsyn Muir'
717
+ ]
718
+ ]
719
+ end
720
+ let(:expected_first) do
721
+ operators = Cuprum::Collections::Queries::Operators
722
+
723
+ [
724
+ [
725
+ 'title',
726
+ operators::EQUAL,
727
+ 'Gideon the Ninth'
728
+ ]
729
+ ]
730
+ end
731
+ let(:expected_second) do
732
+ operators = Cuprum::Collections::Queries::Operators
733
+
734
+ [
735
+ [
736
+ 'title',
737
+ operators::EQUAL,
738
+ 'Harrow the Ninth'
739
+ ]
740
+ ]
741
+ end
747
742
 
748
- context 'when the query has existing criteria' do
749
- let(:old_criteria) { [['genre', :eq, 'Science Fiction']] }
750
- let(:expected) { old_criteria + criteria }
751
- let(:base_query) { super().send(:with_criteria, old_criteria) }
743
+ it { expect(scope).to be_a Cuprum::Collections::Scopes::Base }
752
744
 
753
- it { expect(query.criteria).to be == expected }
754
- end
745
+ it { expect(scope.type).to be :conjunction }
755
746
 
756
- context 'when the parser is unable to parse the query' do
757
- let(:error) { Cuprum::Error.new(message: 'Something went wrong.') }
758
- let(:result) { Cuprum::Result.new(error: error) }
747
+ it { expect(scope.scopes.size).to be 2 }
759
748
 
760
- before(:example) do
761
- allow(parser).to receive(:call).and_return(result)
762
- end
749
+ it { expect(scope.scopes.first.type).to be :criteria }
763
750
 
764
- it 'should raise an exception' do
765
- expect do
766
- builder.call(strategy: strategy, where: filter)
767
- end
768
- .to raise_error Cuprum::Collections::QueryBuilder::ParseError,
769
- error.message
751
+ it { expect(scope.scopes.first.criteria).to be == expected }
752
+
753
+ it { expect(outer).to be_a Cuprum::Collections::Scopes::Base }
754
+
755
+ it { expect(outer.type).to be :disjunction }
756
+
757
+ it { expect(outer.scopes.size).to be 2 }
758
+
759
+ it { expect(outer.scopes.first.criteria).to be == expected_first }
760
+
761
+ it { expect(outer.scopes.last.criteria).to be == expected_second }
770
762
  end
771
763
  end
772
764
  end
@@ -774,318 +766,147 @@ module Cuprum::Collections::RSpec::Contracts
774
766
  end
775
767
 
776
768
  # Contract validating the behavior when performing queries.
777
- module ShouldPerformQueriesContract
769
+ module ShouldQueryTheCollectionContract
778
770
  extend RSpec::SleepingKingStudios::Contract
779
771
 
780
- OPERATORS = Cuprum::Collections::Queries::Operators
781
- private_constant :OPERATORS
782
-
783
- # @!method apply(example_group, block:, operators:)
784
- # Adds the contract to the example group.
785
- #
786
- # @param example_group [RSpec::Core::ExampleGroup] the example group to
787
- # which the contract is applied.
788
- # @param block [Proc] the expectations for each query context.
789
- # @param operators [Array<Symbol>] the expected operators.
790
- contract do |block:, operators: OPERATORS.values|
791
- operators = Set.new(operators.map(&:to_sym))
792
-
793
- wrap_context 'when the query has limit: value' do
794
- instance_exec(&block)
795
- end
772
+ contract do |*tags, &examples|
773
+ ignore_order = tags.include?(:ignore_order)
796
774
 
797
- wrap_context 'when the query has offset: value' do
798
- instance_exec(&block)
799
- end
800
-
801
- wrap_context 'when the query has order: a simple ordering' do
802
- instance_exec(&block)
775
+ shared_examples 'should query the collection' do
776
+ # :nocov:
777
+ if examples
778
+ instance_exec(&examples)
779
+ else
780
+ it { expect(queried_data).to be == expected_data }
781
+ end
782
+ # :nocov:
803
783
  end
804
784
 
805
- wrap_context 'when the query has order: a complex ordering' do
806
- instance_exec(&block)
807
- end
785
+ shared_examples 'should apply limit and offset' do
786
+ include_examples 'should query the collection'
808
787
 
809
- context 'when the query has where: a block filter' do
810
- context 'with a simple filter' do
811
- include_context 'when the query has where: a simple block filter'
788
+ context 'with limit: value' do
789
+ let(:limit) { 3 }
812
790
 
813
- instance_exec(&block)
791
+ include_examples 'should query the collection'
814
792
  end
815
793
 
816
- context 'with a complex filter' do
817
- include_context 'when the query has where: a complex block filter'
794
+ context 'with offset: value' do
795
+ let(:offset) { 2 }
818
796
 
819
- if operators.include?(OPERATORS::EQUAL) &&
820
- operators.include?(OPERATORS::NOT_EQUAL)
821
- instance_exec(&block)
822
- else
823
- # :nocov:
824
- pending
825
- # :nocov:
826
- end
827
- end
828
-
829
- context 'with an equals filter' do
830
- include_context 'when the query has where: an equal block filter'
831
-
832
- if operators.include?(OPERATORS::EQUAL)
833
- instance_exec(&block)
834
- else
835
- # :nocov:
836
- pending
837
- # :nocov:
838
- end
797
+ include_examples 'should query the collection'
839
798
  end
840
799
 
841
- context 'with a greater_than filter' do
842
- include_context 'when the query has where: a greater_than filter'
800
+ describe 'with limit: value and offset: value' do
801
+ let(:limit) { 3 }
802
+ let(:offset) { 2 }
843
803
 
844
- if operators.include?(OPERATORS::GREATER_THAN)
845
- instance_exec(&block)
846
- else
847
- # :nocov:
848
- pending
849
- # :nocov:
850
- end
804
+ include_examples 'should query the collection'
851
805
  end
806
+ end
852
807
 
853
- context 'with a greater_than_or_equal_to filter' do
854
- include_context \
855
- 'when the query has where: a greater_than_or_equal_to filter'
856
-
857
- if operators.include?(OPERATORS::GREATER_THAN_OR_EQUAL_TO)
858
- instance_exec(&block)
859
- else
860
- # :nocov:
861
- pending
862
- # :nocov:
863
- end
864
- end
808
+ shared_examples 'should order the results' do
809
+ include_examples 'should apply limit and offset'
865
810
 
866
- context 'with a less_than filter' do
867
- include_context 'when the query has where: a less_than filter'
811
+ next if ignore_order
868
812
 
869
- if operators.include?(OPERATORS::LESS_THAN)
870
- instance_exec(&block)
871
- else
872
- # :nocov:
873
- pending
874
- # :nocov:
813
+ describe 'with a simple ordering' do
814
+ let(:order) { 'title' }
815
+ let(:ordered_data) do
816
+ filtered_data.sort_by { |item| item['title'] }
875
817
  end
876
- end
877
818
 
878
- context 'with a less_than_or_equal_to filter' do
879
- include_context \
880
- 'when the query has where: a less_than_or_equal_to filter'
881
-
882
- if operators.include?(OPERATORS::LESS_THAN_OR_EQUAL_TO)
883
- instance_exec(&block)
884
- else
885
- # :nocov:
886
- pending
887
- # :nocov:
888
- end
819
+ include_examples 'should apply limit and offset'
889
820
  end
890
821
 
891
- context 'with a not_equal filter' do
892
- include_context 'when the query has where: a not_equal block filter'
893
-
894
- if operators.include?(OPERATORS::NOT_EQUAL)
895
- instance_exec(&block)
896
- else
897
- # :nocov:
898
- pending
899
- # :nocov:
822
+ describe 'with a complex ordering' do
823
+ let(:order) do
824
+ {
825
+ 'author' => :desc,
826
+ 'published_at' => :asc
827
+ }
900
828
  end
901
- end
829
+ let(:ordered_data) do
830
+ filtered_data.sort do |u, v|
831
+ compare = u['author'] <=> v['author']
902
832
 
903
- context 'with a not_one_of filter' do
904
- include_context \
905
- 'when the query has where: a not_one_of block filter'
833
+ next -compare unless compare.zero?
906
834
 
907
- if operators.include?(OPERATORS::NOT_ONE_OF)
908
- instance_exec(&block)
909
- else
910
- # :nocov:
911
- pending
912
- # :nocov:
835
+ u['published_at'] <=> v['published_at']
836
+ end
913
837
  end
914
- end
915
-
916
- context 'with a one_of filter' do
917
- include_context 'when the query has where: a one_of block filter'
918
838
 
919
- if operators.include?(OPERATORS::ONE_OF)
920
- instance_exec(&block)
921
- else
922
- # :nocov:
923
- pending
924
- # :nocov:
925
- end
839
+ include_examples 'should apply limit and offset'
926
840
  end
927
841
  end
928
842
 
929
- wrap_context 'when the query has multiple query options' do
930
- instance_exec(&block)
931
- end
932
- end
933
- end
843
+ let(:filter) { defined?(super()) ? super() : nil }
844
+ let(:limit) { defined?(super()) ? super() : nil }
845
+ let(:offset) { defined?(super()) ? super() : nil }
846
+ let(:mapped_data) { defined?(super()) ? super() : data }
847
+ let(:filtered_data) { mapped_data }
848
+ let(:ordered_data) do
849
+ return super() if defined?(super())
934
850
 
935
- # Contract defining contexts for validating query behavior.
936
- module WithQueryContextsContract
937
- extend RSpec::SleepingKingStudios::Contract
851
+ attr_name = defined?(default_order) ? default_order : 'id'
938
852
 
939
- # @!method apply(example_group)
940
- # Adds the contract to the example group.
941
- #
942
- # @param example_group [RSpec::Core::ExampleGroup] the example group to
943
- # which the contract is applied.
944
- contract do
945
- let(:filter) { nil }
946
- let(:strategy) { nil }
947
- let(:limit) { nil }
948
- let(:offset) { nil }
949
- let(:order) { nil }
950
-
951
- shared_context 'when the query has limit: value' do
952
- let(:limit) { 3 }
953
- let(:matching_data) { super()[0...limit] }
853
+ filtered_data.sort_by { |item| item[attr_name] }
954
854
  end
855
+ let(:matching_data) do
856
+ data = ordered_data
857
+ data = data[offset..] || [] if offset
858
+ data = data[...limit] || [] if limit
955
859
 
956
- shared_context 'when the query has offset: value' do
957
- let(:offset) { 2 }
958
- let(:matching_data) { super()[offset..] || [] }
860
+ data
959
861
  end
960
-
961
- shared_context 'when the query has order: a simple ordering' do
962
- let(:order) { :title }
963
- let(:matching_data) { super().sort_by { |item| item['title'] } }
862
+ let(:expected_data) do
863
+ defined?(super()) ? super() : matching_data
964
864
  end
965
865
 
966
- shared_context 'when the query has order: a complex ordering' do
967
- let(:order) do
968
- {
969
- author: :asc,
970
- title: :desc
971
- }
972
- end
973
- let(:matching_data) do
974
- super().sort do |u, v|
975
- cmp = u['author'] <=> v['author']
976
-
977
- cmp.zero? ? (v['title'] <=> u['title']) : cmp
978
- end
979
- end
980
- end
866
+ include_examples 'should order the results'
981
867
 
982
- shared_context 'when the query has where: a simple block filter' do
983
- let(:filter) { -> { { author: 'Ursula K. LeGuin' } } }
984
- let(:matching_data) do
868
+ describe 'with a block filter' do
869
+ let(:filter) { -> { { 'author' => 'Ursula K. LeGuin' } } }
870
+ let(:filtered_data) do
985
871
  super().select { |item| item['author'] == 'Ursula K. LeGuin' }
986
872
  end
987
- end
988
873
 
989
- shared_context 'when the query has where: a complex block filter' do
990
- let(:filter) do
991
- lambda do
992
- {
993
- author: equals('Ursula K. LeGuin'),
994
- series: not_equal('Earthsea')
995
- }
996
- end
997
- end
998
- let(:matching_data) do
999
- super()
1000
- .select { |item| item['author'] == 'Ursula K. LeGuin' }
1001
- .reject { |item| item['series'] == 'Earthsea' }
1002
- end
1003
- end
1004
-
1005
- shared_context 'when the query has where: a greater_than filter' do
1006
- let(:filter) { -> { { published_at: greater_than('1970-12-01') } } }
1007
- let(:matching_data) do
1008
- super().select { |item| item['published_at'] > '1970-12-01' }
1009
- end
874
+ include_examples 'should order the results'
1010
875
  end
1011
876
 
1012
- shared_context 'when the query has where: a greater_than_or_equal_to ' \
1013
- 'filter' \
1014
- do
1015
- let(:filter) do
1016
- -> { { published_at: greater_than_or_equal_to('1970-12-01') } }
1017
- end
1018
- let(:matching_data) do
1019
- super().select { |item| item['published_at'] >= '1970-12-01' }
877
+ describe 'with a hash filter' do
878
+ let(:filter) { { 'author' => 'Ursula K. LeGuin' } }
879
+ let(:filtered_data) do
880
+ super().select { |item| item['author'] == 'Ursula K. LeGuin' }
1020
881
  end
1021
- end
1022
882
 
1023
- shared_context 'when the query has where: a less_than filter' do
1024
- let(:filter) { -> { { published_at: less_than('1970-12-01') } } }
1025
- let(:matching_data) do
1026
- super().select { |item| item['published_at'] < '1970-12-01' }
1027
- end
883
+ include_examples 'should order the results'
1028
884
  end
1029
885
 
1030
- shared_context 'when the query has where: a ' \
1031
- 'less_than_or_equal_to filter' \
1032
- do
886
+ describe 'with a basic scope filter' do
1033
887
  let(:filter) do
1034
- -> { { published_at: less_than_or_equal_to('1970-12-01') } }
1035
- end
1036
- let(:matching_data) do
1037
- super().select { |item| item['published_at'] <= '1970-12-01' }
888
+ Cuprum::Collections::Scope.new({ 'author' => 'Ursula K. LeGuin' })
1038
889
  end
1039
- end
1040
-
1041
- shared_context 'when the query has where: an equal block filter' do
1042
- let(:filter) { -> { { author: equals('Ursula K. LeGuin') } } }
1043
- let(:matching_data) do
890
+ let(:filtered_data) do
1044
891
  super().select { |item| item['author'] == 'Ursula K. LeGuin' }
1045
892
  end
1046
- end
1047
893
 
1048
- shared_context 'when the query has where: a not_equal block filter' do
1049
- let(:filter) { -> { { author: not_equal('Ursula K. LeGuin') } } }
1050
- let(:matching_data) do
1051
- super().reject { |item| item['author'] == 'Ursula K. LeGuin' }
1052
- end
894
+ include_examples 'should order the results'
1053
895
  end
1054
896
 
1055
- shared_context 'when the query has where: a not_one_of block filter' do
897
+ describe 'with a complex scope filter' do
1056
898
  let(:filter) do
1057
- -> { { series: not_one_of(['Earthsea', 'The Lord of the Rings']) } }
1058
- end
1059
- let(:matching_data) do
1060
- super().reject do |item|
1061
- ['Earthsea', 'The Lord of the Rings'].include?(item['series'])
1062
- end
899
+ Cuprum::Collections::Scope
900
+ .new({ 'author' => 'Ursula K. LeGuin' })
901
+ .or({ 'series' => nil })
1063
902
  end
1064
- end
1065
-
1066
- shared_context 'when the query has where: a one_of block filter' do
1067
- let(:filter) do
1068
- -> { { series: one_of(['Earthsea', 'The Lord of the Rings']) } }
1069
- end
1070
- let(:matching_data) do
903
+ let(:filtered_data) do
1071
904
  super().select do |item|
1072
- ['Earthsea', 'The Lord of the Rings'].include?(item['series'])
905
+ item['author'] == 'Ursula K. LeGuin' || item['series'].nil?
1073
906
  end
1074
907
  end
1075
- end
1076
908
 
1077
- shared_context 'when the query has multiple query options' do
1078
- let(:filter) { -> { { author: 'Ursula K. LeGuin' } } }
1079
- let(:strategy) { nil }
1080
- let(:order) { { title: :desc } }
1081
- let(:limit) { 2 }
1082
- let(:offset) { 1 }
1083
- let(:matching_data) do
1084
- super()
1085
- .select { |item| item['author'] == 'Ursula K. LeGuin' }
1086
- .sort { |u, v| v['title'] <=> u['title'] }
1087
- .slice(1, 2) || []
1088
- end
909
+ include_examples 'should order the results'
1089
910
  end
1090
911
  end
1091
912
  end