From eec5fc327e414e6b81ed10b69bf5b3c01fdc5866 Mon Sep 17 00:00:00 2001
From: Domizio Demichelis
Date: Thu, 4 Jul 2024 05:55:03 +0700
Subject: [PATCH 01/20] Drop the support for 8+ deprecations
---
.rubocop.yml | 4 -
.simplecov | 4 -
CHANGELOG.md | 214 +++--------------------------
CHANGELOG_LEGACY.md | 191 +++++++++++++++++++++++++
gem/lib/pagy.rb | 2 -
gem/lib/pagy/extras/foundation.rb | 95 -------------
gem/lib/pagy/extras/materialize.rb | 100 --------------
gem/lib/pagy/extras/semantic.rb | 94 -------------
gem/lib/pagy/extras/uikit.rb | 98 -------------
gem/pagy.gemspec | 9 --
src/build | 5 -
11 files changed, 207 insertions(+), 609 deletions(-)
delete mode 100644 gem/lib/pagy/extras/foundation.rb
delete mode 100644 gem/lib/pagy/extras/materialize.rb
delete mode 100644 gem/lib/pagy/extras/semantic.rb
delete mode 100644 gem/lib/pagy/extras/uikit.rb
diff --git a/.rubocop.yml b/.rubocop.yml
index 9de5cc230..5f3144685 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -14,10 +14,6 @@ AllCops:
- coverage/**/*
- docs/**/*
- gem/lib/optimist.rb
- - gem/lib/pagy/extras/foundation.rb
- - gem/lib/pagy/extras/materialize.rb
- - gem/lib/pagy/extras/semantic.rb
- - gem/lib/pagy/extras/uikit.rb
- src/**/*
- vendor/**/* # fix the problem with github ruby
diff --git a/.simplecov b/.simplecov
index 52bf695d0..d554d4ee9 100644
--- a/.simplecov
+++ b/.simplecov
@@ -25,8 +25,4 @@ SimpleCov.start do
add_group 'Calendar', %w[gem/lib/pagy/extras/calendar.rb
gem/lib/pagy/calendar]
add_group 'Tests', %w[test]
- add_filter %w[gem/lib/pagy/extras/foundation.rb
- gem/lib/pagy/extras/materialize.rb
- gem/lib/pagy/extras/semantic.rb
- gem/lib/pagy/extras/uikit.rb]
end
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b41ba0e9..eea0f7d5c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,9 +6,10 @@ icon: versions-24
## Breaking Changes
-If you upgrade from version `< 8.0.0` see the following:
+If you upgrade from version `< 9.0.0` see the following:
-- [Breaking changes in version 8.0.0](#version-800)
+- [Breaking changes in version 9.0.0](#version-900)
+- [Breaking changes in version 8.0.0](CHANGELOG_LEGACY.md#version-800)
- [Breaking changes in version 7.0.0](CHANGELOG_LEGACY.md#version-700)
- [Breaking changes in version 6.0.0](CHANGELOG_LEGACY.md#version-600)
- [Breaking changes in version 5.0.0](CHANGELOG_LEGACY.md#version-500)
@@ -19,208 +20,25 @@ If you upgrade from version `< 8.0.0` see the following:
## Deprecations
-- The `foundation`, `materialize`, `semantic` and `uikit` CSS extras have been discontinued and will be removed in v9
- (See the [details](https://github.com/ddnexus/pagy/discussions/672#discussioncomment-9212328))
-- Protected method `Pagy#setup_pages_var`. Use `Pagy#setup_last_var` instead
-- **Javascript files renamed**
+- None
+
+## Version 9.0.0
+
+### Breaking Changes
+
+- The `foundation`, `materialize`, `semantic` and `uikit` CSS extras have been removed:
+ (See the [reasons](https://github.com/ddnexus/pagy/discussions/672#discussioncomment-9212328))
+- Protected method `Pagy#setup_pages_var` have been removed: use `Pagy#setup_last_var` instead
+- **Javascript renamed files**
- `pagy.js`: use `pagy.min.js`
- `pagy-module.js`: use `pagy.mjs`
- `pagy-dev.js`: use the `pagy.min.js`with the `pagy.min.js.map`
- `pagy-module.d.ts`: use `pagy.d.ts`
-- The Array type for the `:size` (e.g. `size: [1, 4, 4, 1]`) that generates the classic bar. Use the `:size` set to an integer
- with the `ends: true` variable (which are the default since 8.4.6). If a legacy bar it will remain REALLY a requirement, add
- `require 'pagy/extras/size` to your `pagy.rb` initalizer for smooth upgrades. (see [size extra](https://ddnexus.github.io/pagy/docs/extras/size))
+- The Array type for the `:size` (e.g. `size: [1, 4, 4, 1]`) that generates the classic bar is not supported anymore: use the
+ `:size` set to an integer with the `ends: true` variable (which are the default since 8.4.6). If a legacy bar remains REALLY
+ a requirement, add `require 'pagy/extras/size` to your `pagy.rb` initalizer. (See the [size extra](https://ddnexus.github.io/pagy/docs/extras/size))
## Version 8.6.3
-- Add missing DEFAULT[:max_pages] hint to the config/pagy.rb
-- Improve activerecord handling in playground apps
-- Fix the missing "ar.pagy.aria_label.nav.other" (closes #577)
-
-## Version 8.6.2
-
-- Fix the legacy size in code and test
-- Improve code readability and size check in series
-- Fix the old terminology in the demo.ru app
-
-## Version 8.6.1
-
-- Update playground apps and e2e tests
-- Update pagy.rb initializer
-
-## Version 8.6.0
-
-- Add translated pluralized aria_label.nav for "ar" locale (close #577)
-- Deprecate the legacy bar. Insert first and last pages and gaps when needed into the simple bar
-
-## Version 8.5.0
-
-- Improve pagy playground launcher
-- Refactor calendar class structure
-- Remove automatic skipping of bundle install in playground apps
-- Update ruby calendar test
-- Update cypress calendar test
-- Refactor calendar test environment to use activerecord
-- Add code for calendar counts
-- Remove redundant Warning
-- Convert calendar.ru to calendar_rails.ru
-
-## Version 8.4.5
-
-- Fix pluralization rule link on locale files (#716)
-- Install gems in pagy CI
-- Indentation changes
-- Remove :cycle false default
-- Fill aria_label.nav ca pluralized entry (#715) (Fixes #581)
-- Fix typos (#710)
-
-## Version 8.4.4
-
-- Update eslint: new configuration, stricter rules and javascript code
-
-## Version 8.4.3
-
-- Deprecate/rename javascript files keeping copies of old files to avoid production breaking changes; updates playground apps
-
-## Version 8.4.2
-
-- Limit the playground --rerun option to linux platforms
-- Simplify and improve the js environment by using bun
-
-## Version 8.4.1
-
-- Fix pagy.in in pagy_get_items method introduced in 8.4.0 (see #696) (closes #704) (closes #708) (#707)
-- Fix renamed Frontend Helpers to JS Tools and typo `next_a` "aria-label" (#700)
-- Fix rubocop
-
-## Version 8.4.0
-
-- Retrieve only @in items:
- - improve the performance of the last page in
- particular storage conditions (see #696)
-- Improve pagy launcher for pagy devs
-
-## Version 8.3.0
-
-- Discontinue foundation materialize, semantic and uikit CSS extras
-- Improve playground:
- - Add install option (automated in pagy development)
- - Fix HTML validation for all apps
- - Remove unused styles from the demo app
-- Hardcode version in pagy.gemspec
-
-## Version 8.2.2
-
-- Add nav translation for ko (closes #592) (#690)
-
-## Version 8.2.1
-
-- Fix empty page param raising error (closes #689)
-
-## Version 8.2.0
-
-- Fix the '#pagy_url_for' method for calendar pagination (#688)
-- Extend the use of pagy_get_page to the arel, array and countless extras
-- Add the pagy_get_count method to the backend
-
-## Version 8.1.2
-
-- Added "da" locale for aria_label.nav (closes #583)
-
-## Version 8.1.1
-
-- Fixed broken aria-label for disabled links in Foundation (#685)
-- Simplification of input variables and defaults: params and request_path are not instance variables
-
-## Version 8.1.0
-
-- Implement max_pages to limit the pagination regardless the actual count
-- Improve efficiency of params in pagy_url_for
-- Remove nil variables from DEFAULT
-- Removed redundant @pages, aliased with @last
-
-## Version 8.0.2
-
-- Minor change in rails app and RM run config
-- Fix canonical gem root:
- - Correct script.build: "NODE_PATH="$(bundle show 'pagy')/javascripts"
- - Move pagy.gemspec inside the gem root dir
-- Fix for Turbo not intercepting changes in window.location
-- Use require_relative for gem/lib files
-- Complete translation of aria.nav for "ru" locale (close #599)
-- Docs improvement and fixes
-
-## Version 8.0.1
-
-- Reorganize the gem root dir: it was the lib dir (containing everything), now is the gem dir (containing lib and everything
- else).
-- Fix broken link in README
-
-## Version 8.0.0
-
-### Breaking changes
-
-- Renamed/removed the following arguments for all the helpers:
- - Search `pagy_id:`, replace with `id:`
- - Search `nav_aria_label:`, replace with`aria_label:`
- - The `nav_i18n_key` has been removed: pass the interpolated/pluralized value as the `aria_label:` argument
- - The `item_i18n_key` has been removed: pass the interpolated/pluralized value as the `item_name:` argument
- - The `link_extra:` has been removed: its cumulative mechanism was confusing and error prone. The `:anchor_string` pagy
- variable substitutes it, however it's not an helper argument anymore, so you can assign it as the `DEFAULT[:anchor_string]`
- and/or pass it as any other pagy variable at object construction. (
- See [customize the link attributes](https://ddnexus.github.io/pagy/docs/how-to/#customize-the-link-attributes))
-- HTML structure, classes and internal methods have been changed: they may break your views if you used custom stylesheets,
- templates or helper overrides. See the complete changes below if you notice any cosmetic changes or get some exception.
-- The `navs` and `support` extras has been merged into the new [pagy extra](https://ddnexus.github.io/pagy/docs/extras/pagy).
- Search for `"extras/navs"` and
- `"extras/support"` and replace with `"extras/pagy"` (remove the duplicate if you used both)
-- The `"extras/frontend_helpers"` has been renamed to `"extras/js_tools"`
-- The build path for javascript builders has been updated to the canonical paths for gems, and has moved from the `lib` to
- the gem root. Notice that the correct setup in `package json` was still wrongly wrapped in the `gem` dir for 8.0.0-8.0.1, and it
- has finally been fixed in 8.0.2 (sorry for that):
- - 8.0.0-8.0.1 only: `build: "NODE_PATH=\"$(bundle show 'pagy')/gem/javascripts\" "`
- - 8.0.2+: `build: "NODE_PATH=\"$(bundle show 'pagy')/javascripts\" "`
-
-### Changes
-
-- Streamlined HTML and CSS helper structure. You may want to look at the actual output by running
- the [pagy demo](https://ddnexus.github.io/pagy/playground.md#3-demo-app)
- - The `pagy_nav` and `pagy_nav_js` helpers output a series of `a` tags inside a wrapper `nav` tag (nothing else)
- - The disabled links are so because they are missing the `href` attributes. (They also have the `role="link"`
- and `aria-disabled="true"` attributes)
- - The `current` and `gap` classes are assigned to the specific `a` tags
- - HTML changes
- - All the pagy helper root classes have been changed according to the following rule. For example:
- - `"pagy-nav"` > `"pagy nav"`
- - `"pagy-bootstrap-nav-js"` > `"pagy-bootstrap nav-js"`
- - and so on for all the helpers
- - The `active` class of the `*nav`/`*nav_js` links as been renamed as `current`
- - The `disabled`, `prev`, `next` and `pagy-combo-input` link classes have been removed (see
- the [stylesheets](https://ddnexus.github.io/pagy/docs/api/stylesheets/#pagy-scss) for details)
- - The `rel="prev"` and `rel="next"` attributes have been dropped (they are obsolete)
- - The `` and ``/`` wrappers in the dictionary files have been removed
-- The `pagy_link_proc` method (only used internally or in your custom overriding) has been renamed to `pagy_anchor` and it works
- slightly differently:
- - The `link_extra:` key argument has been removed
- - The `extra` positional argument of the returned lambda has been removed
- - The `classes:` and `aria_label:` keyword arguments have been added to the returned lambda
-- The `nav_aria_label_attr` method has been renamed as `nav_aria_label`
-- The internal `prev_aria_label_attr` and `next_aria_label_attr` methods have been removed
-- The `gap` in the nav bars is a disabled anchor element (`a` tag without a `href` attribute)
-- The `pagy_prev_html` and `pagy_next_html` have been renamed as `pagy_prev_a` and `pagy_next_a`
-- The `pagy_prev_link_tag` and `pagy_next_link_tag` have been renamed as `pagy_prev_link` and `pagy_next_link`
-- The `*combo_nav_js` and `pagy_items_selector_js` helpers use a more efficient code
-- The `src/pagy.ts` and relative built javascript files have been adapted to the above changes
-- The [stylesheets](https://ddnexus.github.io/pagy/docs/api/stylesheets/) are a lot simpler as a consequence of the changes above
-- All the `*combo-nav_js` of the framework extras use simpler structure and improve the look and feel consistently with their
- respective frameworks
-- All the frontend extra have been normalized and are totally consistent with each other; a few may add the `classes:`
- argument to a few components, when the framework allows it.
-- Created the [pagy playground](https://ddnexus.github.io/pagy/playground) system of apps working with the `pagy` executable.
-- Internal renaming `FrontendHelpers` > `JSTools`
-- Fix broken link of pagy.rb in docs (closes #668, #669)
-- Docs Improvements
-- Better code issue template
-
[LEGACY CHANGELOG >>>](CHANGELOG_LEGACY.md)
diff --git a/CHANGELOG_LEGACY.md b/CHANGELOG_LEGACY.md
index 24701f78c..87d384239 100644
--- a/CHANGELOG_LEGACY.md
+++ b/CHANGELOG_LEGACY.md
@@ -6,6 +6,197 @@ visibility: hidden
# LEGACY CHANGELOG
+## Version 8.6.3
+
+- Add missing DEFAULT[:max_pages] hint to the config/pagy.rb
+- Improve activerecord handling in playground apps
+- Fix the missing "ar.pagy.aria_label.nav.other" (closes #577)
+
+## Version 8.6.2
+
+- Fix the legacy size in code and test
+- Improve code readability and size check in series
+- Fix the old terminology in the demo.ru app
+
+## Version 8.6.1
+
+- Update playground apps and e2e tests
+- Update pagy.rb initializer
+
+## Version 8.6.0
+
+- Add translated pluralized aria_label.nav for "ar" locale (close #577)
+- Deprecate the legacy bar. Insert first and last pages and gaps when needed into the simple bar
+
+## Version 8.5.0
+
+- Improve pagy playground launcher
+- Refactor calendar class structure
+- Remove automatic skipping of bundle install in playground apps
+- Update ruby calendar test
+- Update cypress calendar test
+- Refactor calendar test environment to use activerecord
+- Add code for calendar counts
+- Remove redundant Warning
+- Convert calendar.ru to calendar_rails.ru
+
+## Version 8.4.5
+
+- Fix pluralization rule link on locale files (#716)
+- Install gems in pagy CI
+- Indentation changes
+- Remove :cycle false default
+- Fill aria_label.nav ca pluralized entry (#715) (Fixes #581)
+- Fix typos (#710)
+
+## Version 8.4.4
+
+- Update eslint: new configuration, stricter rules and javascript code
+
+## Version 8.4.3
+
+- Deprecate/rename javascript files keeping copies of old files to avoid production breaking changes; updates playground apps
+
+## Version 8.4.2
+
+- Limit the playground --rerun option to linux platforms
+- Simplify and improve the js environment by using bun
+
+## Version 8.4.1
+
+- Fix pagy.in in pagy_get_items method introduced in 8.4.0 (see #696) (closes #704) (closes #708) (#707)
+- Fix renamed Frontend Helpers to JS Tools and typo `next_a` "aria-label" (#700)
+- Fix rubocop
+
+## Version 8.4.0
+
+- Retrieve only @in items:
+ - improve the performance of the last page in
+ particular storage conditions (see #696)
+- Improve pagy launcher for pagy devs
+
+## Version 8.3.0
+
+- Discontinue foundation materialize, semantic and uikit CSS extras
+- Improve playground:
+ - Add install option (automated in pagy development)
+ - Fix HTML validation for all apps
+ - Remove unused styles from the demo app
+- Hardcode version in pagy.gemspec
+
+## Version 8.2.2
+
+- Add nav translation for ko (closes #592) (#690)
+
+## Version 8.2.1
+
+- Fix empty page param raising error (closes #689)
+
+## Version 8.2.0
+
+- Fix the '#pagy_url_for' method for calendar pagination (#688)
+- Extend the use of pagy_get_page to the arel, array and countless extras
+- Add the pagy_get_count method to the backend
+
+## Version 8.1.2
+
+- Added "da" locale for aria_label.nav (closes #583)
+
+## Version 8.1.1
+
+- Fixed broken aria-label for disabled links in Foundation (#685)
+- Simplification of input variables and defaults: params and request_path are not instance variables
+
+## Version 8.1.0
+
+- Implement max_pages to limit the pagination regardless the actual count
+- Improve efficiency of params in pagy_url_for
+- Remove nil variables from DEFAULT
+- Removed redundant @pages, aliased with @last
+
+## Version 8.0.2
+
+- Minor change in rails app and RM run config
+- Fix canonical gem root:
+ - Correct script.build: "NODE_PATH="$(bundle show 'pagy')/javascripts"
+ - Move pagy.gemspec inside the gem root dir
+- Fix for Turbo not intercepting changes in window.location
+- Use require_relative for gem/lib files
+- Complete translation of aria.nav for "ru" locale (close #599)
+- Docs improvement and fixes
+
+## Version 8.0.1
+
+- Reorganize the gem root dir: it was the lib dir (containing everything), now is the gem dir (containing lib and everything
+ else).
+- Fix broken link in README
+
+## Version 8.0.0
+
+### Breaking changes
+
+- Renamed/removed the following arguments for all the helpers:
+ - Search `pagy_id:`, replace with `id:`
+ - Search `nav_aria_label:`, replace with`aria_label:`
+ - The `nav_i18n_key` has been removed: pass the interpolated/pluralized value as the `aria_label:` argument
+ - The `item_i18n_key` has been removed: pass the interpolated/pluralized value as the `item_name:` argument
+ - The `link_extra:` has been removed: its cumulative mechanism was confusing and error prone. The `:anchor_string` pagy
+ variable substitutes it, however it's not an helper argument anymore, so you can assign it as the `DEFAULT[:anchor_string]`
+ and/or pass it as any other pagy variable at object construction. (
+ See [customize the link attributes](https://ddnexus.github.io/pagy/docs/how-to/#customize-the-link-attributes))
+- HTML structure, classes and internal methods have been changed: they may break your views if you used custom stylesheets,
+ templates or helper overrides. See the complete changes below if you notice any cosmetic changes or get some exception.
+- The `navs` and `support` extras has been merged into the new [pagy extra](https://ddnexus.github.io/pagy/docs/extras/pagy).
+ Search for `"extras/navs"` and
+ `"extras/support"` and replace with `"extras/pagy"` (remove the duplicate if you used both)
+- The `"extras/frontend_helpers"` has been renamed to `"extras/js_tools"`
+- The build path for javascript builders has been updated to the canonical paths for gems, and has moved from the `lib` to
+ the gem root. Notice that the correct setup in `package json` was still wrongly wrapped in the `gem` dir for 8.0.0-8.0.1, and it
+ has finally been fixed in 8.0.2 (sorry for that):
+ - 8.0.0-8.0.1 only: `build: "NODE_PATH=\"$(bundle show 'pagy')/gem/javascripts\" "`
+ - 8.0.2+: `build: "NODE_PATH=\"$(bundle show 'pagy')/javascripts\" "`
+
+### Changes
+
+- Streamlined HTML and CSS helper structure. You may want to look at the actual output by running
+ the [pagy demo](https://ddnexus.github.io/pagy/playground.md#3-demo-app)
+ - The `pagy_nav` and `pagy_nav_js` helpers output a series of `a` tags inside a wrapper `nav` tag (nothing else)
+ - The disabled links are so because they are missing the `href` attributes. (They also have the `role="link"`
+ and `aria-disabled="true"` attributes)
+ - The `current` and `gap` classes are assigned to the specific `a` tags
+ - HTML changes
+ - All the pagy helper root classes have been changed according to the following rule. For example:
+ - `"pagy-nav"` > `"pagy nav"`
+ - `"pagy-bootstrap-nav-js"` > `"pagy-bootstrap nav-js"`
+ - and so on for all the helpers
+ - The `active` class of the `*nav`/`*nav_js` links as been renamed as `current`
+ - The `disabled`, `prev`, `next` and `pagy-combo-input` link classes have been removed (see
+ the [stylesheets](https://ddnexus.github.io/pagy/docs/api/stylesheets/#pagy-scss) for details)
+ - The `rel="prev"` and `rel="next"` attributes have been dropped (they are obsolete)
+ - The `` and ``/`` wrappers in the dictionary files have been removed
+- The `pagy_link_proc` method (only used internally or in your custom overriding) has been renamed to `pagy_anchor` and it works
+ slightly differently:
+ - The `link_extra:` key argument has been removed
+ - The `extra` positional argument of the returned lambda has been removed
+ - The `classes:` and `aria_label:` keyword arguments have been added to the returned lambda
+- The `nav_aria_label_attr` method has been renamed as `nav_aria_label`
+- The internal `prev_aria_label_attr` and `next_aria_label_attr` methods have been removed
+- The `gap` in the nav bars is a disabled anchor element (`a` tag without a `href` attribute)
+- The `pagy_prev_html` and `pagy_next_html` have been renamed as `pagy_prev_a` and `pagy_next_a`
+- The `pagy_prev_link_tag` and `pagy_next_link_tag` have been renamed as `pagy_prev_link` and `pagy_next_link`
+- The `*combo_nav_js` and `pagy_items_selector_js` helpers use a more efficient code
+- The `src/pagy.ts` and relative built javascript files have been adapted to the above changes
+- The [stylesheets](https://ddnexus.github.io/pagy/docs/api/stylesheets/) are a lot simpler as a consequence of the changes above
+- All the `*combo-nav_js` of the framework extras use simpler structure and improve the look and feel consistently with their
+ respective frameworks
+- All the frontend extra have been normalized and are totally consistent with each other; a few may add the `classes:`
+ argument to a few components, when the framework allows it.
+- Created the [pagy playground](https://ddnexus.github.io/pagy/playground) system of apps working with the `pagy` executable.
+- Internal renaming `FrontendHelpers` > `JSTools`
+- Fix broken link of pagy.rb in docs (closes #668, #669)
+- Docs Improvements
+- Better code issue template
+
## Version 7.0.11
- Fix jsonapi prev and next keys for unavailable links (#665)
diff --git a/gem/lib/pagy.rb b/gem/lib/pagy.rb
index 1118604c6..b864690f4 100644
--- a/gem/lib/pagy.rb
+++ b/gem/lib/pagy.rb
@@ -111,10 +111,8 @@ def setup_last_var
@last = [(@count.to_f / @items).ceil, 1].max
@last = vars[:max_pages] if vars[:max_pages] && @last > vars[:max_pages]
end
- alias setup_pages_var setup_last_var
end
-require_relative 'pagy/extras/size' # will be opt in in v9.0
require_relative 'pagy/backend'
require_relative 'pagy/frontend'
require_relative 'pagy/exceptions'
diff --git a/gem/lib/pagy/extras/foundation.rb b/gem/lib/pagy/extras/foundation.rb
deleted file mode 100644
index 22bc5dc9b..000000000
--- a/gem/lib/pagy/extras/foundation.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-# frozen_string_literal: true
-
-warn '[PAGY WARNING] The foundation extra has been discontinued and it will be removed in v9 ' \
- '(https://github.com/ddnexus/pagy/discussions/672#discussioncomment-9212328)'
-
-require_relative 'js_tools'
-
-class Pagy # :nodoc:
- # Frontend modules are specially optimized for performance.
- # The resulting code may not look very elegant, but produces the best benchmarks
- module FoundationExtra
- # Pagination for Foundation: it returns the html with the series of links to the pages
- def pagy_foundation_nav(pagy, id: nil, aria_label: nil, **vars)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
-
- html = +%()
- end
-
- # Javascript pagination for foundation: it returns a nav with a data-pagy attribute used by the pagy.js file
- def pagy_foundation_nav_js(pagy, id: nil, aria_label: nil, **vars)
- sequels = pagy.sequels(**vars)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
- tokens = { 'before' => %(
) }
-
- %()
- end
-
- # Javascript combo pagination for Foundation: it returns a nav with a data-pagy attribute used by the pagy.js file
- def pagy_foundation_combo_nav_js(pagy, id: nil, aria_label: nil)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
- pages = pagy.pages
-
- page_input = %(#{JSTools::A_TAG})
-
- %()
- end
-
- private
-
- def foundation_prev_html(pagy, a)
- if (p_prev = pagy.prev)
- %(
)
- end
- end
- end
- Frontend.prepend FoundationExtra
-end
diff --git a/gem/lib/pagy/extras/materialize.rb b/gem/lib/pagy/extras/materialize.rb
deleted file mode 100644
index 54d9538fa..000000000
--- a/gem/lib/pagy/extras/materialize.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-# frozen_string_literal: true
-
-warn '[PAGY WARNING] The materialize extra has been discontinued and it will be removed in v9 ' \
- '(https://github.com/ddnexus/pagy/discussions/672#discussioncomment-9212328)'
-
-require_relative 'js_tools'
-
-class Pagy # :nodoc:
- # Frontend modules are specially optimized for performance.
- # The resulting code may not look very elegant, but produces the best benchmarks
- module MaterializeExtra
- # Pagination for materialize: it returns the html with the series of links to the pages
- def pagy_materialize_nav(pagy, id: nil, aria_label: nil, **vars)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
-
- html = +%(
#{
- materialize_prev_html(pagy, a)})
- pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
- html << case item
- when Integer
- %(
)
- else
- raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
- end
- end
- html << %(#{materialize_next_html(pagy, a)}
)
- end
-
- # Javascript pagination for materialize: it returns a nav with a data-pagy attribute used by the pagy.js file
- def pagy_materialize_nav_js(pagy, id: nil, aria_label: nil, **vars)
- sequels = pagy.sequels(**vars)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
-
- tokens = { 'before' => %(
) }
-
- %()
- end
-
- # Javascript combo pagination for materialize: it returns a nav with a data-pagy attribute used by the pagy.js file
- def pagy_materialize_combo_nav_js(pagy, id: nil, aria_label: nil)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
- pages = pagy.pages
-
- page_input = %( ) <<
- JSTools::A_TAG
-
- %(
#{
- materialize_prev_html(pagy, a)
- }#{
- materialize_next_html(pagy, a)
- }
)
- end
-
- private
-
- def materialize_prev_html(pagy, a)
- if (p_prev = pagy.prev)
- %(
)
- end
- end
- end
- Frontend.prepend MaterializeExtra
-end
diff --git a/gem/lib/pagy/extras/semantic.rb b/gem/lib/pagy/extras/semantic.rb
deleted file mode 100644
index 9fe5ebdc8..000000000
--- a/gem/lib/pagy/extras/semantic.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-# frozen_string_literal: true
-
-warn '[PAGY WARNING] The semantic extra has been discontinued and it will be removed in v9 ' \
- '(https://github.com/ddnexus/pagy/discussions/672#discussioncomment-9212328)'
-
-require_relative 'js_tools'
-
-class Pagy # :nodoc:
- # Frontend modules are specially optimized for performance.
- # The resulting code may not look very elegant, but produces the best benchmarks
- module SemanticExtra
- # Pagination for semantic: it returns the html with the series of links to the pages
- def pagy_semantic_nav(pagy, id: nil, aria_label: nil, **vars)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
-
- html = %(
#{semantic_prev_html(pagy, a)})
- pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
- html << case item
- when Integer
- a.(item, pagy.label_for(item), classes: 'item')
- when String
- %(#{pagy.label_for(item)})
- when :gap
- %(
#{pagy_t 'pagy.gap'}
)
- else
- raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
- end
- end
- html << %(#{semantic_next_html(pagy, a)}
)
- end
-
- # Javascript pagination for semantic: it returns a nav with a data-pagy attribute used by the pagy.js file
- def pagy_semantic_nav_js(pagy, id: nil, aria_label: nil, **vars)
- sequels = pagy.sequels(**vars)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
- tokens = { 'before' => semantic_prev_html(pagy, a),
- 'a' => a.(PAGE_TOKEN, LABEL_TOKEN, classes: 'item'),
- 'current' => %(#{LABEL_TOKEN}),
- 'gap' => %(
#{pagy_t('pagy.gap')}
),
- 'after' => semantic_next_html(pagy, a) }
-
- %()
- end
-
- # Combo pagination for semantic: it returns a nav with a data-pagy attribute used by the pagy.js file
- def pagy_semantic_combo_nav_js(pagy, id: nil, aria_label: nil)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
- pages = pagy.pages
-
- page_input = %() <<
- JSTools::A_TAG
-
- %(
#{
- semantic_prev_html(pagy, a)
- } #{
- semantic_next_html(pagy, a)
- }
)
- end
-
- private
-
- def semantic_prev_html(pagy, a)
- if (p_prev = pagy.prev)
- a.(p_prev, pagy_t('pagy.prev'), classes: 'item', aria_label: pagy_t('pagy.aria_label.prev'))
- else
- %(
#{pagy_t('pagy.prev')}
)
- end
- end
-
- def semantic_next_html(pagy, a)
- if (p_next = pagy.next)
- a.(p_next, pagy_t('pagy.next'), classes: 'item', aria_label: pagy_t('pagy.aria_label.next'))
- else
- %(
#{pagy_t('pagy.next')}
)
- end
- end
- end
- Frontend.prepend SemanticExtra
-end
diff --git a/gem/lib/pagy/extras/uikit.rb b/gem/lib/pagy/extras/uikit.rb
deleted file mode 100644
index b684b409c..000000000
--- a/gem/lib/pagy/extras/uikit.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-# frozen_string_literal: true
-
-warn '[PAGY WARNING] The uikit extra has been discontinued and it will be removed in v9 ' \
- '(https://github.com/ddnexus/pagy/discussions/672#discussioncomment-9212328)'
-
-require_relative 'js_tools'
-
-class Pagy # :nodoc:
- # Frontend modules are specially optimized for performance.
- # The resulting code may not look very elegant, but produces the best benchmarks
- module UikitExtra
- # Pagination for uikit: it returns the html with the series of links to the pages
- def pagy_uikit_nav(pagy, id: nil, aria_label: nil, **vars)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
-
- html = %(
#{
- uikit_prev_html(pagy, a)})
- pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
- html << case item
- when Integer
- %(
#{a.(item)}
)
- when String
- %(
#{
- pagy.label_for(item)}
)
- when :gap
- %(
#{pagy_t 'pagy.gap'}
)
- else
- raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
- end
- end
- html << %(#{uikit_next_html(pagy, a)}
)
- end
-
- # Javascript pagination for uikit: it returns a nav with a data-pagy attribute used by the pagy.js file
- def pagy_uikit_nav_js(pagy, id: nil, aria_label: nil, **vars)
- sequels = pagy.sequels(**vars)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
- tokens = { 'before' => uikit_prev_html(pagy, a),
- 'a' => %(
#{a.(PAGE_TOKEN, LABEL_TOKEN)}
),
- 'current' => %(
#{
- LABEL_TOKEN}
),
- 'gap' => %(
#{pagy_t 'pagy.gap'}
),
- 'after' => uikit_next_html(pagy, a) }
-
- %(
)
- end
-
- # Javascript combo pagination for uikit: it returns a nav with a data-pagy attribute used by the pagy.js file
- def pagy_uikit_combo_nav_js(pagy, id: nil, aria_label: nil)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
- pages = pagy.pages
-
- page_input = %(#{JSTools::A_TAG})
-
- %(
#{
- uikit_prev_html(pagy, a)
- }#{
- uikit_next_html(pagy, a)
- }
)
- end
-
- private
-
- def uikit_prev_html(pagy, a)
- span = %()
- if (p_prev = pagy.prev)
- %(
)
- end
- end
- end
- Frontend.include UikitExtra
-end
diff --git a/gem/pagy.gemspec b/gem/pagy.gemspec
index 0c33e3524..4bbe01f1a 100644
--- a/gem/pagy.gemspec
+++ b/gem/pagy.gemspec
@@ -19,14 +19,5 @@ Gem::Specification.new do |s|
'changelog_uri' => 'https://github.com/ddnexus/pagy/blob/master/CHANGELOG.md',
'support' => 'https://github.com/ddnexus/pagy/discussions/categories/q-a' }
s.executables << 'pagy'
- s.post_install_message = <<~PIM
- *********************** PAGY WARNING! ***********************
- The foundation, materialize, semantic and uikit CSS extras
- have been discontinued and will be removed in v9
- https://github.com/ddnexus/pagy/discussions/672
- The javascript files have been deprecated/renamed
- https://ddnexus.github.io/pagy/changelog/#deprecations
- *************************************************************
- PIM
s.required_ruby_version = '>= 3.1'
end
diff --git a/src/build b/src/build
index abc7cd9b3..bb33cadfa 100755
--- a/src/build
+++ b/src/build
@@ -12,8 +12,3 @@ bun build ./pagy.ts --target=node --no-bundle --outfile=$jdir/pagy.mjs
bun build ./pagy.min.js --target=browser --minify --sourcemap=linked --outdir=$jdir
sed -i "0,/var ./{s/var ./window.Pagy/}" $jdir/pagy.min.js
-
-# Copy the deprecated files that can break a production app
-cd $jdir
-cp pagy.min.js pagy.js
-cp pagy.mjs pagy-module.js
From 5f9167fe2fde84086d754b84c7371144fb9137c1 Mon Sep 17 00:00:00 2001
From: Domizio Demichelis
Date: Thu, 4 Jul 2024 05:55:59 +0700
Subject: [PATCH 02/20] Refactor the anchor_string system
---
CHANGELOG.md | 4 ++++
docs/api/frontend.md | 7 +++++--
docs/api/pagy.md | 1 -
docs/extras/pagy.md | 22 ++++++++++++++++------
docs/how-to.md | 15 ++++-----------
docs/troubleshooting.md | 5 +----
gem/lib/pagy/extras/bootstrap.rb | 12 ++++++------
gem/lib/pagy/extras/bulma.rb | 15 +++++++++------
gem/lib/pagy/extras/calendar.rb | 7 +++----
gem/lib/pagy/extras/pagy.rb | 16 ++++++++--------
gem/lib/pagy/extras/trim.rb | 2 +-
gem/lib/pagy/frontend.rb | 11 +++++------
test/helpers/nav_tests.rb | 16 ++++++++--------
test/pagy/extras/calendar_extra_test.rb | 4 ++--
test/pagy/frontend_test.rb | 4 ++--
15 files changed, 74 insertions(+), 67 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eea0f7d5c..2be4e6d9d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,10 @@ If you upgrade from version `< 9.0.0` see the following:
- The Array type for the `:size` (e.g. `size: [1, 4, 4, 1]`) that generates the classic bar is not supported anymore: use the
`:size` set to an integer with the `ends: true` variable (which are the default since 8.4.6). If a legacy bar remains REALLY
a requirement, add `require 'pagy/extras/size` to your `pagy.rb` initalizer. (See the [size extra](https://ddnexus.github.io/pagy/docs/extras/size))
+- The `DEFAULT[:anchor_string]` has been dropped.
+- The `:anchor_string` is not an instance variable anymore, but a keyword argument for all the helpers, because it is
+ frontend code (see the [discussion](https://github.com/ddnexus/pagy/discussions/719)). Instead of passing it to the `pagy*`
+ method in the controller, pass it to any `pagy_*nav` method in the view.
## Version 8.6.3
diff --git a/docs/api/frontend.md b/docs/api/frontend.md
index 19d35c48b..a87f383d0 100644
--- a/docs/api/frontend.md
+++ b/docs/api/frontend.md
@@ -110,6 +110,8 @@ Here is how you should use it: in your helper call the method to get the proc (j
```ruby
a = pagy_anchor(pagy)
+# or
+a = pagy_anchor(pagy, anchor_string: 'verbatim string')
```
Then call the `a` proc to get the links (multiple times):
@@ -118,9 +120,10 @@ Then call the `a` proc to get the links (multiple times):
my_link = a.(page_number, text, classes:, aria_label:)
```
-#### The anchor_string variable
+#### The anchor_string argument
-If you need to add some HTML attribute to ALL the page links, you can set the `:anchor_string` variable.
+If you need to add some HTML attribute to ALL the page links, you can pass the `:anchor_string` keyword argument to any pagy
+helper, or if you use templates or override helpers, you can pass it also to the `pagy_anchor` method.
!!!warning Attributes Must be Valid HTML
For performance reasons, the `:anchor_string` string must be formatted as valid HTML attribute/value pairs because it will get
diff --git a/docs/api/pagy.md b/docs/api/pagy.md
index d6d4c0695..a1998f85b 100644
--- a/docs/api/pagy.md
+++ b/docs/api/pagy.md
@@ -130,7 +130,6 @@ number `1`. For performance reasons, only the instance variables get validated.
| Variable | Description | Default |
|:-----------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|
-| `:anchor_string` | The extra attributes string (formatted as a valid HTML `attr="value"`) added to the page links _(see [Customize the link attributes](/docs/how-to.md#customize-the-link-attributes))_ | `nil` |
| `:count` | The total count of the collection to paginate (mandatory argument) | `nil` |
| `:count_args` | The arguments passed to the `collection.count`. You may want to set it to `[]` in ORMs different than ActiveRecord | [:all] |
| `:cycle` | Enable cycling/circular/infinite pagination: `true` sets `next` to `1` when the current page is the last page | `false` |
diff --git a/docs/extras/pagy.md b/docs/extras/pagy.md
index 8b8b337af..b4023e9f3 100644
--- a/docs/extras/pagy.md
+++ b/docs/extras/pagy.md
@@ -73,20 +73,30 @@ bar links (e.g. `countless` extra).
Return the previous page URL string or nil. Useful to build minimalistic UIs that don't use nav bar
links (e.g. `countless` extra).
-==- `pagy_prev_a(pagy, text: pagy_t('pagy.prev'), aria_label: pagy_t('pagy.aria_label.prev))`
+==- `pagy_prev_a(pagy, **vars)`
Return the enabled/disabled previous page anchor tag. It is the same prev link string which is
part of the `pagy_nav` helper.
Useful to build minimalistic helpers UIs that don't use nav bar links (e.g. `countless` extra).
-==- `pagy_next_a(pagy, text: pagy_t('pagy.next'), aria_label: pagy_t('pagy.aria_label.prev))`
+The keyord argument used from `vars` are:
+- `text: pagy_t('pagy.prev')`
+- `aria_label: pagy_t('pagy.aria_label.prev)`
+- `anchor_string: nil`
+
+==- `pagy_next_a(pagy, **vars)`
Return the enabled/disabled next page anchor tag. It is the same next link string which is part of the
`pagy_nav` helper.
Useful to build minimalistic helpers UIs that don't use nav bar links (e.g. `countless` extra).
+The keyord argument used from `vars` are:
+- `text: pagy_t('pagy.prev')`
+- `aria_label: pagy_t('pagy.aria_label.prev)`
+- `anchor_string: nil`
+
==- `pagy_prev_link(pagy)`
Conditionally return the previous page `link` tag. Useful to add the link tag to the HTML `head`.
@@ -109,8 +119,8 @@ supports navbar links (see also [Pagy::Countless](/docs/api/countless.md) for mo
### Navless/incremental
-If you don't need the navbar you can just set the `:size` variable to [0] and the page links will be skipped from the
-rendering. That works with `Pagy` and `Pagy:Countless` instances. All the `*nav` helpers will render only the `prev` and `next`
+If you don't need the navbar you can just set the `:size` variable to `0` and the page links will be skipped from the rendering.
+That works with `Pagy` and `Pagy:Countless` instances. All the `*nav` helpers will render only the `prev` and `next`
links/buttons, allowing for a manual incremental pagination.
You can also use the `pagy_prev_html`, `pagy_next_html` mostly useful if you also use the `countless` extra.
@@ -124,7 +134,7 @@ require 'pagy/extras/countless'
```ruby incremental (controller action)
def incremental
- @pagy, @records = pagy_countless(collection, anchor_string: 'data-remote="true"')
+ @pagy, @records = pagy_countless(collection)
end
```
@@ -154,7 +164,7 @@ end
```erb _next_link.html.erb (partial)
-<%== pagy_next_a(@pagy, text: 'More...') %>
+<%== pagy_next_a(@pagy, text: 'More...', anchor_string: 'data-remote="true"') %>
```
```erb incremental.js.erb (javascript template)
diff --git a/docs/how-to.md b/docs/how-to.md
index c56699b97..263cca23d 100644
--- a/docs/how-to.md
+++ b/docs/how-to.md
@@ -185,20 +185,13 @@ You can also override the [pagy_get_page](/docs/api/backend.md#pagy-get-page-var
## Customize the link attributes
If you need to customize some HTML attribute of the page links, you may not need to override the `pagy_nav*` helper. It might be
-enough to pass some extra attribute string with the `:anchor_string` variable. For example:
+enough to pass some extra attribute string with the `:anchor_string` keyword argument. For example:
-```ruby
-# for all the Pagy instances
-Pagy::DEFAULT[:anchor_string] = 'data-remote="true"'
-
-# for a single Pagy instance (if you use the Pagy::Backend#pagy method)
-@pagy, @records = pagy(collection, anchor_string: 'data-remote="true"')
-
-# or directly to the constructor
-pagy = Pagy.new(count: 1000, anchor_string: 'data-remote="true"')
+```erb
+<%== pagy_nav(@pagy, anchor_string: 'data-remote="true"') %>
```
-_See more advanced details about [The anchor_string variable](api/frontend.md#the-anchor_string-variable)_
+_See more advanced details about [The anchor_string argument](api/frontend.md#the-anchor_string-argument)_
## Customize the params
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
index f69aab15d..cf6c148d7 100644
--- a/docs/troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -53,11 +53,8 @@ should be uniquely aria-identified in the page.
!!!danger Don't duplicate attributes with the `:anchor_string`!
-```ruby
-@pagy, @records = pagy(collection, anchor_string: 'class="my-class"')
-```
```erb
-<%== pagy_bootstrap_nav(@pagy, **vars) %>
+<%== pagy_bootstrap_nav(@pagy, anchor_string: 'class="my-class"', **vars) %>
```
The `class` attribute with a value of `"pagination"` is already added by the `pagy_bootstrap_nav` so it's a duplicate HTML
diff --git a/gem/lib/pagy/extras/bootstrap.rb b/gem/lib/pagy/extras/bootstrap.rb
index 6e84d9158..d37482b45 100644
--- a/gem/lib/pagy/extras/bootstrap.rb
+++ b/gem/lib/pagy/extras/bootstrap.rb
@@ -8,9 +8,9 @@ class Pagy # :nodoc:
# The resulting code may not look very elegant, but produces the best benchmarks
module BootstrapExtra
# Pagination for bootstrap: it returns the html with the series of links to the pages
- def pagy_bootstrap_nav(pagy, id: nil, classes: 'pagination', aria_label: nil, **vars)
+ def pagy_bootstrap_nav(pagy, id: nil, classes: 'pagination', aria_label: nil, anchor_string: nil, **vars)
id = %( id="#{id}") if id
- a = pagy_anchor(pagy)
+ a = pagy_anchor(pagy, anchor_string:)
html = %()
end
- # Return a performance optimized lambda to generate the HTML anchor element (a tag)
- # Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
- def pagy_anchor(pagy, anchor_string: nil, **vars)
- anchor_string &&= %( #{anchor_string})
- left, right = %(#{text})
+ # Generic pagination: it returns the html with the series of links to the pages
+ def pagy_nav(pagy, id: nil, aria_label: nil, anchor_string: nil, **vars)
+ id = %( id="#{id}") if id
+ a = pagy_anchor(pagy, anchor_string:)
+
+ html = %(#{
+ prev_a(pagy, a)})
+ pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
+ html << case item
+ when Integer
+ a.(item)
+ when String
+ %(#{pagy.label_for(item)})
+ when :gap
+ %(#{pagy_t('pagy.gap')})
+ else
+ raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
+ end
end
+ html << %(#{next_a(pagy, a)})
end
# Similar to I18n.t: just ~18x faster using ~10x less memory
# (@pagy_locale explicitly initialized in order to avoid warning)
- def pagy_t(key, opts = {})
- Pagy::I18n.translate(@pagy_locale ||= nil, key, opts)
+ def pagy_t(key, **opts)
+ Pagy::I18n.translate(@pagy_locale ||= nil, key, **opts)
end
private
diff --git a/gem/lib/pagy/i18n.rb b/gem/lib/pagy/i18n.rb
index e612fbcd0..2d2b64cb0 100644
--- a/gem/lib/pagy/i18n.rb
+++ b/gem/lib/pagy/i18n.rb
@@ -154,7 +154,7 @@ def load(*locales)
end
# Translate and pluralize the key with the locale DATA
- def translate(locale, key, opts = {})
+ def translate(locale, key, **opts)
data, pluralize = DATA[locale]
translation = data[key] || (opts[:count] && data[key += ".#{pluralize.call(opts[:count])}"]) \
or return %([translation missing: "#{key}"])
From c019cc1f62af968c086ac338bdb8acd870acacb0 Mon Sep 17 00:00:00 2001
From: Domizio Demichelis
Date: Mon, 15 Jul 2024 22:06:12 +0700
Subject: [PATCH 08/20] Add keyset pagination base files
- Pagy::Keyset API
- ActiveRecord and Sequel adapters
---
gem/lib/pagy/extras/keyset.rb | 26 ++++++++
gem/lib/pagy/keyset.rb | 98 ++++++++++++++++++++++++++++
gem/lib/pagy/keyset/active_record.rb | 38 +++++++++++
gem/lib/pagy/keyset/sequel.rb | 51 +++++++++++++++
4 files changed, 213 insertions(+)
create mode 100644 gem/lib/pagy/extras/keyset.rb
create mode 100644 gem/lib/pagy/keyset.rb
create mode 100644 gem/lib/pagy/keyset/active_record.rb
create mode 100644 gem/lib/pagy/keyset/sequel.rb
diff --git a/gem/lib/pagy/extras/keyset.rb b/gem/lib/pagy/extras/keyset.rb
new file mode 100644
index 000000000..e9af1dfe9
--- /dev/null
+++ b/gem/lib/pagy/extras/keyset.rb
@@ -0,0 +1,26 @@
+# See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/keyset
+# frozen_string_literal: true
+
+require_relative '../keyset'
+
+class Pagy # :nodoc:
+ # Add keyset pagination
+ module KeysetExtra
+ private
+
+ # Return Pagy::Keyset object and paginated records
+ def pagy_keyset(set, **vars)
+ pagy = Keyset.new(set, **pagy_keyset_get_vars(vars))
+ [pagy, pagy.records]
+ end
+
+ # Sub-method called only by #pagy_keyset: here for easy customization of variables by overriding
+ def pagy_keyset_get_vars(vars)
+ vars.tap do |v|
+ v[:page] ||= pagy_get_page(v)
+ v[:items] ||= pagy_get_items(v)
+ end
+ end
+ end
+ Backend.prepend KeysetExtra
+end
diff --git a/gem/lib/pagy/keyset.rb b/gem/lib/pagy/keyset.rb
new file mode 100644
index 000000000..d1b048a5f
--- /dev/null
+++ b/gem/lib/pagy/keyset.rb
@@ -0,0 +1,98 @@
+# See Pagy API documentation: https://ddnexus.github.io/pagy/docs/api/keyset
+# frozen_string_literal: true
+
+require 'json'
+require_relative 'b64'
+
+class Pagy
+ # Implement wicked-fast keyset pagination for big data
+ class Keyset
+ include SharedMethods
+
+ # Pick the right adapter for the set
+ def self.new(set, **vars)
+ if self == Pagy::Keyset
+ if defined?(::ActiveRecord) && set.is_a?(::ActiveRecord::Relation)
+ ActiveRecord
+ elsif defined?(::Sequel) && set.is_a?(::Sequel::Dataset)
+ Sequel
+ else
+ raise TypeError, "expected set to be an instance of ActiveRecord::Relation or Sequel::Dataset; got #{set.class}"
+ end.new(set, **vars)
+ else
+ allocate.tap { |instance| instance.send(:initialize, set, **vars) }
+ end
+ end
+
+ attr_reader :latest # Other readers from SharedMethods
+
+ def initialize(set, **vars)
+ default = DEFAULT.slice(:items, :page_param, # from pagy
+ :headers, # from headers extra
+ :jsonapi, # from jsonapi extra
+ :items_param, :max_items, :items_extra) # from items_extra
+ assign_vars({ **default, page: nil }, vars)
+ assign_items
+ @set = set
+ @page = @vars[:page]
+ @keyset = extract_keyset
+ raise InternalError, 'the set must be ordered' if @keyset.empty?
+ return unless @page
+
+ latest = JSON.parse(B64.urlsafe_decode(@page)).transform_keys(&:to_sym)
+ @latest = @vars[:typecast_latest]&.(latest) || typecast_latest(latest)
+ raise InternalError, 'page and keyset are not consistent' \
+ unless @latest.keys == @keyset.keys
+ end
+
+ # Return the next page
+ def next
+ records
+ return unless @more
+
+ @next ||= B64.urlsafe_encode(latest_from(@records.last).to_json)
+ end
+
+ # Retrieve the array of records for the current page
+ def records
+ @records ||= begin
+ @set = apply_select if select?
+ @set = @vars[:after_latest]&.(@set, @latest) || after_latest if @latest
+ records = @set.limit(@items + 1).to_a
+ @more = records.size > @items && !records.pop.nil?
+ records
+ end
+ end
+
+ protected
+
+ # Prepare the literal query to filter out the already retrieved records
+ def after_latest_query
+ operator = { asc: '>', desc: '<' }
+ directions = @keyset.values
+ if @vars[:row_comparison] && (directions.all?(:asc) || directions.all?(:desc))
+ # Row comparison: works for same directions keysets
+ # Use B-tree index for performance
+ columns = @keyset.keys
+ placeholders = columns.map { |column| ":#{column}" }.join(', ')
+ "( #{columns.join(', ')} ) #{operator[directions.first]} ( #{placeholders} )"
+ else
+ # Generic comparison: works for keysets ordered in mixed or same directions
+ keyset = @keyset.to_a
+ where = []
+ until keyset.empty?
+ last_column, last_direction = keyset.pop
+ query = +'( '
+ query << (keyset.map { |column, _d| "#{column} = :#{column}" } \
+ << "#{last_column} #{operator[last_direction]} :#{last_column}").join(' AND ')
+ query << ' )'
+ where << query
+ end
+ where.join(' OR ')
+ end
+ end
+ end
+end
+
+require_relative 'keyset/active_record'
+require_relative 'keyset/sequel'
diff --git a/gem/lib/pagy/keyset/active_record.rb b/gem/lib/pagy/keyset/active_record.rb
new file mode 100644
index 000000000..8a2024d90
--- /dev/null
+++ b/gem/lib/pagy/keyset/active_record.rb
@@ -0,0 +1,38 @@
+# See Pagy API documentation: https://ddnexus.github.io/pagy/docs/api/keyset
+# frozen_string_literal: true
+
+class Pagy
+ class Keyset
+ # Keyset adapter for ActiveRecord
+ class ActiveRecord < Keyset
+ protected
+
+ # Get the keyset attributes of the record
+ def latest_from(latest_record) = latest_record.slice(*@keyset.keys)
+
+ # Extract the keyset from the set
+ def extract_keyset
+ @set.order_values.each_with_object({}) do |node, keyset|
+ keyset[node.value.name.to_sym] = node.direction
+ end
+ end
+
+ # Filter out the already retrieved records
+ def after_latest = @set.where(after_latest_query, **@latest)
+
+ # Append the missing keyset keys if the set is restricted by select
+ def apply_select
+ @set.select(*@keyset.keys)
+ end
+
+ # Set with selected columns?
+ def select? = !@set.select_values.empty?
+
+ # Typecast the latest attributes
+ def typecast_latest(latest)
+ @set.model.new(latest).slice(latest.keys)
+ .to_hash.transform_keys(&:to_sym)
+ end
+ end
+ end
+end
diff --git a/gem/lib/pagy/keyset/sequel.rb b/gem/lib/pagy/keyset/sequel.rb
new file mode 100644
index 000000000..c3b2d4d2a
--- /dev/null
+++ b/gem/lib/pagy/keyset/sequel.rb
@@ -0,0 +1,51 @@
+# See Pagy API documentation: https://ddnexus.github.io/pagy/docs/api/keyset
+# frozen_string_literal: true
+
+class Pagy
+ class Keyset
+ # Keyset adapter for sequel
+ class Sequel < Keyset
+ protected
+
+ # Get the keyset attributes of the latest record
+ def latest_from(latest_record) = latest_record.to_hash.slice(*@keyset.keys)
+
+ # Extract the keyset from the set
+ def extract_keyset
+ return {} unless @set.opts[:order]
+
+ @set.opts[:order].each_with_object({}) do |item, keyset|
+ case item
+ when Symbol
+ keyset[item] = :asc
+ when ::Sequel::SQL::OrderedExpression
+ keyset[item.expression] = item.instance_values['descending'] ? :desc : :asc
+ else
+ raise TypeError, "#{item.class} is not a supported Sequel::SQL::OrderedExpression"
+ end
+ end
+ end
+
+ # Filter out the already retrieved records
+ def after_latest = @set.where(::Sequel.lit(after_latest_query, **@latest))
+
+ # Append the missing keyset keys if the set is restricted by select
+ def apply_select
+ selected = @set.opts[:select]
+ @set.select_append(*@keyset.keys.reject { |c| selected.include?(c) })
+ end
+
+ # Set with selected columns?
+ def select? = !@set.opts[:select].nil?
+
+ # Typecast the latest attributes
+ def typecast_latest(latest)
+ model = @set.opts[:model]
+ model.unrestrict_primary_key if (restricted_pk = model.restrict_primary_key?)
+ latest = model.new(latest).to_hash.slice(*latest.keys.map(&:to_sym))
+ model.restrict_primary_key if restricted_pk
+ latest
+ end
+ end
+ end
+end
From 131f7c36045fda780e4a7856536a767ad013cc2c Mon Sep 17 00:00:00 2001
From: Domizio Demichelis
Date: Mon, 15 Jul 2024 22:07:02 +0700
Subject: [PATCH 09/20] Add playground keyset_ar.ru and keyset_s.ru apps and
integration with the rest of the gems
---
gem/apps/keyset_ar.ru | 236 ++++++++++++++++++++
gem/apps/keyset_s.ru | 238 +++++++++++++++++++++
gem/bin/pagy | 4 +-
gem/lib/pagy/extras/elasticsearch_rails.rb | 14 +-
gem/lib/pagy/extras/jsonapi.rb | 30 ++-
gem/lib/pagy/url_helpers.rb | 2 +-
6 files changed, 505 insertions(+), 19 deletions(-)
create mode 100644 gem/apps/keyset_ar.ru
create mode 100644 gem/apps/keyset_s.ru
diff --git a/gem/apps/keyset_ar.ru b/gem/apps/keyset_ar.ru
new file mode 100644
index 000000000..4bcd7dc10
--- /dev/null
+++ b/gem/apps/keyset_ar.ru
@@ -0,0 +1,236 @@
+# frozen_string_literal: true
+
+# Starting point to reproduce keyset related pagy issues
+
+# DEV USAGE
+# pagy clone rails
+# pagy ./keyset.ru
+
+# URL
+# http://0.0.0.0:8000
+
+# HELP
+# pagy -h
+
+# DOC
+# https://ddnexus.github.io/pagy/playground/#5-keyset-app
+
+VERSION = '8.6.2'
+
+# Gemfile
+require 'bundler/inline'
+require 'bundler'
+Bundler.configure
+gemfile(ENV['PAGY_INSTALL_BUNDLE'] == 'true') do
+ source 'https://rubygems.org'
+ gem 'oj'
+ gem 'puma'
+ gem 'rails'
+ # activerecord/sqlite3_adapter.rb probably useless) constraint !!!
+ # https://github.com/rails/rails/blame/v7.1.3.4/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L14
+ gem 'sqlite3', '~> 1.4.0'
+end
+
+# require 'rails/all' # too much stuff
+require 'action_controller/railtie'
+require 'active_record'
+
+OUTPUT = Rails.env.showcase? ? IO::NULL : $stdout
+
+# Rails config
+class PagyKeyset < Rails::Application # :nodoc:
+ config.root = __dir__
+ config.session_store :cookie_store, key: 'cookie_store_key'
+ Rails.application.credentials.secret_key_base = 'absolute_secret'
+
+ config.logger = Logger.new(OUTPUT)
+ Rails.logger = config.logger
+
+ routes.draw do
+ root to: 'pets#index'
+ end
+end
+
+dir = Rails.env.development? ? '.' : Dir.pwd # app dir in dev or pwd otherwise
+unless File.writable?(dir)
+ warn "ERROR: directory #{dir.inspect} is not writable (the pagy-rails-app needs to create DB files)"
+ exit 1
+end
+
+# Pagy initializer
+require 'pagy/extras/pagy'
+require 'pagy/extras/items'
+require 'pagy/extras/keyset'
+Pagy::DEFAULT[:items] = 10
+Pagy::DEFAULT.freeze
+
+PETS = <<~PETS
+ Luna | dog | 2018-03-10
+ Coco | cat | 2019-05-15
+ Dodo | dog | 2020-06-25
+ Wiki | bird | 2018-03-12
+ Baby | rabbit | 2020-01-13
+ Neki | horse | 2021-07-20
+ Tino | donkey | 2019-06-18
+ Plot | cat | 2022-09-21
+ Riki | cat | 2018-09-14
+ Susi | horse | 2018-10-26
+ Coco | pig | 2020-08-29
+ Momo | bird | 2023-08-25
+ Lili | cat | 2021-07-22
+ Beli | pig | 2020-07-26
+ Rocky | bird | 2022-08-19
+ Vyvy | dog | 2018-05-16
+ Susi | horse | 2024-01-25
+ Ella | cat | 2020-02-20
+ Rocky | dog | 2019-09-19
+ Juni | rabbit | 2020-08-24
+ Coco | bird | 2021-03-17
+ Susi | dog | 2021-07-28
+ Luna | horse | 2023-05-14
+ Gigi | pig | 2022-05-19
+ Coco | cat | 2020-02-20
+ Nino | donkey | 2019-06-17
+ Luna | cat | 2022-02-09
+ Popi | dog | 2020-09-26
+ Lili | pig | 2022-06-18
+ Mina | horse | 2021-04-21
+ Susi | rabbit | 2023-05-18
+ Toni | donkey | 2018-06-22
+ Rocky | horse | 2019-09-28
+ Lili | cat | 2019-03-18
+ Roby | cat | 2022-06-19
+ Anto | horse | 2022-08-18
+ Susi | pig | 2021-04-21
+ Boly | bird | 2020-03-29
+ Sky | cat | 2023-07-19
+ Lili | dog | 2020-01-28
+ Fami | snake | 2023-04-27
+ Lopi | pig | 2019-06-19
+ Rocky | snake | 2022-03-13
+ Denis | dog | 2022-06-19
+ Maca | cat | 2022-06-19
+ Luna | dog | 2022-08-15
+ Jeme | horse | 2019-08-08
+ Sary | bird | 2023-04-29
+ Rocky | bird | 2023-05-14
+ Coco | dog | 2023-05-27
+PETS
+
+# Activerecord initializer
+ActiveRecord::Base.logger = Logger.new(OUTPUT)
+ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: "#{dir}/tmp/pagy-keyset-ar.sqlite3")
+ActiveRecord::Schema.define do
+ create_table :pets, force: true do |t|
+ t.string :animal
+ t.string :name
+ t.date :birthdate
+ end
+end
+
+# Models
+class Pet < ActiveRecord::Base
+end
+
+# DB seed
+pets = []
+PETS.each_line(chomp: true) do |pet|
+ name, animal, birthdate = pet.split('|').map(&:strip)
+ pets << { name:, animal:, birthdate: }
+end
+Pet.insert_all(pets)
+
+# Helpers
+module PetsHelper
+ include Pagy::Frontend
+
+ def order_symbol(dir)
+ { asc: '↗', desc: '↘' }[dir]
+ end
+end
+
+# Controllers
+class PetsController < ActionController::Base # :nodoc:
+ include Rails.application.routes.url_helpers
+ include Pagy::Backend
+
+ def index
+ Time.zone = 'UTC'
+
+ @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }
+ @pagy, @pets = pagy_keyset(Pet.order(@order))
+ render inline: TEMPLATE
+ end
+end
+
+TEMPLATE = <<~ERB
+
+
+
+
+ Pagy Keyset App
+
+
+
+
+
+
+
+
Pagy Keyset App
+
Self-contained, standalone Rails app usable to easily reproduce any keyset related pagy issue with ActiveRecord sets.
+
Please, report the following versions in any new issue.