lib/json_api_client/paginating/nested_param_paginator.rb
module JsonApiClient
module Paginating
# An alternate, more consistent Paginator that always wraps
# pagination query string params in a top-level wrapper_name,
# e.g. page[offset]=2, page[limit]=10.
class NestedParamPaginator
DEFAULT_WRAPPER_NAME = "page".freeze
DEFAULT_PAGE_PARAM = "page".freeze
DEFAULT_PER_PAGE_PARAM = "per_page".freeze
# Define class accessors as methods to enforce standard way
# of defining pagination related query string params.
class << self
def wrapper_name
@_wrapper_name ||= DEFAULT_WRAPPER_NAME
end
def wrapper_name=(param = DEFAULT_WRAPPER_NAME)
raise ArgumentError, "don't wrap wrapper_name" unless valid_param?(param)
@_wrapper_name = param.to_s
end
def page_param
@_page_param ||= DEFAULT_PAGE_PARAM
"#{wrapper_name}[#{@_page_param}]"
end
def page_param=(param = DEFAULT_PAGE_PARAM)
raise ArgumentError, "don't wrap page_param" unless valid_param?(param)
@_page_param = param.to_s
end
def per_page_param
@_per_page_param ||= DEFAULT_PER_PAGE_PARAM
"#{wrapper_name}[#{@_per_page_param}]"
end
def per_page_param=(param = DEFAULT_PER_PAGE_PARAM)
raise ArgumentError, "don't wrap per_page_param" unless valid_param?(param)
@_per_page_param = param
end
private
def valid_param?(param)
!(param.nil? || param.to_s.include?("[") || param.to_s.include?("]"))
end
end
attr_reader :params, :result_set, :links
def initialize(result_set, data)
@params = params_for_uri(result_set.uri)
@result_set = result_set
@links = data["links"]
end
def next
result_set.links.fetch_link("next")
end
def prev
result_set.links.fetch_link("prev")
end
def first
result_set.links.fetch_link("first")
end
def last
result_set.links.fetch_link("last")
end
def total_pages
if links["last"]
uri = result_set.links.link_url_for("last")
last_params = params_for_uri(uri)
last_params.fetch(page_param, &method(:current_page)).to_i
else
current_page
end
end
# this is an estimate, not necessarily an exact count
def total_entries
per_page * total_pages
end
def total_count; total_entries; end
def offset
per_page * (current_page - 1)
end
def per_page
params.fetch(per_page_param) do
result_set.length
end.to_i
end
def current_page
params.fetch(page_param, 1).to_i
end
def out_of_bounds?
current_page > total_pages
end
def previous_page
current_page > 1 ? (current_page - 1) : nil
end
def next_page
current_page < total_pages ? (current_page + 1) : nil
end
def page_param
self.class.page_param
end
def per_page_param
self.class.per_page_param
end
alias limit_value per_page
protected
def params_for_uri(uri)
return {} unless uri
uri = Addressable::URI.parse(uri)
( uri.query_values || {} ).with_indifferent_access
end
end
end
end