Skip to content

Commit

Permalink
WIP: Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ddnexus committed Dec 26, 2024
1 parent 17a00a7 commit 56750e3
Show file tree
Hide file tree
Showing 7 changed files with 416 additions and 94 deletions.
25 changes: 20 additions & 5 deletions docs/api/javascript/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,39 @@ Your app needs standard script or old browser compatibility

* It's an [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) file meant to be loaded as is, directly in your
production pages and without any further processing
* Minified (~2k) and polyfilled to work also with quite old browsers
* Minified (~3k)

[!file](/gem/javascripts/pagy.min.js)

```ruby
script_path = Pagy.root.join('javascripts', 'pagy.min.js')
```

+++ `pagy.min.js.map`
+++ `pagy.js`
!!! success
Developer version: plain Javascript to use for debugging with its `pagy.js.map`
!!!

* It's an [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) file meant to be loaded as is, directly in your
production pages and without any further processing
* Minified (~2k) and polyfilled to work also with quite old browsers

[!file](/gem/javascripts/pagy.js)

```ruby
script_path = Pagy.root.join('javascripts', 'pagy.js')
```

+++ `pagy.js.map`

!!! success
You need to debug the javascript helpers while using the `pagy.min.js` file
You need to debug the javascript helpers while using the `pagy.js` file
!!!

[!file](/gem/javascripts/pagy.min.js.map)
[!file](/gem/javascripts/pagy.js.map)

```ruby
script_path = Pagy.root.join('javascripts', 'pagy.min.js.map')
script_path = Pagy.root.join('javascripts', 'pagy.js.map')
```

+++
Expand Down
259 changes: 177 additions & 82 deletions docs/api/keyset.md

Large diffs are not rendered by default.

147 changes: 147 additions & 0 deletions docs/api/keyset_for_ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
---
title: Pagy::KeysetForUI
category:
- Feature
- Class
---

# Pagy::KeysetForUI

A [Pagy::Keyset](keyset.md) subclass with numeric pages supporting `pagy_*nav` and the other Frontend helpers.

!!!warning Experimental: the API might change in minor versions
!!!

## Overview

The regular `Pagy::Keyset` uses the fastest technique for pagination, but it cannot work with any Frontend helper because they require numeric
pages.

That's why we created `Pagy::KeysetForUI`: it uses keyset pagination AND supports `pagy_*navs` and the other Frontend
helpers.

!!!
The API is documented here, however you should use the [keyset_for_ui extra](/docs/extras/keyset_for_ui.md)
wrapper to handle the cache and easily integrate it with your app.

You should also familiarize with the [Pagy::Keyset](keyset.md) class.
!!!

[!button corners="pill" variant="success" text=":icon-play: Try it now!"](/playground.md#5-keyset-apps)

## Glossary

This section integrates the [Keyset Glossary](keyset_for_ui.md#glossary)

| Term | Description |
|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `keyset pagination for UI` | The pagy exclusive technique to use `keyset pagination` with numeric pages, supporting `pagy_*navs` and the other Frontend helpers.<br/>The best technique for performance AND functionality! |
| `page` | The current page **number** |
| `cutoffs` | The `cutoff`s of the known pagination state, used to keep track of the visited pages during the navigation. |

## How Pagy Keyset For UI works

### Caching the known cutoffs

See also [Understanding the Cutoffs](/docs/api/keyset.md#understanding-the-cutoffs) for a complete understanding...

`Pagy::KeysetForUI` keeps track of the state of the pagination using the `cutoffs` array. As soon as a page is visited, its `cutoff` gets pushed to the `cutoffs` array (which is cached) hence it can be retrieved by numeric index (i.e. by `:page`) in future requests.

While the `cutoffs` data must persist between requests, this class does not handle the persistency at all: that is a concern of the [keyset_for_ui extra](/docs/extras/keyset_for_ui). Here is a simplified example of what must happen with the `cutoffs` at each request:

```ruby
cutoffs = read_from_cache(cache_key)
pagy = KeysetForUI.new(set, cutoffs:, **vars)
write_to_cache(cache_key, pagy.cutoffs)
```

!!! Notice

The cache handling is external to the `Pagy::KeysetForUI` class for easy overriding. See [Understanding the cache](/docs/extras/keyset_for_ui#understanding-the-cache) for more details.
!!!

### Numeric variables for the Frontend Helpers

Keeping track of the state through the `cutoffs` allows to set the numeric variables that the Frontend helpers require (see [Attribute Readers](#attribute-readers)).

However, it's still keyset pagination, which doesn't know any future page after the `next` page, so we add more pages on the go like we do with `Pagy::Countless`... just better for two reasons:

1. We don't lose the future pages when we jump back because we can count on the cache.
2. Keyset pagination is A LOT faster than offset pagination.

### Accurate queries

`Pagy::KeysetForUI` knows all the `cutoffs` of the current pagination. If the `cutoffs` don't contain the current page's `cutoff`, then it's a new requested page, and it proceeds exactly like the standard `Pagy::Keyset` class.

However, if the `cutoffs` contains the current page's `cutoff` then it's an already requested page, and the number of records pulled with LIMIT may have changed since the time of the first request.

Querying with the LIMIT again, might cause records to get skipped or to appear twice! So, to get it right, it pulls the records AFTER the `cutoff` of the previous page (`@prev_cutoff`) AND NOT AFTER the `cutoff` of the current page, so avoiding the LIMIT and inluding the right records regardless.

!!! Notice

While te accuracy is guaranteed, in case of insertions or deletions of records falling in the range of the visited page, the page will obviously have a number of records different from expected.

That is not a logical nor common problem, however in extreme cases, a page of records might change its size so noticeably and unexpectedly that it may look somehow "broken" to the users.

!!!success We plan to implement page-rebalancing:

- Automatic compacting of empty (or almost empty) visited pages.
- Automatic splitting of eccesively grown visited pages.
!!!

## Setup

See the [Keyset Setup](keyset.md#setup) and the [keyset_for_ui extra](/docs/extras/keyset_for_ui).

## ORMs

`Pagy::KeysetForUI` implements the subclasses for `ActiveRecord::Relation` and `Sequel::Dataset` sets and instantiate them
internally:

```ruby
Pagy::KeysetForUI.new(active_record_set)
#=> #<Pagy::KeysetForUI::ActiveRecord:0x00000001066215e0>

Pagy::KeysetForUI.new(sequel_set)
#=> #<Pagy::KeysetForUI::Sequel:0x00000001066545e0>
```

## Methods

==- `Pagy::KeysetForUI.new(set, **vars)`

The constructor takes the `set`, and an optional hash of [variables](#variables). It returns a
`Pagy::KeysetForUI::ActiveRecord` or
`Pagy::KeysetForUI::Sequel` object (depending on the `set` class).

==- `records`

The `Array` of fetched records for the current page.

===

## Variables

This section ontegrates the [Pagy::Keyset variables](keyset.md#variables):

==- `:cutoffs`

Mandatory array that must persist between requests.

==- `:max_pages`

Paginate only `:max_pages` ignoring the rest.

==- `:reset_overflow`

Resets the pagination in case of overflow, instead of raising a `Pagy::OverflowError`. Use it when you don't need to `rescue` and handle the event in any particular way. Notice: it reuses the current `cache_key`

===

## Attribute Readers

`cutoffs`, `in`, `last`, `limit`, `next`, `page`, `prev`, `vars`

## Troubleshooting

See the [Pagy::Keyset Troubleshooting](keyset.md#troubleshooting)
4 changes: 2 additions & 2 deletions docs/extras/keyset.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Paginate with the Pagy keyset pagination technique.

## Overview

This is a tiny wrapper around the [Pagy::Keyset API](/docs/api/keyset.md). Please refer to the class documentation for a
This is a wrapper around the [Pagy::Keyset API](/docs/api/keyset.md). Please refer to the class documentation for a
fuller understanding of keyset pagination:

[!ref Keyset Pagination: Concepts and Overview](/docs/api/keyset.md)
Expand All @@ -33,7 +33,7 @@ require 'pagy/extras/keyset'
```

```ruby Controller (action)
# The set argument must be an uniquely ORDERED Activerecord Scope or Sequel Dataset
# The set argument must be a uniquely ORDERED Activerecord Scope or Sequel Dataset

# Minimal unique ordering with the primary key: it works fast out of the box
set = Product.order(:id)
Expand Down
59 changes: 59 additions & 0 deletions docs/extras/keyset_for_ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: Keyset For UI
categories:
- Backend
- Extra
---

# Keyset For UI Extra

Paginate with the [Pagy Keyset For UI](/docs/api/keyset_for_ui) pagination technique, using numeric pages to support `pagy_*nav`
and the other Frontend helpers.

!!!warning Experimental: the API might change in minor versions
!!!

[!button corners="pill" variant="success" text=":icon-play: Try it now!"](/playground.md#5-keyset-apps)

## Overview

This is a wrapper around the [Pagy::KeysetForUI API](/docs/api/keyset_for_ui.md). Please refer to the following resources:

[!ref Keyset For UI: Documentation](/docs/api/keyset_for_ui.md)

[!ref Keyset Pagination: Concepts and Overview](/docs/api/keyset.md)

This extra adds a `pagy_keyset_for_ui` constructor method that can be used in your controllers, and provides the automatic setting
of the variables from the request `params`.

It also handles the connection with the client and the `cutoffs` stored in the `sessionStorage`.

It works with

## Synopsis

This section integrates the [Keyset Extra Synopsis](/docs/extras/keyset.md)

```ruby Controller (action)
# Basic defaults (uses the session object as the cache)
@pagy, @records = pagy_keyset_for_ui(set)

# Other variables
@pagy, @records = pagy_keyset_for_ui(set, **vars)
```

### Overriding



## Variables


## Methods

==- `pagy_keyset_for_ui(set, **vars)`

This method is similar to the `pagy` (for offset pagination) method. It returns the `pagy` object and the array of `records`
pulled from the DB.

===
2 changes: 1 addition & 1 deletion docs/how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This page contains the practical tips and examples to get the job done with Pagy

You can also [Ask any question to the Pagy trained AI](https://gurubase.io/g/pagy) for instant answers not covered in this page.

## Choose between offset, countless or keyset pagination
## Choose the right pagination technique

[AI-powered answer](https://gurubase.io/g/pagy/choose-between-pagy-offset-countless-keyset)

Expand Down
14 changes: 10 additions & 4 deletions playground.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ icon: play-24

# Pagy Playground

You can showcase, clone and develop a few pagy APPs without the need to setup anything on your side!
You can showcase, clone and develop a few pagy APPs without the need to set up anything on your side!

```sh
$ bundle exec pagy --help
Expand Down Expand Up @@ -109,9 +109,15 @@ This are the interactive showcase/repro for the keyset extra with `ActiveRecord`

Run the interactive demo from your terminal:

```sh
bundle exec pagy keyset_ar
bundle exec pagy keyset_s
```sh
bundle exec pagy | grep keyset
keyset Showcase the Keyset pagination (ActiveRecord example)
keyset_for_ui Showcase the Keyset Numeric pagination (ActiveRecord example)
keyset_sequel Showcase the Keyset pagination (Sequel example)

bundle exec pagy keyset
bundle exec pagy keyset_for_ui
bundle exec pagy keyset_sequel
```

...and point your browser to http://0.0.0.0:8000
Expand Down

0 comments on commit 56750e3

Please sign in to comment.