mongoid-scroll 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,15 +10,24 @@ module Mongoid
10
10
  criteria = dup
11
11
  criteria.merge!(default_sort) if no_sort_option?
12
12
  cursor_options = build_cursor_options(criteria)
13
- cursor = cursor.is_a?(cursor_type) ? cursor : new_cursor(cursor_type, cursor, cursor_options)
13
+ cursor = new_cursor(cursor_type, cursor, cursor_options) unless cursor.is_a?(cursor_type)
14
14
  raise_mismatched_sort_fields_error!(cursor, cursor_options) if different_sort_fields?(cursor, cursor_options)
15
- cursor_criteria = build_cursor_criteria(criteria, cursor)
15
+ records = find_records(criteria, cursor)
16
16
  if block_given?
17
- cursor_criteria.order_by(_id: scroll_direction(criteria)).each do |record|
18
- yield record, cursor_from_record(cursor_type, record, cursor_options)
17
+ previous_cursor = nil
18
+ current_cursor = nil
19
+ records.each do |record|
20
+ previous_cursor ||= cursor_from_record(cursor_type, record, cursor_options.merge(type: :previous))
21
+ current_cursor ||= cursor_from_record(cursor_type, record, cursor_options.merge(include_current: true))
22
+ iterator = Mongoid::Criteria::Scrollable::Iterator.new(
23
+ previous_cursor: previous_cursor,
24
+ next_cursor: cursor_from_record(cursor_type, record, cursor_options),
25
+ current_cursor: current_cursor
26
+ )
27
+ yield record, iterator
19
28
  end
20
29
  else
21
- cursor_criteria
30
+ records
22
31
  end
23
32
  end
24
33
 
@@ -60,10 +69,21 @@ module Mongoid
60
69
  cursor_type.new(cursor, cursor_options)
61
70
  end
62
71
 
63
- def build_cursor_criteria(criteria, cursor)
72
+ def find_records(criteria, cursor)
64
73
  cursor_criteria = criteria.dup
65
74
  cursor_criteria.selector = { '$and' => [criteria.selector, cursor.criteria] }
66
- cursor_criteria
75
+ if cursor.type == :previous
76
+ pipeline = [
77
+ { '$match' => cursor_criteria.selector },
78
+ { '$sort' => { cursor.field_name => -cursor.direction } },
79
+ { '$limit' => criteria.options[:limit] },
80
+ { '$sort' => { cursor.field_name => cursor.direction } }
81
+ ]
82
+ aggregation = cursor_criteria.view.aggregate(pipeline)
83
+ aggregation.map { |record| Mongoid::Factory.from_db(cursor_criteria.klass, record) }
84
+ else
85
+ cursor_criteria.order_by(_id: scroll_direction(criteria))
86
+ end
67
87
  end
68
88
 
69
89
  def cursor_from_record(cursor_type, record, cursor_options)
@@ -79,4 +99,4 @@ module Mongoid
79
99
  end
80
100
  end
81
101
 
82
- Mongoid::Criteria.send(:include, Mongoid::Criteria::Scrollable)
102
+ Mongoid::Criteria.include Mongoid::Criteria::Scrollable
@@ -10,18 +10,19 @@ module Mongoid
10
10
  if value
11
11
  begin
12
12
  parsed = ::JSON.parse(::Base64.strict_decode64(value))
13
- rescue
13
+ rescue StandardError
14
14
  raise Mongoid::Scroll::Errors::InvalidBase64CursorError.new(cursor: value)
15
15
  end
16
- super parse_field_value(parsed['field_type'], parsed['field_name'], parsed['value']), {
16
+ super(parse_field_value(parsed['field_type'], parsed['field_name'], parsed['value']), {
17
17
  field_type: parsed['field_type'],
18
18
  field_name: parsed['field_name'],
19
19
  direction: parsed['direction'],
20
20
  include_current: parsed['include_current'],
21
- tiebreak_id: parsed['tiebreak_id'] && !parsed['tiebreak_id'].empty? ? BSON::ObjectId.from_string(parsed['tiebreak_id']) : nil
22
- }
21
+ tiebreak_id: parsed['tiebreak_id'] && !parsed['tiebreak_id'].empty? ? BSON::ObjectId.from_string(parsed['tiebreak_id']) : nil,
22
+ type: parsed['type'].try(:to_sym)
23
+ })
23
24
  else
24
- super nil, options
25
+ super(nil, options)
25
26
  end
26
27
  end
27
28
 
@@ -32,7 +33,8 @@ module Mongoid
32
33
  field_name: field_name,
33
34
  direction: direction,
34
35
  include_current: include_current,
35
- tiebreak_id: tiebreak_id && tiebreak_id.to_s
36
+ tiebreak_id: tiebreak_id && tiebreak_id.to_s,
37
+ type: type
36
38
  }.to_json)
37
39
  end
38
40
  end
@@ -1,7 +1,7 @@
1
1
  module Mongoid
2
2
  module Scroll
3
3
  class BaseCursor
4
- attr_accessor :value, :tiebreak_id, :field_type, :field_name, :direction, :include_current
4
+ attr_accessor :value, :tiebreak_id, :field_type, :field_name, :direction, :include_current, :type
5
5
 
6
6
  def initialize(value, options = {})
7
7
  @value = value
@@ -10,17 +10,16 @@ module Mongoid
10
10
  @field_name = options[:field_name]
11
11
  @direction = options[:direction] || 1
12
12
  @include_current = options[:include_current] || false
13
+ @type = options[:type] || :next
14
+
15
+ raise Mongoid::Scroll::Errors::UnsupportedTypeError.new(type: @type) unless %i[previous next].include?(@type)
13
16
  end
14
17
 
15
18
  def criteria
16
19
  mongo_value = value.class.mongoize(value) if value
17
20
  cursor_criteria = { field_name => { compare_direction => mongo_value } } if mongo_value
18
21
  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
22
+ cursor_selector = Mongoid::Criteria::Queryable::Selector.new
24
23
  cursor_selector['$or'] = [cursor_criteria, tiebreak_criteria].compact if cursor_criteria || tiebreak_criteria
25
24
  cursor_selector.__evolve_object_id__
26
25
  end
@@ -69,8 +68,7 @@ module Mongoid
69
68
  return nil unless value
70
69
 
71
70
  case field_type.to_s
72
- when 'BSON::ObjectId' then value.to_s
73
- when 'String' then value.to_s
71
+ when 'BSON::ObjectId', 'String' then value.to_s
74
72
  when 'Date' then Time.utc(value.year, value.month, value.day).to_i
75
73
  when 'DateTime', 'Time' then value.utc.to_f.round(3)
76
74
  when 'Float' then value.to_f
@@ -86,20 +84,23 @@ module Mongoid
86
84
  field_type: field_type.to_s,
87
85
  field_name: field_name.to_s,
88
86
  direction: options[:direction] || 1,
89
- include_current: options[:include_current] || false
87
+ include_current: options[:include_current] || false,
88
+ type: options[:type].try(:to_sym) || :next
90
89
  }
91
90
  elsif options && (field = options[:field])
92
91
  {
93
92
  field_type: field.type.to_s,
94
93
  field_name: field.name.to_s,
95
94
  direction: options[:direction] || 1,
96
- include_current: options[:include_current] || false
95
+ include_current: options[:include_current] || false,
96
+ type: options[:type].try(:to_sym) || :next
97
97
  }
98
98
  end
99
99
  end
100
100
 
101
101
  def compare_direction
102
- direction == 1 ? '$gt' : '$lt'
102
+ dir = type == :previous ? -direction : direction
103
+ dir == 1 ? '$gt' : '$lt'
103
104
  end
104
105
 
105
106
  def tiebreak_compare_direction
@@ -4,20 +4,20 @@ module Mongoid
4
4
  def initialize(value = nil, options = {})
5
5
  options = extract_field_options(options)
6
6
  raise ArgumentError.new 'Missing options[:field_name] and/or options[:field_type].' unless options
7
+
7
8
  if value
8
9
  parts = value.split(':') if value
9
- unless parts && parts.length >= 2
10
- raise Mongoid::Scroll::Errors::InvalidCursorError.new(cursor: value)
11
- end
10
+ raise Mongoid::Scroll::Errors::InvalidCursorError.new(cursor: value) unless parts && parts.length >= 2
11
+
12
12
  value = parse_field_value(
13
13
  options[:field_type],
14
14
  options[:field_name],
15
15
  parts[0...-1].join(':')
16
16
  )
17
17
  options[:tiebreak_id] = BSON::ObjectId.from_string(parts[-1])
18
- super value, options
18
+ super
19
19
  else
20
- super nil, options
20
+ super(nil, options)
21
21
  end
22
22
  end
23
23
 
@@ -21,13 +21,13 @@ module Mongoid
21
21
  @resolution = create_resolution(key, attributes)
22
22
 
23
23
  "\nProblem:\n #{@problem}" \
24
- "\nSummary:\n #{@summary}" \
25
- "\nResolution:\n #{@resolution}"
24
+ "\nSummary:\n #{@summary}" \
25
+ "\nResolution:\n #{@resolution}"
26
26
  end
27
27
 
28
28
  private
29
29
 
30
- BASE_KEY = 'mongoid.scroll.errors.messages'.freeze #:nodoc:
30
+ BASE_KEY = 'mongoid.scroll.errors.messages'.freeze # :nodoc:
31
31
 
32
32
  # Given the key of the specific error and the options hash, translate the
33
33
  # message.
@@ -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, **options).strip
42
42
  end
43
43
 
44
44
  # Create the problem.
@@ -4,9 +4,7 @@ module Mongoid
4
4
  # Raised when the original sort params and the cursor sort params are different
5
5
  class MismatchedSortFieldsError < Mongoid::Scroll::Errors::Base
6
6
  def initialize(opts = {})
7
- if opts[:diff] && opts[:diff].is_a?(Hash)
8
- opts = opts.merge(diff: opts[:diff].keys.join(', '))
9
- end
7
+ opts = opts.merge(diff: opts[:diff].keys.join(', ')) if opts[:diff] && opts[:diff].is_a?(Hash)
10
8
  super(compose_message('mismatched_sort_fields', opts))
11
9
  end
12
10
  end
@@ -3,9 +3,7 @@ module Mongoid
3
3
  module Errors
4
4
  class MultipleSortFieldsError < Mongoid::Scroll::Errors::Base
5
5
  def initialize(opts = {})
6
- if opts[:sort] && opts[:sort].is_a?(Hash)
7
- opts = opts.merge(sort: opts[:sort].keys.join(', '))
8
- end
6
+ opts = opts.merge(sort: opts[:sort].keys.join(', ')) if opts[:sort] && opts[:sort].is_a?(Hash)
9
7
  super(compose_message('multiple_sort_fields', opts))
10
8
  end
11
9
  end
@@ -0,0 +1,11 @@
1
+ module Mongoid
2
+ module Scroll
3
+ module Errors
4
+ class UnsupportedTypeError < Mongoid::Scroll::Errors::Base
5
+ def initialize(opts = {})
6
+ super(compose_message('unsupported_type', opts))
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -6,3 +6,4 @@ require 'mongoid/scroll/errors/invalid_cursor_error'
6
6
  require 'mongoid/scroll/errors/invalid_base64_cursor_error'
7
7
  require 'mongoid/scroll/errors/no_such_field_error'
8
8
  require 'mongoid/scroll/errors/unsupported_field_type_error'
9
+ require 'mongoid/scroll/errors/unsupported_type_error'
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module Scroll
3
- VERSION = '1.0.1'.freeze
3
+ VERSION = '2.0.0'.freeze
4
4
  end
5
5
  end
@@ -3,7 +3,6 @@ require 'i18n'
3
3
  I18n.load_path << File.join(File.dirname(__FILE__), 'config', 'locales', 'en.yml')
4
4
 
5
5
  require 'mongoid'
6
- require 'mongoid-compatibility'
7
6
  require 'mongoid/scroll/version'
8
7
  require 'mongoid/scroll/errors'
9
8
  require 'mongoid/scroll/base_cursor'
@@ -11,5 +10,5 @@ require 'mongoid/scroll/cursor'
11
10
  require 'mongoid/scroll/base64_encoded_cursor'
12
11
  require 'mongoid/criteria/scrollable/fields'
13
12
  require 'mongoid/criteria/scrollable/cursors'
14
- require 'mongo/scrollable' if Object.const_defined?(:Mongo)
13
+ require 'mongoid/criteria/scrollable/iterator'
15
14
  require 'mongoid/criteria/scrollable'
@@ -1,4 +1,4 @@
1
- $LOAD_PATH.push File.expand_path('../lib', __FILE__)
1
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
2
2
  require 'mongoid/scroll/version'
3
3
 
4
4
  Gem::Specification.new do |s|
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
13
13
  s.homepage = 'http://github.com/mongoid/mongoid-scroll'
14
14
  s.licenses = ['MIT']
15
15
  s.summary = 'Mongoid extensions to enable infinite scroll.'
16
- s.add_dependency 'mongoid', '>= 3.0'
17
- s.add_dependency 'mongoid-compatibility'
18
16
  s.add_dependency 'i18n'
17
+ s.add_dependency 'mongoid', '>= 6.0'
18
+ s.metadata['rubygems_mfa_required'] = 'true'
19
19
  end
@@ -3,17 +3,25 @@ require 'spec_helper'
3
3
  describe Mongoid::Scroll::Base64EncodedCursor do
4
4
  context 'new' do
5
5
  context 'an empty cursor' do
6
- let(:base64_string) { 'eyJ2YWx1ZSI6bnVsbCwiZmllbGRfdHlwZSI6IlN0cmluZyIsImZpZWxkX25hbWUiOiJhX3N0cmluZyIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOm51bGx9' }
7
6
  subject do
8
7
  Mongoid::Scroll::Base64EncodedCursor.new base64_string
9
8
  end
10
- its(:tiebreak_id) { should be_nil }
11
- its(:value) { should be_nil }
12
- its(:criteria) { should eq({}) }
13
- its(:to_s) { should eq(base64_string) }
9
+
10
+ let(:base64_string) { 'eyJ2YWx1ZSI6bnVsbCwiZmllbGRfdHlwZSI6IlN0cmluZyIsImZpZWxkX25hbWUiOiJhX3N0cmluZyIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOm51bGwsInR5cGUiOiJuZXh0In0=' }
11
+
12
+ its(:tiebreak_id) { is_expected.to be_nil }
13
+ its(:value) { is_expected.to be_nil }
14
+ its(:criteria) { is_expected.to eq({}) }
15
+ its(:type) { is_expected.to eq(:next) }
16
+ its(:to_s) { is_expected.to eq(base64_string) }
14
17
  end
18
+
15
19
  context 'a string field cursor' do
16
- let(:base64_string) { 'eyJ2YWx1ZSI6ImEgc3RyaW5nIiwiZmllbGRfdHlwZSI6IlN0cmluZyIsImZpZWxkX25hbWUiOiJhX3N0cmluZyIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOiI2NDA2M2RmODA5NDQzNDE3YzdkMmIxMDIifQ==' }
20
+ subject do
21
+ Mongoid::Scroll::Base64EncodedCursor.new base64_string
22
+ end
23
+
24
+ let(:base64_string) { 'eyJ2YWx1ZSI6ImEgc3RyaW5nIiwiZmllbGRfdHlwZSI6IlN0cmluZyIsImZpZWxkX25hbWUiOiJhX3N0cmluZyIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOiI2NDA2M2RmODA5NDQzNDE3YzdkMmIxMDIiLCJ0eXBlIjoibmV4dCJ9' }
17
25
  let(:a_value) { 'a string' }
18
26
  let(:tiebreak_id) { BSON::ObjectId.from_string('64063df809443417c7d2b102') }
19
27
  let(:criteria) do
@@ -24,18 +32,24 @@ describe Mongoid::Scroll::Base64EncodedCursor do
24
32
  ]
25
33
  }
26
34
  end
27
- subject do
28
- Mongoid::Scroll::Base64EncodedCursor.new base64_string
29
- end
30
- its(:value) { should eq a_value }
35
+
36
+ its(:value) { is_expected.to eq a_value }
31
37
  its(:tiebreak_id) { tiebreak_id }
32
- its(:value) { should eq a_value }
33
- its(:tiebreak_id) { should eq tiebreak_id }
34
- its(:criteria) { should eq(criteria) }
35
- its(:to_s) { should eq(base64_string) }
38
+ its(:value) { is_expected.to eq a_value }
39
+ its(:tiebreak_id) { is_expected.to eq tiebreak_id }
40
+ its(:criteria) { is_expected.to eq(criteria) }
41
+ its(:type) { is_expected.to eq(:next) }
42
+ its(:to_s) { is_expected.to eq(base64_string) }
36
43
  end
44
+
37
45
  context 'an id field cursor' do
38
- let(:base64_string) { 'eyJ2YWx1ZSI6IjY0MDY0NTg0MDk0NDM0MjgxZmE3MWFiMiIsImZpZWxkX3R5cGUiOiJCU09OOjpPYmplY3RJZCIsImZpZWxkX25hbWUiOiJpZCIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOiI2NDA2NDU4NDA5NDQzNDI4MWZhNzFhYjIifQ==' }
46
+ subject do
47
+ Mongoid::Scroll::Base64EncodedCursor.new base64_string
48
+ end
49
+
50
+ let(:base64_string) do
51
+ 'eyJ2YWx1ZSI6IjY0MDY0NTg0MDk0NDM0MjgxZmE3MWFiMiIsImZpZWxkX3R5cGUiOiJCU09OOjpPYmplY3RJZCIsImZpZWxkX25hbWUiOiJpZCIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOiI2NDA2NDU4NDA5NDQzNDI4MWZhNzFhYjIiLCJ0eXBlIjoibmV4dCJ9'
52
+ end
39
53
  let(:a_value) { BSON::ObjectId('64064584094434281fa71ab2') }
40
54
  let(:tiebreak_id) { a_value }
41
55
  let(:criteria) do
@@ -46,16 +60,20 @@ describe Mongoid::Scroll::Base64EncodedCursor do
46
60
  ]
47
61
  }
48
62
  end
63
+
64
+ its(:value) { is_expected.to eq a_value }
65
+ its(:tiebreak_id) { is_expected.to eq tiebreak_id }
66
+ its(:criteria) { is_expected.to eq(criteria) }
67
+ its(:type) { is_expected.to eq(:next) }
68
+ its(:to_s) { is_expected.to eq(base64_string) }
69
+ end
70
+
71
+ context 'an integer field cursor' do
49
72
  subject do
50
73
  Mongoid::Scroll::Base64EncodedCursor.new base64_string
51
74
  end
52
- its(:value) { should eq a_value }
53
- its(:tiebreak_id) { should eq tiebreak_id }
54
- its(:criteria) { should eq(criteria) }
55
- its(:to_s) { should eq(base64_string) }
56
- end
57
- context 'an integer field cursor' do
58
- let(:base64_string) { 'eyJ2YWx1ZSI6MTAsImZpZWxkX3R5cGUiOiJJbnRlZ2VyIiwiZmllbGRfbmFtZSI6ImFfaW50ZWdlciIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOiI2NDA2M2RmODA5NDQzNDE3YzdkMmIxMDgifQ==' }
75
+
76
+ let(:base64_string) { 'eyJ2YWx1ZSI6MTAsImZpZWxkX3R5cGUiOiJJbnRlZ2VyIiwiZmllbGRfbmFtZSI6ImFfaW50ZWdlciIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOiI2NDA2M2RmODA5NDQzNDE3YzdkMmIxMDgiLCJ0eXBlIjoibmV4dCJ9' }
59
77
  let(:a_value) { 10 }
60
78
  let(:tiebreak_id) { BSON::ObjectId('64063df809443417c7d2b108') }
61
79
  let(:criteria) do
@@ -66,18 +84,22 @@ describe Mongoid::Scroll::Base64EncodedCursor do
66
84
  ]
67
85
  }
68
86
  end
69
- subject do
70
- Mongoid::Scroll::Base64EncodedCursor.new base64_string
71
- end
72
- its(:value) { should eq a_value }
87
+
88
+ its(:value) { is_expected.to eq a_value }
73
89
  its(:tiebreak_id) { tiebreak_id }
74
- its(:value) { should eq a_value }
75
- its(:tiebreak_id) { should eq tiebreak_id }
76
- its(:criteria) { should eq(criteria) }
77
- its(:to_s) { should eq(base64_string) }
90
+ its(:value) { is_expected.to eq a_value }
91
+ its(:tiebreak_id) { is_expected.to eq tiebreak_id }
92
+ its(:criteria) { is_expected.to eq(criteria) }
93
+ its(:type) { is_expected.to eq(:next) }
94
+ its(:to_s) { is_expected.to eq(base64_string) }
78
95
  end
96
+
79
97
  context 'a date/time field cursor' do
80
- let(:base64_string) { 'eyJ2YWx1ZSI6MTM4NzU5MDEyMy4wLCJmaWVsZF90eXBlIjoiRGF0ZVRpbWUiLCJmaWVsZF9uYW1lIjoiYV9kYXRldGltZSIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOiI2NDA2NDNhNzA5NDQzNDIzOWYyZGJmODYifQ==' }
98
+ subject do
99
+ Mongoid::Scroll::Base64EncodedCursor.new base64_string
100
+ end
101
+
102
+ let(:base64_string) { 'eyJ2YWx1ZSI6MTM4NzU5MDEyMy4wLCJmaWVsZF90eXBlIjoiRGF0ZVRpbWUiLCJmaWVsZF9uYW1lIjoiYV9kYXRldGltZSIsImRpcmVjdGlvbiI6MSwiaW5jbHVkZV9jdXJyZW50IjpmYWxzZSwidGllYnJlYWtfaWQiOiI2NDA2NDNhNzA5NDQzNDIzOWYyZGJmODYiLCJ0eXBlIjoibmV4dCJ9' }
81
103
  let(:a_value) { DateTime.new(2013, 12, 21, 1, 42, 3, 'UTC') }
82
104
  let(:tiebreak_id) { BSON::ObjectId('640643a7094434239f2dbf86') }
83
105
  let(:criteria) do
@@ -88,16 +110,20 @@ describe Mongoid::Scroll::Base64EncodedCursor do
88
110
  ]
89
111
  }
90
112
  end
113
+
114
+ its(:value) { is_expected.to eq a_value }
115
+ its(:tiebreak_id) { is_expected.to eq tiebreak_id }
116
+ its(:criteria) { is_expected.to eq(criteria) }
117
+ its(:type) { is_expected.to eq(:next) }
118
+ its(:to_s) { is_expected.to eq(base64_string) }
119
+ end
120
+
121
+ context 'a date field cursor' do
91
122
  subject do
92
123
  Mongoid::Scroll::Base64EncodedCursor.new base64_string
93
124
  end
94
- its(:value) { should eq a_value }
95
- its(:tiebreak_id) { should eq tiebreak_id }
96
- its(:criteria) { should eq(criteria) }
97
- its(:to_s) { should eq(base64_string) }
98
- end
99
- context 'a date field cursor' do
100
- let(:base64_string) { 'eyJ2YWx1ZSI6MTM4NzU4NDAwMCwiZmllbGRfdHlwZSI6IkRhdGUiLCJmaWVsZF9uYW1lIjoiYV9kYXRlIiwiZGlyZWN0aW9uIjoxLCJpbmNsdWRlX2N1cnJlbnQiOmZhbHNlLCJ0aWVicmVha19pZCI6IjY0MDY0MmM5MDk0NDM0MjEyYzRkNDQyMCJ9' }
125
+
126
+ let(:base64_string) { 'eyJ2YWx1ZSI6MTM4NzU4NDAwMCwiZmllbGRfdHlwZSI6IkRhdGUiLCJmaWVsZF9uYW1lIjoiYV9kYXRlIiwiZGlyZWN0aW9uIjoxLCJpbmNsdWRlX2N1cnJlbnQiOmZhbHNlLCJ0aWVicmVha19pZCI6IjY0MDY0MmM5MDk0NDM0MjEyYzRkNDQyMCIsInR5cGUiOiJuZXh0In0=' }
101
127
  let(:tiebreak_id) { BSON::ObjectId('640642c9094434212c4d4420') }
102
128
  let(:a_value) { Date.new(2013, 12, 21) }
103
129
  let(:criteria) do
@@ -108,16 +134,20 @@ describe Mongoid::Scroll::Base64EncodedCursor do
108
134
  ]
109
135
  }
110
136
  end
137
+
138
+ its(:value) { is_expected.to eq a_value }
139
+ its(:tiebreak_id) { is_expected.to eq tiebreak_id }
140
+ its(:criteria) { is_expected.to eq(criteria) }
141
+ its(:type) { is_expected.to eq(:next) }
142
+ its(:to_s) { is_expected.to eq(base64_string) }
143
+ end
144
+
145
+ context 'a time field cursor' do
111
146
  subject do
112
147
  Mongoid::Scroll::Base64EncodedCursor.new base64_string
113
148
  end
114
- its(:value) { should eq a_value }
115
- its(:tiebreak_id) { should eq tiebreak_id }
116
- its(:criteria) { should eq(criteria) }
117
- its(:to_s) { should eq(base64_string) }
118
- end
119
- context 'a time field cursor' do
120
- let(:base64_string) { 'eyJ2YWx1ZSI6MTM4NzYwNTcyMy4wLCJmaWVsZF90eXBlIjoiVGltZSIsImZpZWxkX25hbWUiOiJhX3RpbWUiLCJkaXJlY3Rpb24iOjEsImluY2x1ZGVfY3VycmVudCI6ZmFsc2UsInRpZWJyZWFrX2lkIjoiNjQwNjNkNGEwOTQ0MzQxNjZiZDA1M2VkIn0=' }
149
+
150
+ let(:base64_string) { 'eyJ2YWx1ZSI6MTM4NzYwNTcyMy4wLCJmaWVsZF90eXBlIjoiVGltZSIsImZpZWxkX25hbWUiOiJhX3RpbWUiLCJkaXJlY3Rpb24iOjEsImluY2x1ZGVfY3VycmVudCI6ZmFsc2UsInRpZWJyZWFrX2lkIjoiNjQwNjNkNGEwOTQ0MzQxNjZiZDA1M2VkIiwidHlwZSI6Im5leHQifQ==' }
121
151
  let(:item_id) { BSON::ObjectId('640636f209443407333b46d4') }
122
152
  let(:a_value) { Time.new(2013, 12, 21, 6, 2, 3, '+00:00').utc }
123
153
  let(:tiebreak_id) { BSON::ObjectId('64063d4a094434166bd053ed') }
@@ -129,15 +159,15 @@ describe Mongoid::Scroll::Base64EncodedCursor do
129
159
  ]
130
160
  }
131
161
  end
132
- subject do
133
- Mongoid::Scroll::Base64EncodedCursor.new base64_string
134
- end
135
- its(:value) { should eq a_value }
162
+
163
+ its(:value) { is_expected.to eq a_value }
136
164
  its(:tiebreak_id) { tiebreak_id }
137
- its(:tiebreak_id) { should eq tiebreak_id }
138
- its(:criteria) { should eq(criteria) }
139
- its(:to_s) { should eq(base64_string) }
165
+ its(:tiebreak_id) { is_expected.to eq tiebreak_id }
166
+ its(:criteria) { is_expected.to eq(criteria) }
167
+ its(:type) { is_expected.to eq(:next) }
168
+ its(:to_s) { is_expected.to eq(base64_string) }
140
169
  end
170
+
141
171
  context 'an invalid field cursor' do
142
172
  it 'raises ArgumentError' do
143
173
  expect do
@@ -145,6 +175,7 @@ describe Mongoid::Scroll::Base64EncodedCursor do
145
175
  end.to raise_error Mongoid::Scroll::Errors::InvalidBase64CursorError
146
176
  end
147
177
  end
178
+
148
179
  context 'an invalid cursor' do
149
180
  it 'raises a Mongoid::Scroll::Errors::InvalidBase64CursorError with an invalid Base64 string' do
150
181
  expect { Mongoid::Scroll::Base64EncodedCursor.new 'invalid' }.to raise_error Mongoid::Scroll::Errors::InvalidBase64CursorError, /The cursor supplied is invalid: invalid./
@@ -155,79 +186,119 @@ describe Mongoid::Scroll::Base64EncodedCursor do
155
186
  end
156
187
  end
157
188
  end
189
+
158
190
  context 'from_record' do
159
191
  context 'a string field cursor' do
192
+ subject do
193
+ Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: field_name, field_type: field_type
194
+ end
195
+
160
196
  let(:field_type) { String }
161
197
  let(:field_value) { 'a string' }
162
198
  let(:field_name) { 'a_string' }
163
199
  let(:feed_item) { Feed::Item.create!(field_name => field_value) }
200
+
201
+ its(:value) { is_expected.to eq field_value }
202
+ its(:field_name) { is_expected.to eq field_name }
203
+ its(:field_type) { is_expected.to eq field_type.to_s }
204
+ end
205
+
206
+ context 'an id field cursor' do
164
207
  subject do
165
208
  Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: field_name, field_type: field_type
166
209
  end
167
- its(:value) { should eq field_value }
168
- its(:field_name) { should eq field_name }
169
- its(:field_type) { should eq field_type.to_s }
170
- end
171
- context 'an id field cursor' do
210
+
172
211
  let(:field_type) { BSON::ObjectId }
173
212
  let(:field_name) { 'id' }
174
213
  let(:feed_item) { Feed::Item.create! }
214
+
215
+ its(:value) { is_expected.to eq feed_item._id }
216
+ its(:field_type) { is_expected.to eq field_type.to_s }
217
+ end
218
+
219
+ context 'an integer field cursor' do
175
220
  subject do
176
221
  Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: field_name, field_type: field_type
177
222
  end
178
- its(:value) { should eq feed_item._id }
179
- its(:field_type) { should eq field_type.to_s }
180
- end
181
- context 'an integer field cursor' do
223
+
182
224
  let(:field_type) { Integer }
183
225
  let(:field_value) { 10 }
184
226
  let(:field_name) { 'a_integer' }
185
227
  let(:feed_item) { Feed::Item.create!(field_name => field_value) }
228
+
229
+ its(:value) { is_expected.to eq field_value }
230
+ its(:field_type) { is_expected.to eq field_type.to_s }
231
+ end
232
+
233
+ context 'a date/time field cursor' do
186
234
  subject do
187
235
  Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: field_name, field_type: field_type
188
236
  end
189
- its(:value) { should eq field_value }
190
- its(:field_type) { should eq field_type.to_s }
191
- end
192
- context 'a date/time field cursor' do
237
+
193
238
  let(:field_type) { DateTime }
194
239
  let(:field_value) { DateTime.new(2013, 12, 21, 1, 42, 3, 'UTC') }
195
240
  let(:field_name) { 'a_datetime' }
196
241
  let(:feed_item) { Feed::Item.create!(field_name => field_value) }
242
+
243
+ its(:value) { is_expected.to eq field_value }
244
+ its(:field_type) { is_expected.to eq field_type.to_s }
245
+ end
246
+
247
+ context 'a date field cursor' do
197
248
  subject do
198
249
  Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: field_name, field_type: field_type
199
250
  end
200
- its(:value) { should eq field_value }
201
- its(:field_type) { should eq field_type.to_s }
202
- end
203
- context 'a date field cursor' do
251
+
204
252
  let(:field_type) { Date }
205
253
  let(:field_value) { Date.new(2013, 12, 21) }
206
254
  let(:field_name) { 'a_date' }
207
255
  let(:feed_item) { Feed::Item.create!(field_name => field_value) }
256
+
257
+ its(:value) { is_expected.to eq field_value }
258
+ end
259
+
260
+ context 'a time field cursor' do
208
261
  subject do
209
262
  Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: field_name, field_type: field_type
210
263
  end
211
- its(:value) { should eq field_value }
212
- end
213
- context 'a time field cursor' do
264
+
214
265
  let(:field_type) { Time }
215
266
  let(:field_value) { Time.new(2013, 12, 21, 1, 2, 3) }
216
267
  let(:field_name) { 'a_time' }
217
268
  let(:feed_item) { Feed::Item.create!(field_name => field_value) }
218
- subject do
219
- Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: field_name, field_type: field_type
220
- end
221
- its(:value) { should eq field_value }
222
- its(:field_type) { should eq field_type.to_s }
269
+
270
+ its(:value) { is_expected.to eq field_value }
271
+ its(:field_type) { is_expected.to eq field_type.to_s }
223
272
  end
273
+
224
274
  context 'an array field cursor' do
225
275
  let(:feed_item) { Feed::Item.create!(a_array: %w[x y]) }
276
+
226
277
  it 'is not supported' do
227
278
  expect do
228
279
  Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: 'a_array', field_type: Array
229
280
  end.to raise_error Mongoid::Scroll::Errors::UnsupportedFieldTypeError, /The type of the field 'a_array' is not supported: Array./
230
281
  end
231
282
  end
283
+
284
+ it 'encode and decode type option' do
285
+ feed_item = Feed::Item.create!
286
+ cursor = Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: 'id', field_type: BSON::ObjectId, type: :previous
287
+ expect(Mongoid::Scroll::Base64EncodedCursor.new(cursor.to_s).type).to eq(:previous)
288
+ end
289
+
290
+ context 'a cursor with previous set to true' do
291
+ subject do
292
+ Mongoid::Scroll::Base64EncodedCursor.from_record feed_item, field_name: field_name, field_type: field_type, type: :previous
293
+ end
294
+
295
+ let(:field_type) { BSON::ObjectId }
296
+ let(:field_name) { 'id' }
297
+ let(:feed_item) { Feed::Item.create! }
298
+
299
+ its(:value) { is_expected.to eq feed_item._id }
300
+ its(:field_type) { is_expected.to eq field_type.to_s }
301
+ its(:type) { is_expected.to eq(:previous) }
302
+ end
232
303
  end
233
304
  end