pagy

package module
v0.0.0-...-997d15e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 3 Imported by: 0

README

go-ruby-pagy/pagy

pagy — go-ruby-pagy

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of the deterministic pagination core of Ruby's pagy gem. It reproduces, with the same arithmetic the MRI gem performs (v8.6.3 semantics), the values a Pagy instance computes at construction — offset, limit, page count (pages / last), the from/to record window, the in-page count (in), the prev/next links — and the navigation series (the [1, :gap, 4, 5, "6", 7, 8, :gap, 36] array, with the current page as a String and elided ranges as :gap) — without any Ruby runtime.

It is the pagination library for go-embedded-ruby, but a standalone, reusable module — a sibling of the other go-ruby-* gems.

What it is — and isn't. Everything pagy computes about a page set is pure arithmetic and needs no interpreter, so it lives here as pure Go: clamping the page into range, deriving the offset and record window from count/items/outset, capping the last page (max_pages), and building the series with its gaps. The only application-coupled piece of the gem — rendering a page URL out of the Rack request — is here a pure template seam: Pagy.URL builds a query-string URL from the vars on the instance, or delegates to an optional URLFormatter you supply. No routing, no request object.

Features

Faithful port of the pagy gem's calculator:

  • New(Vars) — applies the gem's defaults (items 20, size 7, page_param :page, ends true, outset 0) and arithmetic, returning a *Pagy or an *OverflowError when the requested page is past the last one.
  • Page setOffset, Items (Limit()), Last (Pages()), From, To, In, Prev, Next (0 means "no such page"), with faithful edge handling: page clamped up from <= 0, count 0 → one empty page, single page, outset, cycle (wrap next to 1), max_pages cap.
  • Series() / SeriesSize(size) — the navigation array as a []any of int page links, the current page as a string, and Gap for elided ranges (the gem's :gap). Reproduces the gem's start-window selection, the first/last-plus-gap forcing for size >= 7, and the ends: false and small (size < 7) branches. size 0 yields []; a negative size is a *VariableError.
  • URL helpersURL(page, formatter), PrevURL, NextURL: a pure request_path?page_param=…&params…#fragment builder, or your own URLFormatter seam.

Usage

p, err := pagy.New(pagy.Vars{Count: 1000, Page: 6, Items: 20})
if err != nil {
	// *OverflowError when Page is past the last page
}
p.Offset        // 100
p.Limit()       // 20   (alias of p.Items)
p.Pages()       // 50   (alias of p.Last)
p.From, p.To    // 101, 120
p.Prev, p.Next  // 5, 7

p.Series()      // [1 :gap 5 "6" 7 :gap 50]      (default size 7)
p.SeriesSize(9) // [1 :gap 4 5 "6" 7 8 :gap 50]  (window of 5 + first/last)

p.URL(3, nil)   // "?page=3"   (or supply a URLFormatter)

Ruby surface

A host (go-embedded-ruby / rbgo) maps its Ruby Pagy object onto these shapes so require "pagy" works CGO-free:

pagy = Pagy.new(count: 1000, page: 6, items: 20)
pagy.offset   # 100
pagy.limit    # 20
pagy.pages    # 50
pagy.series   # [1, :gap, 5, "6", 7, :gap, 50]
pagy.prev     # 5
pagy.next     # 7

Pagy::OverflowError maps to *OverflowError.

Tests & coverage

Pure computation, deterministic, no network and no filesystem: every branch — the overflow error, count 0, single page, outset, the gap / no-gap series branches, ends: false, max_pages, cycle, and the URL seam — is exercised, holding statement coverage at 100%. The suite runs under -race, on the six supported 64-bit targets (amd64/arm64/riscv64/loong64/ppc64le/s390x, the emulated lanes under qemu-user), and builds for js/wasm and wasip1/wasm.

go test -race ./...

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-pagy/pagy authors.

Documentation

Overview

Package pagy is a pure-Go (CGO-free) reimplementation of the deterministic pagination core of Ruby's pagy gem. It reproduces, byte-for-byte with the MRI gem (v8.6.3 semantics), the arithmetic a Pagy instance performs at construction — offset, limit, page count, the from/to record window, the in-page count, the prev/next links — and the navigation series algorithm (the [1, :gap, 4, 5, "6", 7, 8, :gap, 36] array, with the current page as a String and elided ranges as :gap).

It is the pagination library for [go-embedded-ruby](https://github.com/go-embedded-ruby/ruby), but a standalone, reusable module — a sibling of the other go-ruby-* gems.

What it is — and isn't

Everything pagy computes about a page set is pure arithmetic and needs no interpreter, so it lives here as pure Go: clamping the page into range, deriving the offset and record window from count/items/outset, capping the last page, and building the series with its gaps. The overflow condition (a page past the last one) surfaces as an *OverflowError, mirroring the gem's Pagy::OverflowError.

Rendering a URL for a page is the only piece that touches application state in the gem (it reaches into the Rack request). Here it is a pure template seam: Pagy.URL builds a query-string URL from the vars stored on the instance (request path, params, page param, fragment), or delegates to an optional URLFormatter you supply — no routing, no request object.

Flow

p, err := pagy.New(pagy.Vars{Count: 1000, Page: 6, Items: 20})
if err != nil {
	// *OverflowError when Page is past the last page
}
p.Offset      // 100
p.Limit()     // 20   (alias of p.Items)
p.Pages()     // 50   (alias of p.Last)
p.From, p.To  // 101, 120
p.Prev, p.Next // 5, 7 (0 means "no such page")
p.Series()    // [1 :gap 4 5 "6" 7 8 :gap 50]  (default size 7... etc.)

Value model

A Pagy carries the decoded page-set fields. A series is a []any whose elements are int (a page link), string (the current page), or Gap (an elided range, the gem's :gap). Construction errors are *OverflowError; an out-of-range series size is a *VariableError. A host (go-embedded-ruby / rbgo) maps its Ruby Pagy object and Pagy::OverflowError onto these shapes: Pagy.new(count:, page:, items:) and .offset/.limit/.pages/.series/.prev/.next.

Index

Constants

View Source
const (
	// DefaultItems is the default number of items per page (pagy: items 20).
	DefaultItems = 20
	// DefaultSize is the default series size (pagy: size 7).
	DefaultSize = 7
	// DefaultPageParam is the default query-string parameter for the page
	// number (pagy: page_param :page).
	DefaultPageParam = "page"
)

Default variable values, mirroring Ruby pagy's DEFAULT hash.

Variables

View Source
var Gap = SeriesGap{}

Gap is the sentinel placed in a Pagy.Series where a contiguous range of page numbers has been elided (Ruby :gap).

Functions

This section is empty.

Types

type OverflowError

type OverflowError struct {
	// Page is the requested (overflowing) page.
	Page int
	// Last is the last valid page.
	Last int
}

OverflowError reports that the requested page is past the last page, mirroring Ruby pagy's Pagy::OverflowError.

func (*OverflowError) Error

func (e *OverflowError) Error() string

Error renders the message exactly as the gem does, e.g. "expected :page in 1..36; got 40".

type Pagy

type Pagy struct {
	// Count is the total item count (clamped to >= 0).
	Count int
	// Page is the resolved current page, in 1..Last.
	Page int
	// Items is the resolved number of items per page (>= 1).
	Items int
	// Outset is the resolved outset (>= 0).
	Outset int
	// Last is the number of the last page (>= 1). Also available via Pages.
	Last int
	// Offset is the zero-based offset of the first item on the current page.
	Offset int
	// From is the 1-based index of the first item shown (0 when the page is
	// empty).
	From int
	// To is the 1-based index of the last item shown (0 when empty).
	To int
	// In is the number of items shown on the current page.
	In int
	// Prev is the previous page number, or 0 when there is none.
	Prev int
	// Next is the next page number, or 0 when there is none.
	Next int
	// Vars are the normalized input variables (defaults applied).
	Vars Vars
}

Pagy is a computed page set: the pure result of pagy's initializer.

func New

func New(v Vars) (*Pagy, error)

New computes a Pagy from v, applying pagy's defaults and arithmetic. It returns an *OverflowError when the requested page is past the last page.

func (*Pagy) Limit

func (p *Pagy) Limit() int

Limit reports the number of items per page (pagy alias of Items / limit).

func (*Pagy) NextURL

func (p *Pagy) NextURL(f URLFormatter) string

NextURL builds the URL for the next page, or returns "" when there is no next page. See Pagy.URL for the meaning of f.

func (*Pagy) Pages

func (p *Pagy) Pages() int

Pages reports the number of pages (pagy alias of Last).

func (*Pagy) PrevURL

func (p *Pagy) PrevURL(f URLFormatter) string

PrevURL builds the URL for the previous page, or returns "" when there is no previous page. See Pagy.URL for the meaning of f.

func (*Pagy) Series

func (p *Pagy) Series() []any

Series returns the navigation series using the default size stored in Vars. The result never carries an error because the stored size is always valid.

func (*Pagy) SeriesSize

func (p *Pagy) SeriesSize(size int) ([]any, error)

SeriesSize returns the navigation series for the given size: a []any of int page links, the current page as a string, and Gap for elided ranges. It returns a *VariableError when size is negative, and an empty slice when size is 0, mirroring pagy's series method.

func (*Pagy) URL

func (p *Pagy) URL(page int, f URLFormatter) string

URL builds the URL for the given page. When f is non-nil it delegates to f; otherwise it builds a query-string URL from the instance vars — RequestPath, Params (merged), the PageParam set to page, and Fragment appended — mirroring pagy_url_for as a pure template with no request object.

type SeriesGap

type SeriesGap struct{}

SeriesGap is the type of the Gap sentinel that marks an elided range of pages inside a series, mirroring Ruby pagy's :gap symbol.

func (SeriesGap) String

func (SeriesGap) String() string

String renders the gap as "gap".

type URLFormatter

type URLFormatter func(page int) string

URLFormatter formats an absolute-or-relative URL for a page number. It is the only seam in the package: supply one to Pagy.URL to override the built-in query-string builder with your own routing.

type VariableError

type VariableError struct {
	// Variable is the offending variable name (e.g. "size").
	Variable string
	// Description states the expectation (e.g. "to be an Integer >= 0").
	Description string
	// Value is the value that was supplied.
	Value int
}

VariableError reports an out-of-range variable, mirroring Ruby pagy's Pagy::VariableError.

func (*VariableError) Error

func (e *VariableError) Error() string

Error renders the message exactly as the gem does, e.g. "expected :size to be an Integer >= 0; got -1".

type Vars

type Vars struct {
	// Count is the total number of items to paginate. Negative values are
	// treated as 0.
	Count int
	// Page is the requested 1-based page. Values <= 0 clamp to 1. A value
	// past the last page makes [New] return an [*OverflowError].
	Page int
	// Items is the number of items per page. Values <= 0 use [DefaultItems].
	Items int
	// Outset is the number of items skipped before pagination begins
	// (pagy: outset). Negative values are treated as 0.
	Outset int
	// Size is the default series size used by [Pagy.Series]. Values <= 0 use
	// [DefaultSize].
	Size int
	// Ends, when nil, defaults to true: the series forces the first and last
	// page (with gaps) for sizes >= 7. Set to a pointer to false to disable.
	Ends *bool
	// Cycle, when true, makes Next wrap from the last page back to 1
	// (pagy: cycle).
	Cycle bool
	// MaxPages caps the last page when > 0 (pagy: max_pages).
	MaxPages int
	// PageParam is the query-string key carrying the page number in URLs.
	// Empty uses [DefaultPageParam].
	PageParam string
	// RequestPath is the URL path used by [Pagy.URL] (pagy: request_path).
	RequestPath string
	// Params are extra query parameters merged into URLs by [Pagy.URL].
	Params url.Values
	// Fragment is appended to URLs built by [Pagy.URL] (e.g. "#items").
	Fragment string
}

Vars are the input variables for New, mirroring the pagy gem's option hash. The zero value of each field means "use the pagy default": Page 1, Items 20, Size 7, PageParam "page", Ends true, no page cap.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL