mongoid-scroll 0.3.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +36 -0
  3. data/.github/workflows/danger.yml +17 -0
  4. data/.rubocop_todo.yml +24 -19
  5. data/CHANGELOG.md +14 -0
  6. data/Gemfile +10 -16
  7. data/LICENSE.md +1 -1
  8. data/README.md +59 -46
  9. data/RELEASING.md +4 -4
  10. data/UPGRADING.md +14 -0
  11. data/examples/mongoid_scroll_feed.rb +2 -8
  12. data/lib/config/locales/en.yml +8 -0
  13. data/lib/mongo/scrollable.rb +8 -3
  14. data/lib/mongoid/criteria/scrollable/cursors.rb +18 -0
  15. data/lib/mongoid/criteria/scrollable/fields.rb +21 -0
  16. data/lib/mongoid/criteria/scrollable.rb +13 -12
  17. data/lib/mongoid/scroll/base64_encoded_cursor.rb +40 -0
  18. data/lib/mongoid/scroll/base_cursor.rb +123 -0
  19. data/lib/mongoid/scroll/cursor.rb +24 -87
  20. data/lib/mongoid/scroll/errors/base.rb +1 -1
  21. data/lib/mongoid/scroll/errors/invalid_base64_cursor_error.rb +11 -0
  22. data/lib/mongoid/scroll/errors/invalid_base_cursor_error.rb +8 -0
  23. data/lib/mongoid/scroll/errors/invalid_cursor_error.rb +1 -1
  24. data/lib/mongoid/scroll/errors/mismatched_sort_fields_error.rb +15 -0
  25. data/lib/mongoid/scroll/errors.rb +3 -0
  26. data/lib/mongoid/scroll/version.rb +1 -1
  27. data/lib/mongoid-scroll.rb +4 -1
  28. data/spec/mongo/collection_view_spec.rb +122 -105
  29. data/spec/mongoid/base64_encoded_cursor_spec.rb +233 -0
  30. data/spec/mongoid/criteria_spec.rb +236 -197
  31. data/spec/mongoid/{scroll_cursor_spec.rb → cursor_spec.rb} +48 -12
  32. data/spec/spec_helper.rb +1 -1
  33. data/spec/support/feed/item.rb +1 -1
  34. metadata +19 -13
  35. data/.travis.yml +0 -35
  36. data/examples/moped_scroll_feed.rb +0 -45
  37. data/lib/moped/scrollable.rb +0 -38
  38. data/spec/moped/query_spec.rb +0 -126
@@ -0,0 +1,123 @@
1
+ module Mongoid
2
+ module Scroll
3
+ class BaseCursor
4
+ attr_accessor :value, :tiebreak_id, :field_type, :field_name, :direction, :include_current
5
+
6
+ def initialize(value, options = {})
7
+ @value = value
8
+ @tiebreak_id = options[:tiebreak_id]
9
+ @field_type = options[:field_type]
10
+ @field_name = options[:field_name]
11
+ @direction = options[:direction] || 1
12
+ @include_current = options[:include_current] || false
13
+ end
14
+
15
+ def criteria
16
+ mongo_value = value.class.mongoize(value) if value
17
+ cursor_criteria = { field_name => { compare_direction => mongo_value } } if mongo_value
18
+ tiebreak_criteria = { field_name => mongo_value, :_id => { tiebreak_compare_direction => tiebreak_id } } if mongo_value && tiebreak_id
19
+ cursor_selector = if Mongoid::Compatibility::Version.mongoid6_or_newer?
20
+ Mongoid::Criteria::Queryable::Selector.new
21
+ else
22
+ Origin::Selector.new
23
+ end
24
+ cursor_selector['$or'] = [cursor_criteria, tiebreak_criteria].compact if cursor_criteria || tiebreak_criteria
25
+ cursor_selector.__evolve_object_id__
26
+ end
27
+
28
+ def sort_options
29
+ {
30
+ field_type: field_type,
31
+ field_name: field_name,
32
+ direction: direction
33
+ }
34
+ end
35
+
36
+ def to_s
37
+ raise NotImplementedError.new(:to_s)
38
+ end
39
+
40
+ class << self
41
+ def from_record(record, options)
42
+ cursor = new(nil, options)
43
+ record_value = record.respond_to?(cursor.field_name) ? record.send(cursor.field_name) : record[cursor.field_name]
44
+ cursor.value = cursor.send(:parse_field_value, cursor.field_type, cursor.field_name, record_value)
45
+ cursor.tiebreak_id = record['_id']
46
+ cursor
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def parse_field_value(field_type, field_name, value)
53
+ return nil unless value
54
+
55
+ case field_type.to_s
56
+ when 'BSON::ObjectId' then BSON::ObjectId.from_string(value)
57
+ when 'String' then value.to_s == '' ? nil : value.to_s
58
+ when 'DateTime' then value.is_a?(DateTime) ? value : Time.at(value.to_i).to_datetime
59
+ when 'Time' then value.is_a?(Time) ? value : Time.at(value.to_i)
60
+ when 'Date' then value.is_a?(Date) ? value : Time.at(value.to_i).utc.to_date
61
+ when 'Float' then value.to_f
62
+ when 'Integer' then value.to_i
63
+ else
64
+ raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
65
+ end
66
+ end
67
+
68
+ def transform_field_value(field_type, field_name, value)
69
+ return nil unless value
70
+
71
+ case field_type.to_s
72
+ when 'BSON::ObjectId' then value.to_s
73
+ when 'String' then value.to_s
74
+ when 'Date' then Time.utc(value.year, value.month, value.day).to_i
75
+ when 'DateTime', 'Time' then value.utc.to_i
76
+ when 'Float' then value.to_f
77
+ when 'Integer' then value.to_i
78
+ else
79
+ raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
80
+ end
81
+ end
82
+
83
+ def extract_field_options(options)
84
+ if options && (field_name = options[:field_name]) && (field_type = options[:field_type])
85
+ {
86
+ field_type: field_type.to_s,
87
+ field_name: field_name.to_s,
88
+ direction: options[:direction] || 1,
89
+ include_current: options[:include_current] || false
90
+ }
91
+ elsif options && (field = options[:field])
92
+ {
93
+ field_type: field.type.to_s,
94
+ field_name: field.name.to_s,
95
+ direction: options[:direction] || 1,
96
+ include_current: options[:include_current] || false
97
+ }
98
+ end
99
+ end
100
+
101
+ def compare_direction
102
+ direction == 1 ? '$gt' : '$lt'
103
+ end
104
+
105
+ def tiebreak_compare_direction
106
+ if include_current
107
+ case compare_direction
108
+ when '$gt'
109
+ '$gte'
110
+ when '$lt'
111
+ '$lte'
112
+ end
113
+ else
114
+ compare_direction
115
+ end
116
+ end
117
+
118
+ def parse(_value)
119
+ raise NotImplementedError.new(:parse)
120
+ end
121
+ end
122
+ end
123
+ end
@@ -1,97 +1,34 @@
1
1
  module Mongoid
2
2
  module Scroll
3
- class Cursor
4
- attr_accessor :value, :tiebreak_id, :field_type, :field_name, :direction
5
-
3
+ class Cursor < BaseCursor
6
4
  def initialize(value = nil, options = {})
7
- @field_type, @field_name = Mongoid::Scroll::Cursor.extract_field_options(options)
8
- @direction = options[:direction] || 1
9
- parse(value)
10
- end
11
-
12
- def criteria
13
- mongo_value = value.class.mongoize(value) if value
14
- compare_direction = direction == 1 ? '$gt' : '$lt'
15
- cursor_criteria = { field_name => { compare_direction => mongo_value } } if mongo_value
16
- tiebreak_criteria = { field_name => mongo_value, :_id => { compare_direction => tiebreak_id } } if mongo_value && tiebreak_id
17
- cursor_selector = if Mongoid::Compatibility::Version.mongoid6? || Mongoid::Compatibility::Version.mongoid7?
18
- Mongoid::Criteria::Queryable::Selector.new
19
- else
20
- Origin::Selector.new
21
- end
22
- cursor_selector['$or'] = [cursor_criteria, tiebreak_criteria].compact if cursor_criteria || tiebreak_criteria
23
- cursor_selector.__evolve_object_id__
24
- end
25
-
26
- class << self
27
- def from_record(record, options)
28
- cursor = Mongoid::Scroll::Cursor.new(nil, options)
29
- value = record.respond_to?(cursor.field_name) ? record.send(cursor.field_name) : record[cursor.field_name]
30
- cursor.value = Mongoid::Scroll::Cursor.parse_field_value(cursor.field_type, cursor.field_name, value)
31
- cursor.tiebreak_id = record['_id']
32
- cursor
5
+ options = extract_field_options(options)
6
+ raise ArgumentError.new 'Missing options[:field_name] and/or options[:field_type].' unless options
7
+ if value
8
+ parts = value.split(':') if value
9
+ unless parts && parts.length >= 2
10
+ raise Mongoid::Scroll::Errors::InvalidCursorError.new(cursor: value)
11
+ end
12
+ value = parse_field_value(
13
+ options[:field_type],
14
+ options[:field_name],
15
+ parts[0...-1].join(':')
16
+ )
17
+ options[:tiebreak_id] = BSON::ObjectId.from_string(parts[-1])
18
+ super value, options
19
+ else
20
+ super nil, options
33
21
  end
34
22
  end
35
23
 
36
24
  def to_s
37
- tiebreak_id ? [Mongoid::Scroll::Cursor.transform_field_value(field_type, field_name, value), tiebreak_id].join(':') : nil
38
- end
39
-
40
- private
41
-
42
- def parse(value)
43
- return unless value
44
- parts = value.split(':')
45
- unless parts.length >= 2
46
- raise Mongoid::Scroll::Errors::InvalidCursorError.new(cursor: value)
47
- end
48
- id = parts[-1]
49
- value = parts[0...-1].join(':')
50
- @value = Mongoid::Scroll::Cursor.parse_field_value(field_type, field_name, value)
51
- @tiebreak_id = if Mongoid::Compatibility::Version.mongoid3?
52
- Moped::BSON::ObjectId(id)
53
- else
54
- BSON::ObjectId.from_string(id)
55
- end
56
- end
57
-
58
- class << self
59
- def extract_field_options(options)
60
- if options && (field_name = options[:field_name]) && (field_type = options[:field_type])
61
- [field_type.to_s, field_name.to_s]
62
- elsif options && (field = options[:field])
63
- [field.type.to_s, field.name.to_s]
64
- else
65
- raise ArgumentError.new 'Missing options[:field_name] and/or options[:field_type].'
66
- end
67
- end
68
-
69
- def parse_field_value(field_type, field_name, value)
70
- case field_type.to_s
71
- when 'BSON::ObjectId', 'Moped::BSON::ObjectId' then value
72
- when 'String' then value.to_s
73
- when 'DateTime' then value.is_a?(DateTime) ? value : Time.at(value.to_i).to_datetime
74
- when 'Time' then value.is_a?(Time) ? value : Time.at(value.to_i)
75
- when 'Date' then value.is_a?(Date) ? value : Time.at(value.to_i).utc.to_date
76
- when 'Float' then value.to_f
77
- when 'Integer' then value.to_i
78
- else
79
- raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
80
- end
81
- end
82
-
83
- def transform_field_value(field_type, field_name, value)
84
- case field_type.to_s
85
- when 'BSON::ObjectId', 'Moped::BSON::ObjectId' then value
86
- when 'String' then value.to_s
87
- when 'Date' then Time.utc(value.year, value.month, value.day).to_i
88
- when 'DateTime', 'Time' then value.utc.to_i
89
- when 'Float' then value.to_f
90
- when 'Integer' then value.to_i
91
- else
92
- raise Mongoid::Scroll::Errors::UnsupportedFieldTypeError.new(field: field_name, type: field_type)
93
- end
94
- end
25
+ tiebreak_id ? [
26
+ transform_field_value(
27
+ field_type,
28
+ field_name,
29
+ value
30
+ ), tiebreak_id
31
+ ].join(':') : nil
95
32
  end
96
33
  end
97
34
  end
@@ -38,7 +38,7 @@ module Mongoid
38
38
  #
39
39
  # Returns a localized error message string.
40
40
  def translate(key, options)
41
- ::I18n.translate("#{BASE_KEY}.#{key}", { locale: :en }.merge(options)).strip
41
+ ::I18n.translate("#{BASE_KEY}.#{key}", **{ locale: :en }.merge(options)).strip
42
42
  end
43
43
 
44
44
  # Create the problem.
@@ -0,0 +1,11 @@
1
+ module Mongoid
2
+ module Scroll
3
+ module Errors
4
+ class InvalidBase64CursorError < InvalidBaseCursorError
5
+ def initialize(opts = {})
6
+ super(compose_message('invalid_base64_cursor', opts))
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ module Mongoid
2
+ module Scroll
3
+ module Errors
4
+ class InvalidBaseCursorError < Mongoid::Scroll::Errors::Base
5
+ end
6
+ end
7
+ end
8
+ end
@@ -1,7 +1,7 @@
1
1
  module Mongoid
2
2
  module Scroll
3
3
  module Errors
4
- class InvalidCursorError < Mongoid::Scroll::Errors::Base
4
+ class InvalidCursorError < InvalidBaseCursorError
5
5
  def initialize(opts = {})
6
6
  super(compose_message('invalid_cursor', opts))
7
7
  end
@@ -0,0 +1,15 @@
1
+ module Mongoid
2
+ module Scroll
3
+ module Errors
4
+ # Raised when the original sort params and the cursor sort params are different
5
+ class MismatchedSortFieldsError < Mongoid::Scroll::Errors::Base
6
+ def initialize(opts = {})
7
+ if opts[:diff] && opts[:diff].is_a?(Hash)
8
+ opts = opts.merge(diff: opts[:diff].keys.join(', '))
9
+ end
10
+ super(compose_message('mismatched_sort_fields', opts))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,8 @@
1
1
  require 'mongoid/scroll/errors/base'
2
2
  require 'mongoid/scroll/errors/multiple_sort_fields_error'
3
+ require 'mongoid/scroll/errors/mismatched_sort_fields_error'
4
+ require 'mongoid/scroll/errors/invalid_base_cursor_error'
3
5
  require 'mongoid/scroll/errors/invalid_cursor_error'
6
+ require 'mongoid/scroll/errors/invalid_base64_cursor_error'
4
7
  require 'mongoid/scroll/errors/no_such_field_error'
5
8
  require 'mongoid/scroll/errors/unsupported_field_type_error'
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module Scroll
3
- VERSION = '0.3.6'.freeze
3
+ VERSION = '1.0.0'.freeze
4
4
  end
5
5
  end
@@ -6,7 +6,10 @@ require 'mongoid'
6
6
  require 'mongoid-compatibility'
7
7
  require 'mongoid/scroll/version'
8
8
  require 'mongoid/scroll/errors'
9
+ require 'mongoid/scroll/base_cursor'
9
10
  require 'mongoid/scroll/cursor'
10
- require 'moped/scrollable' if Object.const_defined?(:Moped)
11
+ require 'mongoid/scroll/base64_encoded_cursor'
12
+ require 'mongoid/criteria/scrollable/fields'
13
+ require 'mongoid/criteria/scrollable/cursors'
11
14
  require 'mongo/scrollable' if Object.const_defined?(:Mongo)
12
15
  require 'mongoid/criteria/scrollable'
@@ -2,123 +2,140 @@ require 'spec_helper'
2
2
 
3
3
  if Object.const_defined?(:Mongo)
4
4
  describe Mongo::Collection::View do
5
- context 'scrollable' do
6
- subject do
7
- Mongoid.default_client['feed_items'].find
8
- end
9
- it ':scroll' do
10
- expect(subject).to respond_to(:scroll)
11
- end
12
- end
13
- context 'with multiple sort fields' do
14
- subject do
15
- Mongoid.default_client['feed_items'].find.sort(name: 1, value: -1)
16
- end
17
- it 'raises Mongoid::Scroll::Errors::MultipleSortFieldsError' do
18
- expect { subject.scroll }.to raise_error Mongoid::Scroll::Errors::MultipleSortFieldsError,
19
- /You're attempting to scroll over data with a sort order that includes multiple fields: name, value./
20
- end
21
- end
22
- context 'with no sort' do
23
- subject do
24
- Mongoid.default_client['feed_items'].find
25
- end
26
- it 'adds a default sort by _id' do
27
- expect(subject.scroll.sort).to eq('_id' => 1)
28
- end
29
- end
30
- context 'with data' do
31
- before :each do
32
- 10.times do |i|
33
- Mongoid.default_client['feed_items'].insert_one(
34
- a_string: i.to_s,
35
- a_integer: i,
36
- a_datetime: DateTime.mongoize(DateTime.new(2013, i + 1, 21, 1, 42, 3, 'UTC')),
37
- a_date: Date.mongoize(Date.new(2013, i + 1, 21)),
38
- a_time: Time.mongoize(Time.at(Time.now.to_i + i))
39
- )
5
+ [Mongoid::Scroll::Cursor, Mongoid::Scroll::Base64EncodedCursor].each do |cursor_type|
6
+ context cursor_type do
7
+ context 'scrollable' do
8
+ subject do
9
+ Mongoid.default_client['feed_items'].find
10
+ end
11
+ it ':scroll' do
12
+ expect(subject).to respond_to(:scroll)
13
+ end
40
14
  end
41
- end
42
- context 'default' do
43
- it 'scrolls all' do
44
- records = []
45
- Mongoid.default_client['feed_items'].find.scroll do |record, _next_cursor|
46
- records << record
15
+ context 'with multiple sort fields' do
16
+ subject do
17
+ Mongoid.default_client['feed_items'].find.sort(name: 1, value: -1)
18
+ end
19
+ it 'raises Mongoid::Scroll::Errors::MultipleSortFieldsError' do
20
+ expect { subject.scroll(cursor_type) }.to raise_error Mongoid::Scroll::Errors::MultipleSortFieldsError,
21
+ /You're attempting to scroll over data with a sort order that includes multiple fields: name, value./
47
22
  end
48
- expect(records.size).to eq 10
49
- expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
50
23
  end
51
- end
52
- { a_string: String, a_integer: Integer, a_date: Date, a_datetime: DateTime }.each_pair do |field_name, field_type|
53
- context field_type do
54
- it 'scrolls all with a block' do
55
- records = []
56
- Mongoid.default_client['feed_items'].find.sort(field_name => 1).scroll(nil, field_type: field_type) do |record, _next_cursor|
57
- records << record
58
- end
59
- expect(records.size).to eq 10
60
- expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
24
+ context 'with different sort fields between the cursor and the criteria' do
25
+ subject do
26
+ Mongoid.default_client['feed_items'].find.sort(name: -1)
61
27
  end
62
- it 'scrolls all with a break' do
63
- records = []
64
- cursor = nil
65
- Mongoid.default_client['feed_items'].find.sort(field_name => 1).limit(5).scroll(nil, field_type: field_type) do |record, next_cursor|
66
- records << record
67
- cursor = next_cursor
68
- end
69
- expect(records.size).to eq 5
70
- Mongoid.default_client['feed_items'].find.sort(field_name => 1).scroll(cursor, field_type: field_type) do |record, next_cursor|
71
- records << record
72
- cursor = next_cursor
28
+
29
+ it 'raises Mongoid::Scroll::Errors::MismatchedSortFieldsError' do
30
+ record = Feed::Item.create!
31
+ cursor = cursor_type.from_record(record, field: record.fields['a_string'])
32
+ expect(cursor).to be_a cursor_type
33
+ error_string = /You're attempting to scroll over data with a sort order that differs between the cursor and the original criteria: field_name, direction./
34
+ expect { subject.scroll(cursor, field_type: String) }.to raise_error Mongoid::Scroll::Errors::MismatchedSortFieldsError, error_string
35
+ end
36
+ end
37
+ context 'with no sort' do
38
+ subject do
39
+ Mongoid.default_client['feed_items'].find
40
+ end
41
+ it 'adds a default sort by _id' do
42
+ expect(subject.scroll(cursor_type).sort).to eq('_id' => 1)
43
+ end
44
+ end
45
+ context 'with data' do
46
+ before :each do
47
+ 10.times do |i|
48
+ Mongoid.default_client['feed_items'].insert_one(
49
+ a_string: i.to_s,
50
+ a_integer: i,
51
+ a_datetime: DateTime.mongoize(DateTime.new(2013, i + 1, 21, 1, 42, 3, 'UTC')),
52
+ a_date: Date.mongoize(Date.new(2013, i + 1, 21)),
53
+ a_time: Time.mongoize(Time.at(Time.now.to_i + i))
54
+ )
73
55
  end
74
- expect(records.size).to eq 10
75
- expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
76
56
  end
77
- it 'scrolls in descending order' do
78
- records = []
79
- Mongoid.default_client['feed_items'].find.sort(field_name => -1).limit(3).scroll(nil, field_type: field_type, field_name: field_name) do |record, _next_cursor|
80
- records << record
57
+ context 'default' do
58
+ it 'scrolls all' do
59
+ records = []
60
+ Mongoid.default_client['feed_items'].find.scroll(cursor_type) do |record, _next_cursor|
61
+ records << record
62
+ end
63
+ expect(records.size).to eq 10
64
+ expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
81
65
  end
82
- expect(records.size).to eq 3
83
- expect(records).to eq Mongoid.default_client['feed_items'].find.sort(field_name => -1).limit(3).to_a
84
66
  end
85
- it 'map' do
86
- record = Mongoid.default_client['feed_items'].find.limit(3).scroll(nil, field_type: field_type, field_name: field_name).map { |r| r }.last
87
- cursor = Mongoid::Scroll::Cursor.from_record(record, field_type: field_type, field_name: field_name)
88
- expect(cursor).to_not be nil
89
- expect(cursor.to_s.split(':')).to eq [
90
- Mongoid::Scroll::Cursor.transform_field_value(field_type, field_name, record[field_name.to_s]).to_s,
91
- record['_id'].to_s
92
- ]
67
+ { a_string: String, a_integer: Integer, a_date: Date, a_datetime: DateTime }.each_pair do |field_name, field_type|
68
+ context field_type do
69
+ it 'scrolls all with a block' do
70
+ records = []
71
+ Mongoid.default_client['feed_items'].find.sort(field_name => 1).scroll(cursor_type, field_type: field_type) do |record, _next_cursor|
72
+ records << record
73
+ end
74
+ expect(records.size).to eq 10
75
+ expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
76
+ end
77
+ it 'scrolls all with a break' do
78
+ records = []
79
+ cursor = nil
80
+ Mongoid.default_client['feed_items'].find.sort(field_name => 1).limit(5).scroll(cursor_type, field_type: field_type) do |record, next_cursor|
81
+ records << record
82
+ cursor = next_cursor
83
+ expect(cursor).to be_a cursor_type
84
+ end
85
+ expect(records.size).to eq 5
86
+ Mongoid.default_client['feed_items'].find.sort(field_name => 1).scroll(cursor, field_type: field_type) do |record, next_cursor|
87
+ records << record
88
+ cursor = next_cursor
89
+ expect(cursor).to be_a cursor_type
90
+ end
91
+ expect(records.size).to eq 10
92
+ expect(records).to eq Mongoid.default_client['feed_items'].find.to_a
93
+ end
94
+ it 'scrolls in descending order' do
95
+ records = []
96
+ Mongoid.default_client['feed_items'].find.sort(field_name => -1).limit(3).scroll(cursor_type, field_type: field_type, field_name: field_name) do |record, _next_cursor|
97
+ records << record
98
+ end
99
+ expect(records.size).to eq 3
100
+ expect(records).to eq Mongoid.default_client['feed_items'].find.sort(field_name => -1).limit(3).to_a
101
+ end
102
+ it 'map' do
103
+ record = Mongoid.default_client['feed_items'].find.limit(3).scroll(cursor_type, field_type: field_type, field_name: field_name).map { |r| r }.last
104
+ cursor = cursor_type.from_record(record, field_type: field_type, field_name: field_name)
105
+ expect(cursor).to_not be nil
106
+ expect(cursor.value).to eq record[field_name.to_s]
107
+ expect(cursor.tiebreak_id).to eq record['_id']
108
+ end
109
+ end
93
110
  end
94
111
  end
95
- end
96
- end
97
- context 'with overlapping data', if: MongoDB.mmapv1? do
98
- before :each do
99
- 3.times { Feed::Item.create! a_integer: 5 }
100
- Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
101
- end
102
- it 'natural order is different from order by id' do
103
- # natural order isn't necessarily going to be the same as _id order
104
- # if a document is updated and grows in size, it may need to be relocated and
105
- # thus cause the natural order to change
106
- expect(Feed::Item.order_by('$natural' => 1).to_a).to_not eq Feed::Item.order_by(_id: 1).to_a
107
- end
108
- [{ a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
109
- it "scrolls by #{sort_order}" do
110
- records = []
111
- cursor = nil
112
- Mongoid.default_client['feed_items'].find.sort(sort_order).limit(2).scroll do |record, next_cursor|
113
- records << record
114
- cursor = next_cursor
112
+ context 'with overlapping data', if: MongoDB.mmapv1? do
113
+ before :each do
114
+ 3.times { Feed::Item.create! a_integer: 5 }
115
+ Feed::Item.first.update_attributes!(name: Array(1000).join('a'))
116
+ end
117
+ it 'natural order is different from order by id' do
118
+ # natural order isn't necessarily going to be the same as _id order
119
+ # if a document is updated and grows in size, it may need to be relocated and
120
+ # thus cause the natural order to change
121
+ expect(Feed::Item.order_by('$natural' => 1).to_a).to_not eq Feed::Item.order_by(_id: 1).to_a
115
122
  end
116
- expect(records.size).to eq 2
117
- Mongoid.default_client['feed_items'].find.sort(sort_order).scroll(cursor) do |record, _next_cursor|
118
- records << record
123
+ [{ a_integer: 1 }, { a_integer: -1 }].each do |sort_order|
124
+ it "scrolls by #{sort_order}" do
125
+ records = []
126
+ cursor = nil
127
+ Mongoid.default_client['feed_items'].find.sort(sort_order).limit(2).scroll(cursor_type) do |record, next_cursor|
128
+ records << record
129
+ cursor = next_cursor
130
+ end
131
+ expect(records.size).to eq 2
132
+ Mongoid.default_client['feed_items'].find.sort(sort_order).scroll(cursor) do |record, _next_cursor|
133
+ records << record
134
+ end
135
+ expect(records.size).to eq 3
136
+ expect(records).to eq Mongoid.default_client['feed_items'].find.sort(sort_order.merge(_id: sort_order[:a_integer])).to_a
137
+ end
119
138
  end
120
- expect(records.size).to eq 3
121
- expect(records).to eq Mongoid.default_client['feed_items'].find.sort(sort_order.merge(_id: sort_order[:a_integer])).to_a
122
139
  end
123
140
  end
124
141
  end