class CursorPager::Page

The main class that coordinates the whole pagination.

Attributes

after[R]
before[R]
first_value[R]
last_value[R]
order_values[R]
relation[R]

Public Class Methods

new(relation, first: nil, last: nil, after: nil, before: nil) click to toggle source
# File lib/cursor_pager/page.rb, line 14
def initialize(relation, first: nil, last: nil, after: nil, before: nil)
  @configuration = CursorPager.configuration
  @relation = relation
  @first_value = first
  @last_value = last
  @after = after
  @before = before
  @order_values = OrderValues.from_relation(relation)

  add_default_order
  verify_order_directions!
end

Public Instance Methods

cursor_for(item) click to toggle source
# File lib/cursor_pager/page.rb, line 69
def cursor_for(item)
  return if item.nil?

  encoder.encode(item.id.to_s)
end
first() click to toggle source

A capped `first` value. The underlying instance variable `first_value` doesn't have limits on it. If neither `first` nor `last` is given, but `default_page_size` or `maximum_page_size` are configured, they will be used for first.

# File lib/cursor_pager/page.rb, line 31
def first
  @first ||= begin
               capped = limit_pagination_argument(first_value)

               if capped.nil? && last.nil?
                 capped = default_page_size || maximum_page_size
               end

               capped
             end
end
first_cursor() click to toggle source
# File lib/cursor_pager/page.rb, line 75
def first_cursor
  cursor_for(records.first)
end
last() click to toggle source

A capped `last` value. The underlying instance variable `last_value` doesn't have limits on it.

# File lib/cursor_pager/page.rb, line 45
def last
  @last ||= limit_pagination_argument(last_value)
end
last_cursor() click to toggle source
# File lib/cursor_pager/page.rb, line 79
def last_cursor
  cursor_for(records.last)
end
next_page?() click to toggle source
# File lib/cursor_pager/page.rb, line 59
def next_page?
  @next_page ||= if before_limit_value.present?
                   true
                 elsif first
                   sliced_relation.offset(first).exists?
                 else
                   false
                 end
end
previous_page?() click to toggle source
# File lib/cursor_pager/page.rb, line 49
def previous_page?
  @previous_page ||= if after_limit_value.present?
                       true
                     elsif last
                       limited_relation.offset_value.to_i.positive?
                     else
                       false
                     end
end
records() click to toggle source
# File lib/cursor_pager/page.rb, line 83
def records
  @records ||= limited_relation.to_a
end

Private Instance Methods

add_default_order() click to toggle source
# File lib/cursor_pager/page.rb, line 89
def add_default_order
  return if sufficiently_ordered?

  direction = order_values.direction || :asc

  @order_values << OrderValue.new(relation, relation.primary_key, direction)
end
after_limit_value() click to toggle source
# File lib/cursor_pager/page.rb, line 145
def after_limit_value
  @after_limit_value ||= after.present? && limit_value_for(after)
end
before_limit_value() click to toggle source
# File lib/cursor_pager/page.rb, line 141
def before_limit_value
  @before_limit_value ||= before.present? && limit_value_for(before)
end
limit_item_for(cursor) click to toggle source
# File lib/cursor_pager/page.rb, line 157
def limit_item_for(cursor)
  id = encoder.decode(cursor)

  selects = order_values.map(&:select_string)
  ordered_relation_copy = ordered_relation.dup

  ordered_relation_copy.preload_values = []
  ordered_relation_copy.eager_load_values = []
  ordered_relation_copy
    .unscope(:includes).where(id: id).select(selects).first
end
limit_pagination_argument(argument) click to toggle source

Used to cap `first` and `last` arguments. Returns `nil` if the argument is `nil`, otherwise a value between `0` and `maximum_page_size`.

# File lib/cursor_pager/page.rb, line 112
def limit_pagination_argument(argument)
  return if argument.nil?

  if argument.negative?
    argument = 0
  elsif maximum_page_size && argument > maximum_page_size
    argument = maximum_page_size
  end

  argument
end
limit_value_for(cursor) click to toggle source
# File lib/cursor_pager/page.rb, line 149
def limit_value_for(cursor)
  item = limit_item_for(cursor)

  raise CursorNotFoundError, cursor if item.blank?

  order_values.map { |value| item[value.select_alias] }
end
limited_relation() click to toggle source
# File lib/cursor_pager/page.rb, line 124
def limited_relation
  @limited_relation ||= LimitRelation.new(sliced_relation, first, last).call
end
ordered_relation() click to toggle source
# File lib/cursor_pager/page.rb, line 137
def ordered_relation
  @ordered_relation ||= relation.reorder(order_values.order_string)
end
sliced_relation() click to toggle source
# File lib/cursor_pager/page.rb, line 128
def sliced_relation
  @sliced_relation ||= SliceRelation.new(
    ordered_relation,
    order_values,
    after_limit_value,
    before_limit_value
  ).call
end
sufficiently_ordered?() click to toggle source
# File lib/cursor_pager/page.rb, line 97
def sufficiently_ordered?
  order_values.present? && order_values.all? do |value|
    value.primary_key? || value.type == :datetime
  end
end
verify_order_directions!() click to toggle source
# File lib/cursor_pager/page.rb, line 103
def verify_order_directions!
  return if order_values.map(&:direction).uniq.size == 1

  raise ConflictingOrdersError
end