mongoid-scroll 1.0.1 → 2.0.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.
@@ -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