philiprehberger-pagination

Tests Gem Version Last updated

Framework-agnostic pagination with cursor, offset, and keyset strategies

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-pagination"

Or install directly:

gem install philiprehberger-pagination

Usage

require "philiprehberger/pagination"

page = Philiprehberger::Pagination.paginate(users,
  strategy: :offset,
  per_page: 25,
  page: 2
)

page.items       # => [user_26, user_27, ...]
page.total       # => 100
page.has_next?   # => true
page.has_prev?   # => true

Offset Pagination

page = Philiprehberger::Pagination.paginate(items, strategy: :offset, per_page: 10, page: 3)
page.next_cursor  # => "4" (next page number)
page.prev_cursor  # => "2" (previous page number)
page.next_page    # => 4
page.prev_page    # => 2
page.page_range   # => 1..5

Cursor Pagination

page = Philiprehberger::Pagination.paginate(items, strategy: :cursor, per_page: 10)
next_page = Philiprehberger::Pagination.paginate(items,
  strategy: :cursor,
  per_page: 10,
  cursor: page.next_cursor
)

Keyset Pagination

# Items must be sorted; cursor is based on the last item's value
page = Philiprehberger::Pagination.paginate(sorted_items, strategy: :keyset, per_page: 10)

Page Metadata

page = Philiprehberger::Pagination.paginate(items, strategy: :offset, per_page: 10, page: 2)
page.
# => { current_page: 2, per_page: 10, total_pages: 5, total_count: 50, offset: 10 }
page.total_pages   # => 5
page.first_page?   # => false
page.last_page?    # => false

Hash & JSON Serialization

page = Philiprehberger::Pagination.paginate(items, strategy: :offset, per_page: 10, page: 2)
page.to_h
# => {
#   items: [...],
#   metadata: { current_page: 2, per_page: 10, total_pages: 5, total_count: 50, offset: 10 },
#   links: { next: "3", prev: "1" }
# }

Iteration

page = Philiprehberger::Pagination.paginate(items, strategy: :offset, per_page: 10)
page.size              # => 10
page.empty?            # => false
page.each { |item| puts item }
page.map { |item| item.name }  # Enumerable methods work directly on the page

Page Size Limits

page = Philiprehberger::Pagination.paginate(items,
  per_page: 25,
  max_per_page: 100,
  min_per_page: 1
)
# Raises InvalidPageSizeError if per_page is out of bounds

Cursor Encryption

page = Philiprehberger::Pagination.paginate(items,
  strategy: :cursor,
  per_page: 10,
  secret: "my-secret-key"
)
# Cursors are signed with HMAC-SHA256
# Tampered cursors raise InvalidCursorError

API

Pagination

Method Description
.paginate(collection, strategy:, per_page:, cursor:, page:, max_per_page:, min_per_page:, secret:) Paginate a collection

Page

Method Description
#items Items on the current page
#total Total number of items in the collection
#next_cursor Cursor for the next page
#prev_cursor Cursor for the previous page
#has_next? Whether there is a next page
#has_prev? Whether there is a previous page
#first_page? Whether this is the first page
#last_page? Whether this is the last page
#next_page Next page number (offset only)
#prev_page Previous page number (offset only)
#page_range Range of all page numbers, e.g. 1..5
#total_pages Ceiling division of total by per_page
#links Hash of navigation cursors
#metadata Hash with current_page, per_page, total_pages, total_count, offset
#to_h Hash with items, metadata, and links (JSON-ready)
#size / #length / #count Number of items on this page
#empty? Whether the page has no items
#each Iterate over items (includes Enumerable)
#per_page Items per page
#current_page Current page number (offset only)
#offset Current offset (offset only)

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT