Skip to content

Commit

Permalink
Finalize changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ddnexus committed Mar 7, 2025
1 parent 22a7cd4 commit 73470fb
Show file tree
Hide file tree
Showing 24 changed files with 169 additions and 166 deletions.
36 changes: 22 additions & 14 deletions CHANGELOG-10.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ None
This version introduces a complete redesign of the legacy code.

- **New [Keynav](https://ddnexus.github.io/pagy/toolbox/paginator/keynav_js.md) Pagination**
- The pagy-exclusive technique using [keyset](https://ddnexus.github.io/pagy/toolbox/paginator/keyset.md) pagination alongside all frontend helpers.
- The pagy-exclusive technique using [keyset](https://ddnexus.github.io/pagy/toolbox/paginator/keyset.md) pagination alongside
all frontend helpers.
- **Method Autoloading**
- Methods are autoloaded only if used, unused methods consume no memory.
- **Intelligent automation**
Expand All @@ -67,9 +68,10 @@ This version introduces a complete redesign of the legacy code.

Take a look at the [Examples](https://github.com/ddnexus/pagy#examples) for a quick overview.

### Breaking Changes / Updating Guide
### Breaking Changes / Updating Guide

Your existing code will require several minor adjustments to function. Following these changes, **this API will remain stable for a long time**.
Your existing code will require several minor adjustments to function. Following these changes, **this API will remain stable for
a long time**.

#### Replace your `pagy.rb` config file

Expand All @@ -86,15 +88,20 @@ Your existing code will require several minor adjustments to function. Following
- Remove any existing `include Pagy::Frontend`
- The paginators (i.e. the `pagy_*` methods returning the `@pagy` instance and the `@records`) got integrated in the `pagy`
method.
- The old `pagy(...)` statement works as-is, but it is _preferable_ to update it to the new explicit syntax: `pagy(:offset, ...)`.
- The old `pagy(...)` statement works as-is, but it is _preferable_ to update it to the new explicit syntax:
`pagy(:offset, ...)`.
- All the `pagy_*` helpers provided by the `Pagy::Frontend` are now `@pagy` instance methods (and most have been renamed). _(See
how to replace them in the [Extras Changes](#extras-changes))_

#### Core changes

- If the `:params` variable was set to a lambda, ensure it directly modifies the passed `query_params`.
- The returned value is now ignored for a slightly better performance.
- The `:page_key` and `:limit_key` (legacy `:page_param` and `:limit_param`) are strings now, not symbols.
- The `:params` variable/option has been replaced with the `:query_tweak` option. It has nothing to do with params anymore,
because it's a specific override, besides they have string keys and not symbol keys (for significant fewer conversions).
- If set to a lambda, ensure it directly modifies the passed `query_params`. The returned value is now ignored for a slightly
better performance.
- The `:page_param` and `:limit_param` have been replaced by `:page_key` and `:limit_key`, which are strings now, not symbols (for
significant fewer conversions).
- The `count_args` variable has been removed. Pagy will set it automatically.
- The `:outset` and `:cycle` variables have been removed.
- They were seldom used, mostly useless, and implementing them in your own code is trivial.
- The `:anchor_string` variable has been removed
Expand Down Expand Up @@ -123,7 +130,6 @@ understanding.
| Naming | `*prev*` | `*previous*` | Because we don't use abbreviated words anymore (check: option, accessor, methods, CSS). |
| Option | `size: 7` | `slots: 7` | Because it's actually the number of page slots, and avoids confusion with other `size`s. |
| Option | `ends: false` | `compact: true` | Because it's an opt-in option of the `series`, boolean inverse of `ends`. |
| Option | `:count_args` | `:count_arguments` | Because we don't use abbreviated words anymore. |
| Option | `:page_param` | `:page_key` | Because `page_param` make people think "page param value". Value is a string now, not a symbol. |
| Option | `:limit_param` | `:limit_key` | Because `limit_param` make people think "limit param value". Value is a string now, not a symbol. |
| Variable | `@pagy_locale = ...` | `Pagy::I18n = ...` | Because the `Pagy::I18n` API is now fully compatible with the `i18n` gem |
Expand Down Expand Up @@ -185,11 +191,12 @@ All the extras are gone. Here is what to do in order to accomodate the changes:
- Replace `pagy_countless(...)` with `pagy(:countless, ...)`
- Rename any existing `countless_minimal: true` to `headless: true`.
- Leave all remaining elements unchanged.

##### `calendar`

- Replace `pagy_calendar(...)` with `pagy(:calendar, ...)`.
- Remove your old localization configuration (if any), then uncomment and customize the following line in the `pagy.rb` initializer:
- Remove your old localization configuration (if any), then uncomment and customize the following line in the `pagy.rb`
initializer:
`Pagy::Calendar.localize_with_rails_i18n_gem(*your_locales)`.
- _Note: In non-Rails applications, calendar localization requires adding `rails-i18n` to your Gemfile._
- Replace any existing `Pagy::Calendar::OutOfRangeError` with `Pagy::RangeError`.
Expand Down Expand Up @@ -234,7 +241,7 @@ All the extras are gone. Here is what to do in order to accomodate the changes:

##### `limit`

- Rename the existing `:limit_param` to `:limit_sym`.
- Rename the existing `:limit_param` to `:limit_key`.
- Delete the existing `:limit_max` and `:limit_extra`.
- Enable the feature by passing `requestable_limit: your_max_limit` option to the `pagy` method.

Expand Down Expand Up @@ -286,12 +293,13 @@ All the extras are gone. Here is what to do in order to accomodate the changes:

##### `size` (discontinued feature)

- Pagination bars similar to WillPaginate and Kaminari are not good for a lot of reasons. If still required, adapt the
legacy file from a previous commit.
- Pagination bars similar to WillPaginate and Kaminari are not good for a lot of reasons. If still required, adapt the legacy file
from a previous commit.

##### `trim` (discontinued feature)

- It was mostly useless and half-baked, causing numerous complications in both the Ruby and JavaScript code for no significant benefit.
- It was mostly useless and half-baked, causing numerous complications in both the Ruby and JavaScript code for no significant
benefit.
- Use an appropriate approach to address your requirement, such as utilizing URL rewriting at the HTTP server level.

#### Direct instantiation of the pagy classes is not recommended
Expand Down
55 changes: 30 additions & 25 deletions README.md

Large diffs are not rendered by default.

Binary file added assets/images/pagy-the-frog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/README.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
label: Pagy 🐸 The Leaping Gem!
label: Pagy 💚 The Leaping Gem!
icon: heart
56 changes: 19 additions & 37 deletions docs/guides/how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ You can customize the number and position of page links in the navigation bar us

==- Force the `:page`

Pagy retrieves the page from the `params[:page]`. To force a specific page number, pass it directly to the `pagy` method. For example:
Pagy retrieves the page from the `'page'` request query hash. To force a specific page number, pass it directly to the `pagy` method. For example:

```ruby controller
@pagy, @records = pagy(:offset, collection, page: 3) # force page #3
Expand All @@ -77,26 +77,16 @@ See [ARIA](../resources/ARIA.md).

==- Customize the page and limit symbols

By default, Pagy retrieves the page from `params[:page]` and generates URLs using the `:page` query parameter, e.g., `?page=3`.
By default, Pagy retrieves the page from the request query hash and generates URLs using the `"page"` key, e.g., `?page=3`.

- Set `page_sym: :your_symbol` to customize URL generation, e.g., `?your_symbol=3`.
- Set the `limit_sym` to customize the `limit` param the same way.
- Set `page_key: "your_key"` to customize URL generation, e.g., `?your_key=3`.
- Set the `limit_key` to customize the `limit` param the same way.

See [Common Options](../toolbox/paginators#common-options)

==- Customize the params
==- Customize the URL query

Modify the parameters embedded in page link URLs by using the `:params` option, which can be:
- a `Hash` of parameters to merge
- a `Proc` to edit, add, or delete request parameters.
- The `Proc` will receive the **key-stringified** `query_params` hash complete with the pagy-added params, and it should modify
it in place.

Here is an example with `except!` (available in Rails) and `merge!`:

```ruby
@pagy, @records = pagy(:offset, collection, params: ->(params) { params.except!('not_useful').merge!('custom' => 'useful') })
```
See the [:query_tweak Option](../toolbox/paginators.md#common-options)

==- Add a URL fragment

Expand Down Expand Up @@ -185,11 +175,11 @@ Explore the following options:

==- Paginate with JSON:API

Enable `jsonapi: true`, optionally providing `:page_sym` and `:limit_sym`:
Enable `jsonapi: true`, optionally providing `:page_key` and `:limit_key`:

```ruby
# JSON:API nested query_params: E.g.: ?page[number]=2&page[size]=100
@pagy, @records = pagy(:offset, collection, jsonapi: true, page_sym: :number, limit_sym: :size)
@pagy, @records = pagy(:offset, collection, jsonapi: true, page_key: 'number', limit_key: 'size'R)
```

==- Paginate for Javascript Frameworks
Expand Down Expand Up @@ -279,28 +269,20 @@ of the above logic.

You can also
paginate [multiple model in the same request](https://www.imaginarycloud.com/blog/how-to-paginate-ruby-on-rails-apps-with-pagy/)
by simply using different `:page_sym` for each instance:
by simply using different `:page_key` for each instance:

```rb

def index # controller action
@pagy_stars, @stars = pagy(:offset, Star.all, page_sym: :page_stars)
@pagy_nebulae, @nebulae = pagy(:offset, Nebula.all, page_sym: :page_nebulae)
@pagy_stars, @stars = pagy(:offset, Star.all, page_key: :page_stars)
@pagy_nebulae, @nebulae = pagy(:offset, Nebula.all, page_key: :page_nebulae)
end
```

==- Paginate only max_pages, regardless the count

See [:max_pages](../toolbox/paginators.md#common-options) option.

==- Paginate non-ActiveRecord collections

For collections other than `ActiveRecord` (e.g. `mongoid`, etc.), you might want to change the `:count_arguments` default to suite your ORM count method:

```ruby pagy.rb (initializer)
Pagy.options[:count_arguments] = []
```

==- Paginate collections with metadata

When your collection is already paginated and contains count and pagination metadata, you don't need any `pagy*` controller
Expand All @@ -319,7 +301,7 @@ As you can see, it contains the pagination metadata that you can use to set up t
# get the paginated collection
tobj = Tmdb::Search.movie("Harry Potter", page: params[:page])
# use its count and page to initialize the @pagy object
@pagy = Pagy::Offset.new(count: tobj.total_results, page: tobj.page, request: Pagy::Get.hash_from(request))
@pagy = Pagy::Offset.new(count: tobj.total_results, page: tobj.page, request: Pagy::Request.new(request))
# set the paginated collection records
@movies = tobj.results
```
Expand Down Expand Up @@ -426,9 +408,9 @@ pagy demo
For non-rack environments that don't respond to the request method, you should pass the `:request` option to the paginator, set
with a hash with the following keys:

- `:base_url` (e.g. 'http://www.example.com')
- `:path` (e.g. '/path')
- `:params` (e.g. a string-key hash of the request query params)
- `:base_url` (e.g. 'http://www.example.com')
- `:path` (e.g. '/path')
- `:query_hash` (e.g. a string-keyed hash of the request query)

==- Use `pagy` outside of a controller or view

Expand Down Expand Up @@ -467,10 +449,10 @@ def filtered_action
if params[:filter_key] # already cached
filters = session[params[:filter_key]]
else # first request
filters = params[:filters]
key = Digest::SHA1.hexdigest(filters.sort.to_json)
session[key] = filters
options[:params] = { filter_key: key }
filters = params[:filters]
key = Digest::SHA1.hexdigest(filters.sort.to_json)
session[key] = filters
options[:query_tweak] = { filter_key: key }
end
collection = Product.where(**filters)
@pagy, @records = pagy(:offset, collection, **options)
Expand Down
4 changes: 2 additions & 2 deletions docs/sandbox/console.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ You can use a few method to create a simple collection paginated with the `:offs
=> Collection

>> pagy, records = pagy(:offset, collection.new, limit: 10) # Example pagination of sample data
=> [#<Pagy::Offset:0x00007fb92fb35840 @count=1000, @from=1, @in=10, @in_range=true, @last=100, @limit=10, @next=2, @offset=0, @options={limit: 10, limit_sym: :limit, page_sym: :page, page: 1, request: {base_url: "http://www.example.com", path: "/path", query_params: {example: "123"}}, count: 1000}, @page=1, @to=10>, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
=> [#<Pagy::Offset:0x00007fb92fb35840 @count=1000, @from=1, @in=10, @in_range=true, @last=100, @limit=10, @next=2, @offset=0, @options={limit: 10, limit_key: :limit, page_key: :page, page: 1, request: {base_url: "http://www.example.com", path: "/path", query_params: {example: "123"}}, count: 1000}, @page=1, @to=10>, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]

>> pagy.data_hash
=> {url_template: "/path?example=123&page=P ", first_url: "/path?example=123", previous_url: "/path?example=123&page=", page_url: "/path?example=123&page=1", next_url: "/path?example=123&page=2", last_url: "/path?example=123&page=100", count: 1000, page: 1, limit: 10, last: 100, in: 10, from: 1, to: 10, previous: nil, next: 2, options: {limit: 10, limit_sym: :limit, page_sym: :page, page: 1, request: {base_url: "http://www.example.com", path: "/path", query_params: {example: "123"}}, count: 1000}}
=> {url_template: "/path?example=123&page=P ", first_url: "/path?example=123", previous_url: "/path?example=123&page=", page_url: "/path?example=123&page=1", next_url: "/path?example=123&page=2", last_url: "/path?example=123&page=100", count: 1000, page: 1, limit: 10, last: 100, in: 10, from: 1, to: 10, previous: nil, next: 2, options: {limit: 10, limit_key: :limit, page_key: :page, page: 1, request: {base_url: "http://www.example.com", path: "/path", query_params: {example: "123"}}, count: 1000}}

>> pagy.links_hash
=> {first: "/path?example=123", next: "/path?example=123&page=2", last: "/path?example=123&page=100"}
Expand Down
4 changes: 2 additions & 2 deletions docs/toolbox/initializer.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
label: pagy 🐸 Initializer
label: pagy 💚 Initializer
icon: gear
order: 100
---

#

## 🐸 Initializer
## <span style="font-size: .65em; vertical-align: middle">💚</span> Initializer

---

Expand Down
6 changes: 3 additions & 3 deletions docs/toolbox/methods.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
label: pagy 🐸 Methods
label: pagy 💚 Methods
icon: mention
order: 80
categories:
Expand All @@ -8,11 +8,11 @@ categories:

#

## 🐸 Methods
## <span style="font-size: .65em; vertical-align: middle">💚</span> Methods

---

The `@pagy` instance provides methods for every UI components and helpers to use in your code.
The `@pagy` instance provides methods for every navigation tag and helper to use in your code.

Its class is determined by the paginator used, but you can safely ignore it. Simply utilize its methods.

Expand Down
4 changes: 2 additions & 2 deletions docs/toolbox/methods/page_url.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Try this method in the [Pagy Console](../../sandbox/console.md):

```ruby
>> @pagy, @records = pagy(:offset, collection.new, page: 3)
=> [#<Pagy::Offset:0x00007f77c4a352f8 @count=1000, @from=41, @in=20, @in_range=true, @last=50, @limit=20, @next=4, @offset=40, @options={limit: 20, limit_sym: :limit, page_sym: :page, page: 3, request: {base_url: "http://www.example.com", path: "/path", query_params: {example: "123"}}, count: 1000}, @page=3, @previous=2, @to=60>, [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]]
=> [#<Pagy::Offset:0x00007f77c4a352f8 @count=1000, @from=41, @in=20, @in_range=true, @last=50, @limit=20, @next=4, @offset=40, @options={limit: 20, limit_key: "limit", page_key: "page", page: 3, request: {base_url: "http://www.example.com", path: "/path", query_params: {example: "123"}}, count: 1000}, @page=3, @previous=2, @to=60>, [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60]]

>> @pagy.page_url(:first)
=> "/path?example=123"
Expand All @@ -33,7 +33,7 @@ Try this method in the [Pagy Console](../../sandbox/console.md):
>> @pagy.page_url(:last)
=> "/path?example=123&page=50"

>> @pagy.page_url(23, page_sym: :custom_page)
>> @pagy.page_url(23, page_key: 'custom_page')
=> "/path?example=123&custom_page=23"

>> @pagy.page_url(23, absolute: true)
Expand Down
57 changes: 33 additions & 24 deletions docs/toolbox/paginators.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
label: pagy 🐸 Paginators
label: pagy 💚 Paginators
icon: database
order: 90
categories:
Expand All @@ -8,19 +8,20 @@ categories:

#

## 🐸 Paginators
## <span style="font-size: .65em; vertical-align: middle">💚</span> Paginators

---

The `pagy` method starts every pagination. It paginates a collection and returns the `@pagy` instance and the page of `@records`.
### `pagy` method

The `pagy` method starts every pagination.

Include the `Pagy::Method` where you are going to use it _(usually in ApplicationContoller)_:

```ruby
include Pagy::Method
```

Now, you can use it to paginate ANY collection, with ANY technique:
You can use it to paginate ANY collection, with ANY technique:

```ruby
@pagy, @records = pagy(:offset, collection, **options)
Expand All @@ -31,6 +32,9 @@ Now, you can use it to paginate ANY collection, with ANY technique:
- `@pagy` is the pagination istance. It provides methods for every UI components and helpers to use in your code.
- `@records` are the records belonging to the requested page

### Paginators

The `paginators` are internal methods that provide different type of pagination for different types of collections, with a common API.

==- Common Options

Expand All @@ -39,32 +43,37 @@ Now, you can use it to paginate ANY collection, with ANY technique:
Individual paginators may offer additional options, which are documented with the paginator itself.
!!!

- `:page`
- Set it only to force the current `:page`. _(It is set automatically from the request params)_.
- `:limit`
- `page: 3`
- Set it only to force the current `:page`. _(It is set automatically from the request query hash)_.
- `limit: 10`
- Specifies the number of items per page (default: `20`)
- `requestable_limit: max_limit`
- Allows the client to set the `:limit` in the `request` params, up to the `max_limit` value
- `:max_pages`
- `requestable_limit: 1_000`
- Allows the client to set the `:limit` in the `request` query, up to `1_000` in the example
- `max_pages: 500`
- Restricts pagination to only `:max_pages`. (`Pagy::Calendar::*` unit objects ignore it)
- `jsonapi: true`
- Enables JSON:API-compliant URLs and query_params
- `:page_sym`
- Set it to change the symbol of the `:page` in URLs and query_params (default `:page`).
- `:limit_sym`
- Set it to change the symbol of the `:limit` in URLs and query_params (default `:limit`).
- `:params`
- Set it to a `Hash` of params to merge with the query params.
- Set it to a `Lambda` to edit/add/delete the request params (modify the query_params directly: the result is ignored). Keys
must be strings.
- `:request_path`
- `page_key: 'custom_page'`
- Set it to change the key string used for the `:page` in URLs (default `'page'`).
- `limit_key: 'custom_limit'`
- Set it to change the key string used for the `:limit` in URLs (default `'limit'`).
- `query_tweak: tweak`
- Set it to a _string-keyed_ `Hash` to merge with the URL `query_hash`.
```ruby
tweak = { 'custom' => 'useful' }
```
- Set it to a `Lambda` to directly edit the passed `query_hash` itself. Its result is ignored.
```ruby
tweak = ->(query_hash) { query_hash.except!('not_useful').merge!('custom' => 'useful') }
```
- `request_path: '/custom_path'`
- Overrides the request path in pagination URLs. Pass the path only (not the absolute URL). _(see [Pass the request path](../guides/how-to.md#paginate-multiple-independent-collections))_
- `:request`
- `request: custom_request`
- **Set this hash only in non-rack environments** or when instructed by the docs. _(It is set automatically from the request)_. For example:
```ruby
{ base_url: 'http://www.example.com',
path: '/path',
query_params: { 'param1' => 1234 } } # string keys only
{ base_url: 'http://www.example.com',
path: '/path',
query_hash: { 'param1' => 1234 } } # string keys only
```

==- Common Readers
Expand Down
Loading

0 comments on commit 73470fb

Please sign in to comment.