mongoid-scroll 1.0.1 → 2.0.1

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +26 -7
  3. data/.github/workflows/danger-comment.yml +9 -0
  4. data/.github/workflows/danger.yml +9 -15
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +5 -0
  7. data/.rubocop_todo.yml +153 -35
  8. data/CHANGELOG.md +22 -5
  9. data/Dangerfile +5 -1
  10. data/Gemfile +16 -8
  11. data/README.md +27 -38
  12. data/RELEASING.md +31 -50
  13. data/Rakefile +3 -3
  14. data/UPGRADING.md +21 -1
  15. data/examples/{mongoid_scroll_feed.rb → feed.rb} +4 -3
  16. data/lib/config/locales/en.yml +4 -1
  17. data/lib/mongoid/criteria/scrollable/fields.rb +6 -0
  18. data/lib/mongoid/criteria/scrollable/iterator.rb +19 -0
  19. data/lib/mongoid/criteria/scrollable.rb +29 -9
  20. data/lib/mongoid/scroll/base64_encoded_cursor.rb +8 -6
  21. data/lib/mongoid/scroll/base_cursor.rb +12 -11
  22. data/lib/mongoid/scroll/cursor.rb +5 -5
  23. data/lib/mongoid/scroll/errors/base.rb +4 -4
  24. data/lib/mongoid/scroll/errors/mismatched_sort_fields_error.rb +1 -3
  25. data/lib/mongoid/scroll/errors/multiple_sort_fields_error.rb +1 -3
  26. data/lib/mongoid/scroll/errors/unsupported_type_error.rb +11 -0
  27. data/lib/mongoid/scroll/errors.rb +1 -0
  28. data/lib/mongoid/scroll/version.rb +1 -1
  29. data/lib/mongoid-scroll.rb +1 -2
  30. data/mongoid-scroll.gemspec +3 -3
  31. data/spec/mongoid/base64_encoded_cursor_spec.rb +148 -77
  32. data/spec/mongoid/criteria_spec.rb +139 -37
  33. data/spec/mongoid/cursor_spec.rb +112 -63
  34. data/spec/mongoid/scroll_spec.rb +1 -1
  35. data/spec/spec_helper.rb +14 -3
  36. data/spec/support/feed/embedded_item.rb +2 -0
  37. data/spec/support/feed/item.rb +1 -1
  38. metadata +13 -26
  39. data/examples/mongo_ruby_driver_scroll_feed.rb +0 -45
  40. data/lib/mongo/scrollable.rb +0 -39
  41. data/spec/mongo/collection_view_spec.rb +0 -143
@@ -7,19 +7,23 @@ describe Mongoid::Criteria do
7
7
  subject do
8
8
  Feed::Item.desc(:name).asc(:value)
9
9
  end
10
+
10
11
  it ':scroll' do
11
12
  expect(subject).to respond_to(:scroll)
12
13
  end
14
+
13
15
  it 'raises Mongoid::Scroll::Errors::MultipleSortFieldsError' do
14
16
  expect do
15
17
  subject.scroll(cursor_type)
16
18
  end.to raise_error Mongoid::Scroll::Errors::MultipleSortFieldsError, /You're attempting to scroll over data with a sort order that includes multiple fields: name, value./
17
19
  end
18
20
  end
21
+
19
22
  context 'with different sort fields between the cursor and the criteria' do
20
23
  subject do
21
24
  Feed::Item.desc(:name)
22
25
  end
26
+
23
27
  it 'raises Mongoid::Scroll::Errors::MismatchedSortFieldsError' do
24
28
  record = Feed::Item.create!
25
29
  cursor = cursor_type.from_record(record, field: record.fields['a_string'])
@@ -27,16 +31,50 @@ describe Mongoid::Criteria do
27
31
  expect { subject.scroll(cursor) }.to raise_error Mongoid::Scroll::Errors::MismatchedSortFieldsError, error_string
28
32
  end
29
33
  end
34
+
30
35
  context 'with no sort' do
31
36
  subject do
32
37
  Feed::Item.all
33
38
  end
39
+
34
40
  it 'adds a default sort by _id' do
35
41
  expect(subject.scroll(cursor_type).options[:sort]).to eq('_id' => 1)
36
42
  end
37
43
  end
44
+
45
+ context 'with a missing field' do
46
+ it 'rejects a sort field that is not defined on the model' do
47
+ expect do
48
+ Feed::Item.asc(:removed_field).scroll(cursor_type)
49
+ end.to raise_error Mongoid::Scroll::Errors::NoSuchFieldError, /does not exist: removed_field/
50
+ end
51
+
52
+ it 'rejects a cursor whose field is no longer defined on the model' do
53
+ cursor = cursor_type.new(nil, field_name: 'removed_field', field_type: String)
54
+ expect do
55
+ Feed::Item.asc(:name).scroll(cursor)
56
+ end.to raise_error Mongoid::Scroll::Errors::NoSuchFieldError, /does not exist: removed_field/
57
+ end
58
+
59
+ it 'does not confuse a nil field value with a missing field' do
60
+ record = Feed::Item.create!
61
+ cursor = cursor_type.from_record(record, field: record.fields['name'])
62
+ expect { Feed::Item.asc(:name).scroll(cursor) }.not_to raise_error
63
+ end
64
+
65
+ if cursor_type == Mongoid::Scroll::Base64EncodedCursor
66
+ it 'rejects a serialized cursor whose field is no longer defined on the model' do
67
+ cursor = cursor_type.new(nil, field_name: 'removed_field', field_type: String)
68
+ saved_cursor = cursor_type.new(cursor.to_s)
69
+ expect do
70
+ Feed::Item.asc(:name).scroll(saved_cursor)
71
+ end.to raise_error Mongoid::Scroll::Errors::NoSuchFieldError, /does not exist: removed_field/
72
+ end
73
+ end
74
+ end
75
+
38
76
  context 'with data' do
39
- before :each do
77
+ before do
40
78
  10.times do |i|
41
79
  Feed::Item.create!(
42
80
  name: i.to_s,
@@ -48,30 +86,33 @@ describe Mongoid::Criteria do
48
86
  )
49
87
  end
50
88
  end
89
+
51
90
  context 'default' do
52
91
  it 'scrolls all' do
53
92
  records = []
54
- Feed::Item.all.scroll(cursor_type) do |record, _next_cursor|
93
+ Feed::Item.all.scroll(cursor_type) do |record, _iterator|
55
94
  records << record
56
95
  end
57
96
  expect(records.size).to eq 10
58
97
  expect(records).to eq Feed::Item.all.to_a
59
98
  end
99
+
60
100
  it 'does not change original criteria' do
61
101
  criteria = Feed::Item.where(:a_time.gt => Time.new(2013, 7, 22, 1, 2, 3))
62
102
  original_criteria = criteria.dup
63
103
  criteria.limit(2).scroll(cursor_type)
64
104
  expect(criteria).to eq original_criteria
65
105
  cursor = nil
66
- criteria.limit(2).scroll(cursor) do |_record, next_cursor|
67
- cursor = next_cursor
106
+ criteria.limit(2).scroll(cursor) do |_record, iterator|
107
+ cursor = iterator.next_cursor
68
108
  end
69
- criteria.scroll(cursor) do |_record, next_cursor|
70
- cursor = next_cursor
109
+ criteria.scroll(cursor) do |_record, iterator|
110
+ cursor = iterator.next_cursor
71
111
  end
72
112
  expect(criteria).to eq original_criteria
73
113
  end
74
114
  end
115
+
75
116
  context 'with a foreign key' do
76
117
  it 'sorts by object id' do
77
118
  records = []
@@ -79,92 +120,144 @@ describe Mongoid::Criteria do
79
120
  expect(records).not_to be_empty
80
121
  end
81
122
  end
123
+
82
124
  { a_string: String, a_integer: Integer, a_date: Date, a_datetime: DateTime }.each_pair do |field_name, field_type|
83
125
  context field_type do
84
126
  it 'scrolls all with a block' do
85
127
  records = []
86
- Feed::Item.asc(field_name).scroll(cursor_type) do |record, _next_cursor|
128
+ Feed::Item.asc(field_name).scroll(cursor_type) do |record, _iterator|
87
129
  records << record
88
130
  end
89
131
  expect(records.size).to eq 10
90
132
  expect(records).to eq Feed::Item.all.to_a
91
133
  end
134
+
92
135
  it 'scrolls all with a break' do
93
136
  records = []
94
137
  cursor = nil
95
- Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, next_cursor|
138
+ Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, iterator|
96
139
  records << record
97
- cursor = next_cursor
140
+ cursor = iterator.next_cursor
98
141
  end
99
142
  expect(records.size).to eq 5
100
- Feed::Item.asc(field_name).scroll(cursor) do |record, next_cursor|
143
+ Feed::Item.asc(field_name).scroll(cursor) do |record, iterator|
101
144
  records << record
102
- cursor = next_cursor
145
+ cursor = iterator.next_cursor
103
146
  end
104
147
  expect(records.size).to eq 10
105
148
  expect(records).to eq Feed::Item.all.to_a
106
149
  end
150
+
107
151
  it 'scrolls from a cursor' do
108
152
  last_record = nil
109
153
  cursor = nil
110
- Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, next_cursor|
154
+ Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, iterator|
111
155
  last_record = record
112
- cursor = next_cursor
156
+ cursor = iterator.next_cursor
113
157
  end
114
158
  sixth_item = Feed::Item.asc(field_name).to_a[5]
115
159
  from_item = Feed::Item.asc(field_name).scroll(cursor).to_a.first
116
160
  expect(from_item).to eq sixth_item
117
161
  end
162
+
118
163
  it 'includes the current record when Mongoid::Scroll::Cursor#include_current is true' do
119
164
  last_record = nil
120
165
  cursor = nil
121
- Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, next_cursor|
166
+ Feed::Item.asc(field_name).limit(5).scroll(cursor_type) do |record, iterator|
122
167
  last_record = record
123
- cursor = next_cursor
168
+ cursor = iterator.next_cursor
124
169
  end
125
170
  fifth_item = last_record
126
171
  cursor.include_current = true
127
172
  from_item = Feed::Item.asc(field_name).scroll(cursor).to_a.first
128
173
  expect(from_item).to eq fifth_item
129
174
  end
175
+
130
176
  it 'scrolls in descending order' do
131
177
  records = []
132
- Feed::Item.desc(field_name).limit(3).scroll(cursor_type) do |record, _next_cursor|
178
+ Feed::Item.desc(field_name).limit(3).scroll(cursor_type) do |record, _iterator|
133
179
  records << record
134
180
  end
135
181
  expect(records.size).to eq 3
136
182
  expect(records).to eq Feed::Item.desc(field_name).limit(3).to_a
137
183
  end
184
+
138
185
  it 'map' do
139
186
  record = Feed::Item.desc(field_name).limit(3).scroll(cursor_type).map { |r| r }.last
140
- expect(record).to_not be nil
187
+ expect(record).not_to be_nil
141
188
  cursor = cursor_type.from_record(record, field_type: field_type, field_name: field_name)
142
- expect(cursor).to_not be nil
189
+ expect(cursor).not_to be_nil
143
190
  expect(cursor.tiebreak_id).to eq record.id
144
191
  expect(cursor.value).to eq record.send(field_name)
145
192
  end
193
+
146
194
  it 'can be reused' do
147
195
  ids = Feed::Item.asc(field_name).limit(2).map(&:id)
148
- Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, cursor|
149
- cursor.include_current = true
150
- expect(Feed::Item.asc(field_name).limit(2).scroll(cursor).pluck(:id)).to eq ids
196
+ Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, iterator|
197
+ iterator.next_cursor.include_current = true
198
+ expect(Feed::Item.asc(field_name).limit(2).scroll(iterator.next_cursor).pluck(:id)).to eq ids
151
199
  break
152
200
  end
153
201
  end
202
+
154
203
  it 'can be re-created and reused' do
155
204
  ids = Feed::Item.asc(field_name).limit(2).map(&:id)
156
- Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, cursor|
157
- new_cursor = cursor_type.new(cursor.to_s, field_type: field_type, field_name: field_name)
205
+ Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, iterator|
206
+ new_cursor = cursor_type.new(iterator.next_cursor.to_s, field_type: field_type, field_name: field_name)
158
207
  new_cursor.include_current = true
159
208
  expect(Feed::Item.asc(field_name).limit(2).scroll(new_cursor).pluck(:id)).to eq ids
160
209
  break
161
210
  end
162
211
  end
212
+
213
+ it 'can scroll back with the previous cursor' do
214
+ first_iterator = nil
215
+ second_iterator = nil
216
+ third_iterator = nil
217
+
218
+ Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, iterator|
219
+ first_iterator = iterator
220
+ end
221
+
222
+ Feed::Item.asc(field_name).limit(2).scroll(first_iterator.next_cursor) do |_, iterator|
223
+ second_iterator = iterator
224
+ end
225
+
226
+ Feed::Item.asc(field_name).limit(2).scroll(second_iterator.next_cursor) do |_, iterator|
227
+ third_iterator = iterator
228
+ end
229
+
230
+ records = Feed::Item.asc(field_name)
231
+ expect(Feed::Item.asc(field_name).limit(2).scroll(second_iterator.previous_cursor)).to eq(records.limit(2))
232
+ expect(Feed::Item.asc(field_name).limit(2).scroll(third_iterator.previous_cursor)).to eq(records.skip(2).limit(2))
233
+ end
234
+
235
+ it 'can loop over the same records with the current cursor' do
236
+ current_cursor = nil
237
+ cursor = cursor_type.from_record Feed::Item.skip(4).first, field_name: field_name, field_type: field_type, include_current: true
238
+
239
+ Feed::Item.asc(field_name).limit(2).scroll(cursor) do |_, iterator|
240
+ current_cursor = iterator.current_cursor
241
+ end
242
+
243
+ expect(Feed::Item.asc(field_name).limit(2).scroll(current_cursor).to_a).to eq(Feed::Item.asc(field_name).skip(4).limit(2).to_a)
244
+ end
245
+
246
+ it 'can loop over the first records with the first page cursor' do
247
+ first_cursor = nil
248
+
249
+ Feed::Item.asc(field_name).limit(2).scroll(cursor_type) do |_, it|
250
+ first_cursor = it.first_cursor
251
+ end
252
+
253
+ expect(Feed::Item.asc(field_name).limit(2).scroll(first_cursor).to_a).to eq(Feed::Item.asc(field_name).limit(2).to_a)
254
+ end
163
255
  end
164
256
  end
165
257
  end
258
+
166
259
  context 'with logic in initial criteria' do
167
- before :each do
260
+ before do
168
261
  3.times do |i|
169
262
  Feed::Item.create!(
170
263
  name: "Feed Item #{i}",
@@ -184,63 +277,69 @@ describe Mongoid::Criteria do
184
277
  a_time: Time.new(2014, 2, 2, 1, 2, 3)
185
278
  )
186
279
  end
280
+
187
281
  it 'respects original criteria with OR logic' do
188
282
  criteria = Feed::Item.where(
189
283
  '$or' => [{ :a_time.gt => Time.new(2015, 7, 22, 1, 2, 3) }, { :a_time.lte => Time.new(2015, 7, 22, 1, 2, 3), :a_date.gte => Date.new(2015, 7, 30) }]
190
284
  ).asc(:a_time)
191
285
  records = []
192
286
  cursor = nil
193
- criteria.limit(2).scroll(cursor_type) do |record, next_cursor|
287
+ criteria.limit(2).scroll(cursor_type) do |record, iterator|
194
288
  records << record
195
- cursor = next_cursor
289
+ cursor = iterator.next_cursor
196
290
  end
197
291
  expect(records.size).to eq 2
198
292
  expect(records.map(&:name)).to eq ['Feed Item 0', 'Feed Item 1']
199
293
  records = []
200
- criteria.limit(2).scroll(cursor) do |record, next_cursor|
294
+ criteria.limit(2).scroll(cursor) do |record, iterator|
201
295
  records << record
202
- cursor = next_cursor
296
+ cursor = iterator.next_cursor
203
297
  end
204
298
  expect(records.size).to eq 1
205
299
  expect(records.map(&:name)).to eq ['Feed Item 2']
206
300
  end
207
301
  end
302
+
208
303
  context 'with embeddable objects' do
209
304
  before do
210
305
  @item = Feed::Item.create! a_integer: 1, name: 'item'
211
306
  @embedded_item = Feed::EmbeddedItem.create! name: 'embedded', item: @item
212
307
  end
308
+
213
309
  it 'respects embedded queries' do
214
310
  records = []
215
311
  criteria = @item.embedded_items.limit(2)
216
- criteria.scroll(cursor_type) do |record, _next_cursor|
312
+ criteria.scroll(cursor_type) do |record, _iterator|
217
313
  records << record
218
314
  end
219
315
  expect(records.size).to eq 1
220
316
  expect(records.map(&:name)).to eq ['embedded']
221
317
  end
222
318
  end
319
+
223
320
  context 'with overlapping data', if: MongoDB.mmapv1? do
224
- before :each do
321
+ before do
225
322
  3.times { Feed::Item.create! a_integer: 5 }
226
323
  Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
227
324
  end
325
+
228
326
  it 'natural order is different from order by id' do
229
327
  # natural order isn't necessarily going to be the same as _id order
230
328
  # if a document is updated and grows in size, it may need to be relocated and
231
329
  # thus cause the natural order to change
232
- expect(Feed::Item.order_by('$natural' => 1).to_a).to_not eq(Feed::Item.order_by(_id: 1).to_a)
330
+ expect(Feed::Item.order_by('$natural' => 1).to_a).not_to eq(Feed::Item.order_by(_id: 1).to_a)
233
331
  end
332
+
234
333
  [{ a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
235
334
  it "scrolls by #{sort_order}" do
236
335
  records = []
237
336
  cursor = nil
238
- Feed::Item.order_by(sort_order).limit(2).scroll(cursor_type) do |record, next_cursor|
337
+ Feed::Item.order_by(sort_order).limit(2).scroll(cursor_type) do |record, iterator|
239
338
  records << record
240
- cursor = next_cursor
339
+ cursor = iterator.next_cursor
241
340
  end
242
341
  expect(records.size).to eq 2
243
- Feed::Item.order_by(sort_order).scroll(cursor) do |record, _next_cursor|
342
+ Feed::Item.order_by(sort_order).scroll(cursor) do |record, _iterator|
244
343
  records << record
245
344
  end
246
345
  expect(records.size).to eq 3
@@ -248,10 +347,12 @@ describe Mongoid::Criteria do
248
347
  end
249
348
  end
250
349
  end
350
+
251
351
  context 'with several records having the same value' do
252
- before :each do
352
+ before do
253
353
  3.times { Feed::Item.create! a_integer: 5 }
254
354
  end
355
+
255
356
  it 'returns records from the current one when Mongoid::Scroll::Cursor#include_current is true' do
256
357
  _first_item, second_item, third_item = Feed::Item.asc(:a_integer).to_a
257
358
  cursor = Mongoid::Scroll::Cursor.from_record(second_item, field: Feed::Item.fields['a_integer'])
@@ -260,6 +361,7 @@ describe Mongoid::Criteria do
260
361
  expect(items).to eq([second_item, third_item])
261
362
  end
262
363
  end
364
+
263
365
  context 'with DateTime with a milisecond precision' do
264
366
  let!(:item_1) { Feed::Item.create!(a_datetime: DateTime.new(2013, 1, 21, 1, 42, 3.1, 'UTC')) }
265
367
  let!(:item_2) { Feed::Item.create!(a_datetime: DateTime.new(2013, 1, 21, 1, 42, 3.2, 'UTC')) }
@@ -268,9 +370,9 @@ describe Mongoid::Criteria do
268
370
  it 'doesn\'t lose the precision when rebuilding the cursor' do
269
371
  records = []
270
372
  cursor = nil
271
- Feed::Item.order_by(a_datetime: 1).limit(2).scroll(cursor_type) do |record, next_cursor|
373
+ Feed::Item.order_by(a_datetime: 1).limit(2).scroll(cursor_type) do |record, iterator|
272
374
  records << record
273
- cursor = next_cursor
375
+ cursor = iterator.next_cursor
274
376
  end
275
377
  expect(records).to eq [item_1, item_2]
276
378
  cursor = cursor_type.new(cursor.to_s, field: Feed::Item.fields['a_datetime'])
@@ -5,125 +5,158 @@ describe Mongoid::Scroll::Cursor do
5
5
  subject do
6
6
  Mongoid::Scroll::Cursor.new nil, field_name: 'a_string', field_type: String
7
7
  end
8
- its(:tiebreak_id) { should be_nil }
9
- its(:value) { should be_nil }
10
- its(:criteria) { should eq({}) }
8
+
9
+ its(:tiebreak_id) { is_expected.to be_nil }
10
+ its(:value) { is_expected.to be_nil }
11
+ its(:criteria) { is_expected.to eq({}) }
11
12
  end
13
+
12
14
  context 'an invalid cursor' do
13
15
  it 'raises InvalidCursorError' do
14
16
  expect { Mongoid::Scroll::Cursor.new 'invalid', field_name: 'a_string', field_type: String }.to raise_error Mongoid::Scroll::Errors::InvalidCursorError,
15
17
  /The cursor supplied is invalid: invalid./
16
18
  end
17
19
  end
20
+
18
21
  context 'an id field cursor' do
19
- let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
20
22
  subject do
21
23
  Mongoid::Scroll::Cursor.new "#{feed_item.id}:#{feed_item.id}", field_name: '_id', field_type: BSON::ObjectId, direction: 1
22
24
  end
23
- its(:value) { should eq feed_item.id }
24
- its(:tiebreak_id) { should eq feed_item.id }
25
+
26
+ let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
27
+
28
+ its(:value) { is_expected.to eq feed_item.id }
29
+ its(:tiebreak_id) { is_expected.to eq feed_item.id }
30
+
25
31
  its(:criteria) do
26
- should eq('$or' => [
27
- { '_id' => { '$gt' => BSON::ObjectId(feed_item.id.to_s) } }
28
- ])
32
+ is_expected.to eq('$or' => [
33
+ { '_id' => { '$gt' => BSON::ObjectId(feed_item.id.to_s) } }
34
+ ])
29
35
  end
30
36
  end
37
+
31
38
  context 'a string field cursor' do
32
- let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
33
39
  subject do
34
40
  Mongoid::Scroll::Cursor.new "#{feed_item.a_string}:#{feed_item.id}", field_name: 'a_string', field_type: String
35
41
  end
36
- its(:value) { should eq feed_item.a_string }
37
- its(:tiebreak_id) { should eq feed_item.id }
42
+
43
+ let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
44
+
45
+ its(:value) { is_expected.to eq feed_item.a_string }
46
+ its(:tiebreak_id) { is_expected.to eq feed_item.id }
47
+
38
48
  its(:criteria) do
39
- should eq('$or' => [
40
- { 'a_string' => { '$gt' => feed_item.a_string } },
41
- { 'a_string' => feed_item.a_string, '_id' => { '$gt' => feed_item.id } }
42
- ])
49
+ is_expected.to eq('$or' => [
50
+ { 'a_string' => { '$gt' => feed_item.a_string } },
51
+ { 'a_string' => feed_item.a_string, '_id' => { '$gt' => feed_item.id } }
52
+ ])
43
53
  end
44
54
  end
55
+
45
56
  context 'an integer field cursor' do
46
- let(:feed_item) { Feed::Item.create!(a_integer: 10) }
47
57
  subject do
48
58
  Mongoid::Scroll::Cursor.new "#{feed_item.a_integer}:#{feed_item.id}", field_name: 'a_integer', field_type: Integer
49
59
  end
50
- its(:value) { should eq feed_item.a_integer }
51
- its(:tiebreak_id) { should eq feed_item.id }
60
+
61
+ let(:feed_item) { Feed::Item.create!(a_integer: 10) }
62
+
63
+ its(:value) { is_expected.to eq feed_item.a_integer }
64
+ its(:tiebreak_id) { is_expected.to eq feed_item.id }
65
+
52
66
  its(:criteria) do
53
- should eq('$or' => [
54
- { 'a_integer' => { '$gt' => feed_item.a_integer } },
55
- { 'a_integer' => feed_item.a_integer, '_id' => { '$gt' => feed_item.id } }
56
- ])
67
+ is_expected.to eq('$or' => [
68
+ { 'a_integer' => { '$gt' => feed_item.a_integer } },
69
+ { 'a_integer' => feed_item.a_integer, '_id' => { '$gt' => feed_item.id } }
70
+ ])
57
71
  end
58
72
  end
73
+
59
74
  context 'a date/time field cursor' do
60
- let(:feed_item) { Feed::Item.create!(a_datetime: DateTime.new(2013, 12, 21, 1, 42, 3, 'UTC')) }
61
75
  subject do
62
76
  Mongoid::Scroll::Cursor.new "#{feed_item.a_datetime.utc.to_f.round(3)}:#{feed_item.id}", field_name: 'a_datetime', field_type: DateTime
63
77
  end
64
- its(:value) { should eq feed_item.a_datetime }
65
- its(:tiebreak_id) { should eq feed_item.id }
66
- its(:to_s) { should eq "#{feed_item.a_datetime.utc.to_f.round(3)}:#{feed_item.id}" }
78
+
79
+ let(:feed_item) { Feed::Item.create!(a_datetime: DateTime.new(2013, 12, 21, 1, 42, 3, 'UTC')) }
80
+
81
+ its(:value) { is_expected.to eq feed_item.a_datetime }
82
+ its(:tiebreak_id) { is_expected.to eq feed_item.id }
83
+ its(:to_s) { is_expected.to eq "#{feed_item.a_datetime.utc.to_f.round(3)}:#{feed_item.id}" }
84
+
67
85
  its(:criteria) do
68
- should eq('$or' => [
69
- { 'a_datetime' => { '$gt' => feed_item.a_datetime } },
70
- { 'a_datetime' => feed_item.a_datetime, '_id' => { '$gt' => feed_item.id } }
71
- ])
86
+ is_expected.to eq('$or' => [
87
+ { 'a_datetime' => { '$gt' => feed_item.a_datetime } },
88
+ { 'a_datetime' => feed_item.a_datetime, '_id' => { '$gt' => feed_item.id } }
89
+ ])
72
90
  end
73
91
  end
92
+
74
93
  context 'a date field cursor' do
75
- let(:feed_item) { Feed::Item.create!(a_date: Date.new(2013, 12, 21)) }
76
94
  subject do
77
95
  Mongoid::Scroll::Cursor.new "#{feed_item.a_date.to_datetime.to_i}:#{feed_item.id}", field_name: 'a_date', field_type: Date
78
96
  end
79
- its(:value) { should eq feed_item.a_date }
80
- its(:tiebreak_id) { should eq feed_item.id }
81
- its(:to_s) { should eq "#{feed_item.a_date.to_datetime.to_i}:#{feed_item.id}" }
97
+
98
+ let(:feed_item) { Feed::Item.create!(a_date: Date.new(2013, 12, 21)) }
99
+
100
+ its(:value) { is_expected.to eq feed_item.a_date }
101
+ its(:tiebreak_id) { is_expected.to eq feed_item.id }
102
+ its(:to_s) { is_expected.to eq "#{feed_item.a_date.to_datetime.to_i}:#{feed_item.id}" }
103
+
82
104
  its(:criteria) do
83
- should eq('$or' => [
84
- { 'a_date' => { '$gt' => feed_item.a_date.to_datetime } },
85
- { 'a_date' => feed_item.a_date.to_datetime, '_id' => { '$gt' => feed_item.id } }
86
- ])
105
+ is_expected.to eq('$or' => [
106
+ { 'a_date' => { '$gt' => feed_item.a_date.to_datetime } },
107
+ { 'a_date' => feed_item.a_date.to_datetime, '_id' => { '$gt' => feed_item.id } }
108
+ ])
87
109
  end
88
110
  end
111
+
89
112
  context 'a time field cursor' do
90
- let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
91
113
  subject do
92
114
  Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}", field_name: 'a_time', field_type: Time
93
115
  end
94
- its(:value) { should eq feed_item.a_time }
95
- its(:tiebreak_id) { should eq feed_item.id }
96
- its(:to_s) { should eq "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}" }
116
+
117
+ let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
118
+
119
+ its(:value) { is_expected.to eq feed_item.a_time }
120
+ its(:tiebreak_id) { is_expected.to eq feed_item.id }
121
+ its(:to_s) { is_expected.to eq "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}" }
122
+
97
123
  its(:criteria) do
98
- should eq('$or' => [
99
- { 'a_time' => { '$gt' => feed_item.a_time } },
100
- { 'a_time' => feed_item.a_time, '_id' => { '$gt' => feed_item.id } }
101
- ])
124
+ is_expected.to eq('$or' => [
125
+ { 'a_time' => { '$gt' => feed_item.a_time } },
126
+ { 'a_time' => feed_item.a_time, '_id' => { '$gt' => feed_item.id } }
127
+ ])
102
128
  end
103
129
  end
130
+
104
131
  context 'a time field cursor with a field option' do
105
- let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
106
132
  subject do
107
133
  Mongoid::Scroll::Cursor.new "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}", field: Feed::Item.fields['a_time']
108
134
  end
109
- its(:value) { should eq feed_item.a_time }
110
- its(:tiebreak_id) { should eq feed_item.id }
111
- its(:to_s) { should eq "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}" }
135
+
136
+ let(:feed_item) { Feed::Item.create!(a_time: Time.new(2013, 12, 21, 1, 2, 3)) }
137
+
138
+ its(:value) { is_expected.to eq feed_item.a_time }
139
+ its(:tiebreak_id) { is_expected.to eq feed_item.id }
140
+ its(:to_s) { is_expected.to eq "#{feed_item.a_time.to_f.round(3)}:#{feed_item.id}" }
141
+
112
142
  its(:criteria) do
113
- should eq('$or' => [
114
- { 'a_time' => { '$gt' => feed_item.a_time } },
115
- { 'a_time' => feed_item.a_time, '_id' => { '$gt' => feed_item.id } }
116
- ])
143
+ is_expected.to eq('$or' => [
144
+ { 'a_time' => { '$gt' => feed_item.a_time } },
145
+ { 'a_time' => feed_item.a_time, '_id' => { '$gt' => feed_item.id } }
146
+ ])
117
147
  end
118
148
  end
149
+
119
150
  context 'an array field cursor' do
120
151
  let(:feed_item) { Feed::Item.create!(a_array: %w[x y]) }
152
+
121
153
  it 'is not supported' do
122
154
  expect do
123
155
  Mongoid::Scroll::Cursor.from_record feed_item, field_name: 'a_array', field_type: Array
124
156
  end.to raise_error Mongoid::Scroll::Errors::UnsupportedFieldTypeError, /The type of the field 'a_array' is not supported: Array./
125
157
  end
126
158
  end
159
+
127
160
  context 'an invalid field cursor' do
128
161
  it 'raises ArgumentError' do
129
162
  expect do
@@ -131,23 +164,38 @@ describe Mongoid::Scroll::Cursor do
131
164
  end.to raise_error ArgumentError
132
165
  end
133
166
  end
134
- context 'a cursor with include_current set to true' do
167
+
168
+ context 'an invalid type cursor' do
135
169
  let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
170
+
171
+ it 'raises Mongoid::Scroll::Errors::UnsupportedTypeError' do
172
+ expect do
173
+ Mongoid::Scroll::Cursor.new "#{feed_item.a_string}:#{feed_item.id}", field_name: 'a_string', field_type: String, include_current: true, type: :invalid
174
+ end.to raise_error Mongoid::Scroll::Errors::UnsupportedTypeError, /The type supplied in the cursor is not supported: invalid./
175
+ end
176
+ end
177
+
178
+ context 'a cursor with include_current set to true' do
136
179
  subject do
137
180
  Mongoid::Scroll::Cursor.new "#{feed_item.a_string}:#{feed_item.id}", field_name: 'a_string', field_type: String, include_current: true
138
181
  end
139
- its(:value) { should eq 'astring' }
140
- its(:tiebreak_id) { should eq feed_item.id }
182
+
183
+ let(:feed_item) { Feed::Item.create!(a_string: 'astring') }
184
+
185
+ its(:value) { is_expected.to eq 'astring' }
186
+ its(:tiebreak_id) { is_expected.to eq feed_item.id }
187
+
141
188
  its(:criteria) do
142
- should eq('$or' => [
143
- { 'a_string' => { '$gt' => 'astring' } },
144
- { '_id' => { '$gte' => BSON::ObjectId(feed_item.id.to_s) }, 'a_string' => 'astring' }
145
- ])
189
+ is_expected.to eq('$or' => [
190
+ { 'a_string' => { '$gt' => 'astring' } },
191
+ { '_id' => { '$gte' => BSON::ObjectId(feed_item.id.to_s) }, 'a_string' => 'astring' }
192
+ ])
146
193
  end
147
194
  end
195
+
148
196
  context 'criteria' do
149
197
  context 'with data' do
150
- before :each do
198
+ before do
151
199
  3.times do |i|
152
200
  Feed::Item.create!(
153
201
  name: "Feed Item #{i}",
@@ -159,6 +207,7 @@ describe Mongoid::Scroll::Cursor do
159
207
  a_time: Time.new(2014, 2, 2, 1, 2, 3)
160
208
  )
161
209
  end
210
+
162
211
  it 'merges cursor criteria when no sort field is given' do
163
212
  criteria = Feed::Item.where(:a_time.gt => Time.new(2013, 7, 22, 1, 2, 3))
164
213
  feed_item = Feed::Item.where(name: 'Feed Item 1').first
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Mongoid::Scroll::VERSION do
4
- it { should_not be_nil }
4
+ it { is_expected.not_to be_nil }
5
5
  end