From f28f576eef976657aa76bb136f56a2aef56bdc5d Mon Sep 17 00:00:00 2001
From: Domizio Demichelis
Date: Thu, 9 Jan 2025 17:19:01 +0700
Subject: [PATCH] WIP: Super refactoring 3
---
.simplecov | 26 +-----
gem/apps/calendar.ru | 2 -
gem/apps/demo.ru | 10 +-
gem/apps/keyset.ru | 4 +-
gem/apps/keyset_sequel.ru | 6 +-
gem/apps/rails.ru | 10 +-
gem/apps/repro.ru | 6 +-
gem/lib/pagy.rb | 8 +-
gem/lib/pagy/backend.rb | 2 +-
gem/lib/pagy/console.rb | 2 +-
gem/lib/pagy/extras/i18n.rb | 9 --
gem/lib/pagy/extras/limit.rb | 32 +------
gem/lib/pagy/extras/standalone.rb | 15 ---
gem/lib/pagy/frontend.rb | 26 +-----
gem/lib/pagy/keyset.rb | 2 +
gem/lib/pagy/links_helper.rb | 9 +-
gem/lib/pagy/{autoload.rb => loader.rb} | 16 ++--
gem/lib/pagy/mixins/headers.rb | 6 +-
gem/lib/pagy/mixins/limit_selector.rb | 35 +++++++
gem/lib/pagy/mixins/pagy.rb | 57 +++++-------
gem/lib/pagy/mixins/pagy_helpers.rb | 41 +++++++++
gem/lib/pagy/offset/calendar/unit.rb | 3 +-
gem/lib/pagy/offset/countless.rb | 2 +
gem/lib/pagy/ui_support.rb | 3 -
gem/lib/pagy/url_helpers.rb | 6 +-
test/files/db/test.sqlite3-wal | 0
test/pagy/autoload_test.rb | 7 +-
test/pagy/extras/standalone_limit_test.rb | 25 -----
test/pagy/extras/standalone_test.rb | 89 ------------------
.../augmented_test.rb} | 6 +-
test/pagy/{extras => mixins}/arel_test.rb | 2 +-
test/pagy/{extras => mixins}/array_test.rb | 2 +-
.../pagy/{extras => mixins}/bootstrap_test.rb | 2 +-
.../{extras => mixins}/bootstrap_test.rb.yaml | 10 +-
test/pagy/{extras => mixins}/bulma_test.rb | 2 +-
.../{extras => mixins}/bulma_test.rb.yaml | 10 +-
.../calendar_test.rb} | 2 +-
.../calendar_test.rb.yaml} | 34 +++----
.../countless_test.rb} | 2 +-
.../elasticsearch_rails_test.rb | 2 +-
.../elasticsearch_rails_test.rb.yaml | 16 ++--
test/pagy/{extras => mixins}/headers_test.rb | 2 +-
.../{extras => mixins}/headers_test.rb.yaml | 92 +++++++++----------
test/pagy/{extras => mixins}/jsonapi_test.rb | 6 +-
.../{extras => mixins}/jsonapi_test.rb.yaml | 33 +++----
.../keyset_augmented_test.rb | 0
test/pagy/{extras => mixins}/keyset_test.rb | 2 +-
.../{extras => mixins}/meilisearch_test.rb | 2 +-
.../meilisearch_test.rb.yaml | 6 +-
test/pagy/{extras => mixins}/metadata_test.rb | 2 +-
.../{extras => mixins}/metadata_test.rb.yaml | 12 +--
test/pagy/{extras => mixins}/pagy_test.rb | 2 +-
.../pagy/{extras => mixins}/pagy_test.rb.yaml | 58 ++++++------
.../{extras => mixins}/searchkick_test.rb | 2 +-
.../searchkick_test.rb.yaml | 8 +-
test/pagy/{ => offset}/calendar_dst_test.rb | 2 +-
test/pagy/{ => offset}/calendar_test.rb | 2 +-
test/pagy/{ => offset}/calendar_test.rb.yaml | 0
test/pagy/{ => offset}/countless_test.rb | 2 +-
test/pagy/url_helpers_test.rb | 20 +++-
60 files changed, 335 insertions(+), 467 deletions(-)
delete mode 100644 gem/lib/pagy/extras/standalone.rb
rename gem/lib/pagy/{autoload.rb => loader.rb} (77%)
create mode 100644 gem/lib/pagy/mixins/limit_selector.rb
create mode 100644 gem/lib/pagy/mixins/pagy_helpers.rb
delete mode 100644 test/files/db/test.sqlite3-wal
delete mode 100644 test/pagy/extras/standalone_limit_test.rb
delete mode 100644 test/pagy/extras/standalone_test.rb
rename test/pagy/{keyset_for_ui_test.rb => keyset/augmented_test.rb} (98%)
rename test/pagy/{extras => mixins}/arel_test.rb (98%)
rename test/pagy/{extras => mixins}/array_test.rb (97%)
rename test/pagy/{extras => mixins}/bootstrap_test.rb (95%)
rename test/pagy/{extras => mixins}/bootstrap_test.rb.yaml (98%)
rename test/pagy/{extras => mixins}/bulma_test.rb (95%)
rename test/pagy/{extras => mixins}/bulma_test.rb.yaml (98%)
rename test/pagy/{extras/calendar_extra_test.rb => mixins/calendar_test.rb} (99%)
rename test/pagy/{extras/calendar_extra_test.rb.yaml => mixins/calendar_test.rb.yaml} (96%)
rename test/pagy/{extras/countless_extra_test.rb => mixins/countless_test.rb} (98%)
rename test/pagy/{extras => mixins}/elasticsearch_rails_test.rb (99%)
rename test/pagy/{extras => mixins}/elasticsearch_rails_test.rb.yaml (78%)
rename test/pagy/{extras => mixins}/headers_test.rb (99%)
rename test/pagy/{extras => mixins}/headers_test.rb.yaml (75%)
rename test/pagy/{extras => mixins}/jsonapi_test.rb (96%)
rename test/pagy/{extras => mixins}/jsonapi_test.rb.yaml (55%)
rename test/pagy/{extras => mixins}/keyset_augmented_test.rb (100%)
rename test/pagy/{extras => mixins}/keyset_test.rb (98%)
rename test/pagy/{extras => mixins}/meilisearch_test.rb (98%)
rename test/pagy/{extras => mixins}/meilisearch_test.rb.yaml (72%)
rename test/pagy/{extras => mixins}/metadata_test.rb (98%)
rename test/pagy/{extras => mixins}/metadata_test.rb.yaml (87%)
rename test/pagy/{extras => mixins}/pagy_test.rb (98%)
rename test/pagy/{extras => mixins}/pagy_test.rb.yaml (91%)
rename test/pagy/{extras => mixins}/searchkick_test.rb (99%)
rename test/pagy/{extras => mixins}/searchkick_test.rb.yaml (77%)
rename test/pagy/{ => offset}/calendar_dst_test.rb (99%)
rename test/pagy/{ => offset}/calendar_test.rb (99%)
rename test/pagy/{ => offset}/calendar_test.rb.yaml (100%)
rename test/pagy/{ => offset}/countless_test.rb (98%)
diff --git a/.simplecov b/.simplecov
index a37d7ede5..3243d77b7 100644
--- a/.simplecov
+++ b/.simplecov
@@ -11,26 +11,8 @@ SimpleCov.start do
merge_timeout 120
enable_coverage :branch
- add_group 'All Extras', %w[gem/lib/pagy/extras]
- add_group 'Core', %w[gem/lib/pagy.rb
- gem/lib/pagy/b64.rb
- gem/lib/pagy/backend.rb
- gem/lib/pagy/console.rb
- gem/lib/pagy/countless.rb
- gem/lib/pagy/init_vars.rb
- gem/lib/pagy/exceptions.rb
- gem/lib/pagy/frontend.rb
- gem/lib/pagy/i18n.rb
- gem/lib/pagy/url_helpers.rb]
- add_group 'Countless', %w[gem/lib/pagy/countless.rb
- gem/lib/pagy/extras/countless.rb]
- add_group 'Calendar', %w[gem/lib/pagy/calendar
- gem/lib/pagy/extras/calendar.rb]
- add_group 'Keyset', %w[gem/lib/pagy/keyset.rb
- gem/lib/pagy/keyset_for_ui.rb
- gem/lib/pagy/keyset/active_record.rb
- gem/lib/pagy/keyset/sequel.rb
- gem/lib/pagy/extras/keyset.rb
- gem/lib/pagy/extras/keyset_for_ui.rb]
- add_group 'Tests', %w[test]
+ add_group 'Core', %w[gem/lib/pagy.rb gem/lib/pagy]
+ add_group 'Extras', 'gem/lib/pagy/extras'
+ add_group 'Mixins', 'gem/lib/pagy/mixins'
+ add_group 'Tests', 'test'
end
diff --git a/gem/apps/calendar.ru b/gem/apps/calendar.ru
index 7c6eb1ea5..92297a4c0 100644
--- a/gem/apps/calendar.ru
+++ b/gem/apps/calendar.ru
@@ -33,8 +33,6 @@ gemfile(ENV['PAGY_INSTALL_BUNDLE'] == 'true') do
end
# Pagy initializer
-require 'pagy/extras/calendar'
-require 'pagy/extras/bootstrap'
Pagy::DEFAULT.freeze
# Sinatra setup
diff --git a/gem/apps/demo.ru b/gem/apps/demo.ru
index c22bad8ac..e389858b5 100644
--- a/gem/apps/demo.ru
+++ b/gem/apps/demo.ru
@@ -39,9 +39,9 @@ STYLES = { pagy: { extra: 'pagy', prefix: '', css_anchor: 'pagy-scss' },
bulma: {},
tailwind: { extra: 'pagy', prefix: '', css_anchor: 'pagy-tailwind-css' } }.freeze
-STYLES.each_key do |style|
- require "pagy/extras/#{STYLES[style][:extra] || style}"
-end
+# STYLES.each_key do |style|
+# require "pagy/extras/#{STYLES[style][:extra] || style}"
+# end
require 'pagy/extras/limit'
# Sinatra setup
@@ -57,7 +57,7 @@ class PagyDemo < Sinatra::Base
get '/template' do
collection = MockCollection.new
- @pagy, @records = pagy(collection)
+ @pagy, @records = pagy_offset(collection)
erb :template, locals: { pagy: @pagy, style: 'pagy' }
end
@@ -83,7 +83,7 @@ class PagyDemo < Sinatra::Base
get("/#{style}") do
collection = MockCollection.new
- @pagy, @records = pagy(collection)
+ @pagy, @records = pagy_offset(collection)
erb :helpers, locals: { style:, prefix: }
end
diff --git a/gem/apps/keyset.ru b/gem/apps/keyset.ru
index 985b535a0..49c2c7231 100644
--- a/gem/apps/keyset.ru
+++ b/gem/apps/keyset.ru
@@ -32,15 +32,13 @@ end
# Pagy initializer
require 'pagy/extras/limit'
-require 'pagy/extras/keyset'
-require 'pagy/extras/pagy'
Pagy::DEFAULT[:limit] = 10
Pagy::DEFAULT.freeze
# Sinatra setup
require 'sinatra/base'
# Sinatra application
-class PagyKeysetAugmented < Sinatra::Base
+class PagyKeyset < Sinatra::Base
include Pagy::Backend
# Root route/action
get '/' do
diff --git a/gem/apps/keyset_sequel.ru b/gem/apps/keyset_sequel.ru
index 60d8701da..1f06c29aa 100644
--- a/gem/apps/keyset_sequel.ru
+++ b/gem/apps/keyset_sequel.ru
@@ -32,8 +32,6 @@ end
# Pagy initializer
require 'pagy/extras/limit'
-require 'pagy/extras/keyset'
-require 'pagy/extras/pagy'
Pagy::DEFAULT[:limit] = 10
Pagy::DEFAULT.freeze
@@ -41,7 +39,7 @@ Pagy::DEFAULT.freeze
require 'sinatra/base'
require 'logger'
# Sinatra application
-class PagyKeysetAugmented < Sinatra::Base
+class PagyKeysetSequel < Sinatra::Base
include Pagy::Backend
# Root route/action
get '/' do
@@ -216,4 +214,4 @@ data.each_line(chomp: true) do |pet|
dataset.insert(name:, animal:, birthdate:)
end
-run PagyKeysetAugmented
+run PagyKeysetSequel
diff --git a/gem/apps/rails.ru b/gem/apps/rails.ru
index 61ed4c83b..bfce8c260 100644
--- a/gem/apps/rails.ru
+++ b/gem/apps/rails.ru
@@ -59,13 +59,9 @@ unless File.writable?(dir)
end
# Pagy initializer
-require 'pagy/extras/pagy'
require 'pagy/extras/limit'
-require 'pagy/extras/overflow'
-require 'pagy/extras/headers'
-Pagy::DEFAULT[:limit] = 10
-Pagy::DEFAULT[:overflow] = :empty_page
-Pagy::DEFAULT.freeze
+Pagy::DEFAULT[:limit] = 10
+Pagy::Offset::DEFAULT[:overflow] = :empty_page
# Activerecord initializer
ActiveRecord::Base.logger = Logger.new(OUTPUT)
@@ -113,7 +109,7 @@ class CommentsController < ActionController::Base # :nodoc:
include Pagy::Backend
def index
- @pagy, @comments = pagy(Comment.all)
+ @pagy, @comments = pagy_offset(Comment.all)
pagy_headers_merge(@pagy)
render inline: TEMPLATE
end
diff --git a/gem/apps/repro.ru b/gem/apps/repro.ru
index 201241afd..b37d06546 100644
--- a/gem/apps/repro.ru
+++ b/gem/apps/repro.ru
@@ -31,10 +31,8 @@ end
# Edit this section adding/removing the extras and Pagy::DEFAULT as needed
# pagy initializer
-require 'pagy/extras/pagy'
require 'pagy/extras/limit'
-require 'pagy/extras/overflow'
-Pagy::DEFAULT[:overflow] = :empty_page
+Pagy::Offset::DEFAULT[:overflow] = :empty_page
Pagy::DEFAULT.freeze
# Sinatra setup
@@ -56,7 +54,7 @@ class PagyRepro < Sinatra::Base
# Edit this action as needed
get '/' do
collection = MockCollection.new
- @pagy, @records = pagy(collection)
+ @pagy, @records = pagy_offset(collection)
erb :main
end
diff --git a/gem/lib/pagy.rb b/gem/lib/pagy.rb
index 086044ad9..12ddfaa7e 100644
--- a/gem/lib/pagy.rb
+++ b/gem/lib/pagy.rb
@@ -2,7 +2,7 @@
# frozen_string_literal: true
require 'pathname'
-require_relative 'pagy/autoload'
+require_relative 'pagy/loader'
# Top superclass: it should define only what's common to all the subclasses
class Pagy
@@ -15,6 +15,7 @@ class Pagy
autoload :ElasticsearchRails, 'pagy/mixins/elasticsearch_rails'
autoload :Meilisearch, 'pagy/mixins/meilisearch'
autoload :Searchkick, 'pagy/mixins/searchkick'
+ autoload :Console, 'pagy/console'
VERSION = '9.3.3'
PAGE_TOKEN = 'P '
@@ -28,7 +29,10 @@ def self.root
@root ||= Pathname.new(__dir__).parent.freeze
end
- attr_reader :page, :limit, :vars
+ attr_reader :page, :prev, :next, :in, :limit, :vars, :last
+
+ alias pages last
+ def self.predict_last? = true
# Validates and assign the passed vars: var must be present and value.to_i must be >= to min
def assign_and_check(name_min)
diff --git a/gem/lib/pagy/backend.rb b/gem/lib/pagy/backend.rb
index 00741bbd7..0fb828de9 100644
--- a/gem/lib/pagy/backend.rb
+++ b/gem/lib/pagy/backend.rb
@@ -8,7 +8,7 @@ class Pagy
# or any collection by overriding any of the `pagy_*` methods in your controller.
# See also the extras if you need specialized methods to paginate Arrays or other collections
module Backend
- include Autoload
+ include Loader
private
diff --git a/gem/lib/pagy/console.rb b/gem/lib/pagy/console.rb
index d4fa05f09..6ff5d378a 100644
--- a/gem/lib/pagy/console.rb
+++ b/gem/lib/pagy/console.rb
@@ -2,7 +2,6 @@
# frozen_string_literal: true
require_relative '../pagy' # so you can require just the extra in the console
-require_relative 'extras/standalone'
class Pagy
# Provide a ready to use pagy environment when included in irb/rails console
@@ -11,6 +10,7 @@ module Console
def self.included(main)
main.include(Backend)
main.include(Frontend)
+ main.define_method(:params) { {} }
DEFAULT[:url] = 'http://www.example.com/subdir'
end
diff --git a/gem/lib/pagy/extras/i18n.rb b/gem/lib/pagy/extras/i18n.rb
index 53c84fa95..7ba5a2091 100644
--- a/gem/lib/pagy/extras/i18n.rb
+++ b/gem/lib/pagy/extras/i18n.rb
@@ -11,16 +11,7 @@ def pagy_t(key, **)
end
end
Frontend.prepend I18nExtra::FrontendOverride
-
- # Calendar overriding for localization (see also the block in the calendar section of the config/pagy.rb initializer)
- module CalendarOverride
- def localize(time, opts)
- ::I18n.l(time, **opts)
- end
- end
end
- Offset::Calendar::Unit.prepend I18nExtra::CalendarOverride if defined?(::Pagy::Offset::Calendar::Unit)
-
# Add the pagy locales to the I18n.load_path
::I18n.load_path += Dir[Pagy.root.join('locales', '*.yml')]
end
diff --git a/gem/lib/pagy/extras/limit.rb b/gem/lib/pagy/extras/limit.rb
index e9dc3e673..bc1e9ccd1 100644
--- a/gem/lib/pagy/extras/limit.rb
+++ b/gem/lib/pagy/extras/limit.rb
@@ -6,7 +6,8 @@ class Pagy
DEFAULT[:limit_max] = 100
DEFAULT[:limit_extra] = true # extra enabled by default
- # Allow the client to request a custom limit per page with an optional selector UI
+ # Allow the client to request a custom limit per page
+ # with an optional selector UI
module LimitExtra
# Additions for the Backend module
module BackendAddOn
@@ -27,34 +28,5 @@ def pagy_get_limit_param(vars)
end
end
Backend.prepend LimitExtra::BackendAddOn
-
- # Additions for the Frontend module
- module FrontendAddOn
- # Return the limit selector HTML. For example "Show [20] items per page"
- def pagy_limit_selector_js(pagy, id: nil, item_name: nil)
- return '' unless pagy.vars[:limit_extra]
-
- id = %( id="#{id}") if id
- vars = pagy.vars
- limit = vars[:limit]
- vars[:limit] = LIMIT_TOKEN # limit token replaced in the javascript
- url_token = pagy_page_url(pagy, PAGE_TOKEN)
- vars[:limit] = limit # restore the limit
-
- limit_input = %(#{
- A_TAG})
-
- %()
- end
- end
- Frontend.prepend LimitExtra::FrontendAddOn
end
end
diff --git a/gem/lib/pagy/extras/standalone.rb b/gem/lib/pagy/extras/standalone.rb
deleted file mode 100644
index 937d2e212..000000000
--- a/gem/lib/pagy/extras/standalone.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/standalone
-# frozen_string_literal: true
-
-require_relative '../url_helpers'
-
-class Pagy
- # Use pagy without any request object, nor Rack environment/gem, nor any defined params method,
- # even in the irb/rails console without any app or config.
- # Define a dummy params method if it's not already defined in the including module
- module Backend
- def self.included(controller)
- controller.define_method(:params) { {} } unless controller.method_defined?(:params)
- end
- end
-end
diff --git a/gem/lib/pagy/frontend.rb b/gem/lib/pagy/frontend.rb
index 4fef012a0..21d12bbfe 100644
--- a/gem/lib/pagy/frontend.rb
+++ b/gem/lib/pagy/frontend.rb
@@ -9,7 +9,7 @@ class Pagy
# Frontend modules are specially optimized for performance.
# The resulting code may not look very elegant, but produces the best benchmarks
module Frontend
- include Autoload
+ include Loader
include UrlHelpers
# Return a performance optimized lambda to generate the HTML anchor element (a tag)
@@ -43,30 +43,6 @@ def pagy_info(pagy, id: nil, item_name: nil)
})
end
- # Generic pagination: it returns the html with the series of links to the pages
- def pagy_nav(pagy, id: nil, aria_label: nil, **vars)
- id = %( id="#{id}") if id
- a = pagy_anchor(pagy, **vars)
- data = %( #{pagy_data(pagy, :n)}) if defined?(::Pagy::Keyset::Augmented) && pagy.is_a?(Keyset::Augmented)
-
- html = %()
- 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, **)
diff --git a/gem/lib/pagy/keyset.rb b/gem/lib/pagy/keyset.rb
index bbef6c15f..aff933120 100644
--- a/gem/lib/pagy/keyset.rb
+++ b/gem/lib/pagy/keyset.rb
@@ -39,6 +39,8 @@ def initialize(set, **vars) # rubocop:disable Lint/MissingSuper
assign_filter_args
end
+ def self.predict_last? = false
+
# Assign the filter_args
def assign_filter_args
return unless @prev_cutoff
diff --git a/gem/lib/pagy/links_helper.rb b/gem/lib/pagy/links_helper.rb
index a56ba7bbf..80a071115 100644
--- a/gem/lib/pagy/links_helper.rb
+++ b/gem/lib/pagy/links_helper.rb
@@ -5,13 +5,10 @@ module LinksHelper
def pagy_links(pagy, **)
url_str = pagy_page_url(pagy, PAGE_TOKEN, **)
{ first: url_str.sub(PAGE_TOKEN, '1') }.tap do |links|
- links[:next] = url_str.sub(PAGE_TOKEN, pagy.next.to_s) if pagy.next
- next if defined?(::Pagy::Keyset) && pagy.is_a?(Keyset)
-
links[:prev] = url_str.sub(PAGE_TOKEN, pagy.prev.to_s) if pagy.prev
- links[:last] = url_str.sub(PAGE_TOKEN, pagy.last.to_s) if pagy.last \
- && !(defined?(::Pagy::Offset::Countless) && pagy.is_a?(Offset::Countless))
- end.slice(:first, :prev, :next, :last)
+ links[:next] = url_str.sub(PAGE_TOKEN, pagy.next.to_s) if pagy.next
+ links[:last] = url_str.sub(PAGE_TOKEN, pagy.last.to_s) if pagy.class.predict_last?
+ end
end
end
end
diff --git a/gem/lib/pagy/autoload.rb b/gem/lib/pagy/loader.rb
similarity index 77%
rename from gem/lib/pagy/autoload.rb
rename to gem/lib/pagy/loader.rb
index fdcf87e7f..a4790b333 100644
--- a/gem/lib/pagy/autoload.rb
+++ b/gem/lib/pagy/loader.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Pagy
- module Autoload
+ module Loader
METHOD_MIXINS = { pagy_arel: :arel,
pagy_array: :array,
pagy_bootstrap_nav: :bootstrap,
@@ -18,18 +18,20 @@ module Autoload
pagy_headers_merge: :headers,
pagy_keyset: :keyset,
pagy_keyset_augmented_js: :keyset_augmented,
+ pagy_limit_selector_js: :limit_selector,
new_from_meilisearch: :meilisearch,
pagy_meilisearch: :meilisearch,
pagy_metadata: :metadata,
pagy_offset: :offset,
+ pagy_nav: :pagy,
pagy_nav_js: :pagy,
pagy_combo_nav_js: :pagy,
- pagy_prev_url: :pagy,
- pagy_next_url: :pagy,
- pagy_prev_a: :pagy,
- pagy_next_a: :pagy,
- pagy_prev_link: :pagy,
- pagy_next_link: :pagy,
+ pagy_prev_url: :pagy_helpers,
+ pagy_next_url: :pagy_helpers,
+ pagy_prev_a: :pagy_helpers,
+ pagy_next_a: :pagy_helpers,
+ pagy_prev_link: :pagy_helpers,
+ pagy_next_link: :pagy_helpers,
new_from_searchkick: :searchkick,
pagy_searchkick: :searchkick }.freeze
diff --git a/gem/lib/pagy/mixins/headers.rb b/gem/lib/pagy/mixins/headers.rb
index 0898ebd68..a1f721d6b 100644
--- a/gem/lib/pagy/mixins/headers.rb
+++ b/gem/lib/pagy/mixins/headers.rb
@@ -26,7 +26,7 @@ def pagy_headers(pagy, **)
# If it's not in the vars, the autoloading kicked-in after the object creation,
# which means that no custom DEFAULT has been set, so we use the original
headers = pagy.vars[:headers] || DEFAULT[:headers]
- pagy_link_header(pagy, **).tap do |hash|
+ { 'link' => pagy_link_header(pagy, **) }.tap do |hash|
hash[headers[:page]] = pagy.page.to_s if pagy.page && headers[:page]
hash[headers[:limit]] = pagy.limit.to_s \
if headers[:limit] && !(defined?(::Pagy::Offset::Calendar) && pagy.is_a?(Offset::Calendar::Unit))
@@ -38,8 +38,8 @@ def pagy_headers(pagy, **)
end
end
- def pagy_link_header(pagy, absolute: true, **vars)
- pagy_links(pagy, absolute:, **vars).map { |key, link| %(<#{link}>; rel="#{key}") }.join(', ')
+ def pagy_link_header(pagy, **)
+ pagy_links(pagy, **, absolute: true).map { |key, link| %(<#{link}>; rel="#{key}") }.join(', ')
end
end
Backend.prepend HeadersMixin
diff --git a/gem/lib/pagy/mixins/limit_selector.rb b/gem/lib/pagy/mixins/limit_selector.rb
new file mode 100644
index 000000000..8fa3620a5
--- /dev/null
+++ b/gem/lib/pagy/mixins/limit_selector.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require_relative '../extras/limit'
+
+class Pagy
+ module LimitSelectorMixin
+ # Additions for the Frontend module
+ module FrontendAddOn
+ # Return the limit selector HTML. For example "Show [20] items per page"
+ def pagy_limit_selector_js(pagy, id: nil, item_name: nil)
+ return '' unless pagy.vars[:limit_extra]
+
+ id = %( id="#{id}") if id
+ vars = pagy.vars
+ limit = vars[:limit]
+ vars[:limit] = LIMIT_TOKEN # limit token replaced in the javascript
+ url_token = pagy_page_url(pagy, PAGE_TOKEN)
+ vars[:limit] = limit # restore the limit
+
+ limit_input = %(#{A_TAG})
+
+ %()
+ end
+ end
+ Frontend.prepend FrontendAddOn
+ end
+end
diff --git a/gem/lib/pagy/mixins/pagy.rb b/gem/lib/pagy/mixins/pagy.rb
index 95c3f58bc..9a7f480f3 100644
--- a/gem/lib/pagy/mixins/pagy.rb
+++ b/gem/lib/pagy/mixins/pagy.rb
@@ -5,7 +5,30 @@ class Pagy
# Frontend modules are specially optimized for performance.
# The resulting code may not look very elegant, but produces the best benchmarks
module PagyMixin
- # pagy_nav is defined in the Frontend itself
+ # Generic pagination: it returns the html with the series of links to the pages
+ def pagy_nav(pagy, id: nil, aria_label: nil, **vars)
+ id = %( id="#{id}") if id
+ a = pagy_anchor(pagy, **vars)
+ data = %( #{pagy_data(pagy, :n)}) if defined?(::Pagy::Keyset::Augmented) && pagy.is_a?(Keyset::Augmented)
+
+ html = %()
+ end
+
# Javascript pagination: it returns a nav with a data-pagy attribute used by the pagy.js file
def pagy_nav_js(pagy, id: nil, aria_label: nil, **vars)
sequels = pagy.sequels(**vars)
@@ -41,38 +64,6 @@ def pagy_combo_nav_js(pagy, id: nil, aria_label: nil, **vars)
next_a(pagy, a)
})
end
-
- # Return the previous page URL string or nil
- def pagy_prev_url(pagy, **vars)
- pagy_page_url(pagy, pagy.prev, **vars) if pagy.prev
- end
-
- # Return the next page URL string or nil
- def pagy_next_url(pagy, **vars)
- pagy_page_url(pagy, pagy.next, **vars) if pagy.next
- end
-
- # Return the enabled/disabled previous page anchor tag
- def pagy_prev_a(pagy, text: pagy_t('pagy.prev'), aria_label: pagy_t('pagy.aria_label.prev'), **vars)
- a = pagy_anchor(pagy, **vars)
- prev_a(pagy, a, text:, aria_label:)
- end
-
- # Return the enabled/disabled next page anchor tag
- def pagy_next_a(pagy, text: pagy_t('pagy.next'), aria_label: pagy_t('pagy.aria_label.next'), **vars)
- a = pagy_anchor(pagy, **vars)
- next_a(pagy, a, text:, aria_label:)
- end
-
- # Conditionally return the previous page link tag
- def pagy_prev_link(pagy, **vars)
- %() if pagy.prev
- end
-
- # Conditionally return the next page link tag
- def pagy_next_link(pagy, **vars)
- %() if pagy.next
- end
end
Frontend.prepend PagyMixin
end
diff --git a/gem/lib/pagy/mixins/pagy_helpers.rb b/gem/lib/pagy/mixins/pagy_helpers.rb
new file mode 100644
index 000000000..cfe09d406
--- /dev/null
+++ b/gem/lib/pagy/mixins/pagy_helpers.rb
@@ -0,0 +1,41 @@
+# See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/pagy
+# frozen_string_literal: true
+
+class Pagy
+ # Frontend modules are specially optimized for performance.
+ # The resulting code may not look very elegant, but produces the best benchmarks
+ module PagyHelpersMixin
+ # Return the previous page URL string or nil
+ def pagy_prev_url(pagy, **vars)
+ pagy_page_url(pagy, pagy.prev, **vars) if pagy.prev
+ end
+
+ # Return the next page URL string or nil
+ def pagy_next_url(pagy, **vars)
+ pagy_page_url(pagy, pagy.next, **vars) if pagy.next
+ end
+
+ # Return the enabled/disabled previous page anchor tag
+ def pagy_prev_a(pagy, text: pagy_t('pagy.prev'), aria_label: pagy_t('pagy.aria_label.prev'), **vars)
+ a = pagy_anchor(pagy, **vars)
+ prev_a(pagy, a, text:, aria_label:)
+ end
+
+ # Return the enabled/disabled next page anchor tag
+ def pagy_next_a(pagy, text: pagy_t('pagy.next'), aria_label: pagy_t('pagy.aria_label.next'), **vars)
+ a = pagy_anchor(pagy, **vars)
+ next_a(pagy, a, text:, aria_label:)
+ end
+
+ # Conditionally return the previous page link tag
+ def pagy_prev_link(pagy, **vars)
+ %() if pagy.prev
+ end
+
+ # Conditionally return the next page link tag
+ def pagy_next_link(pagy, **vars)
+ %() if pagy.next
+ end
+ end
+ Frontend.prepend PagyHelpersMixin
+end
diff --git a/gem/lib/pagy/offset/calendar/unit.rb b/gem/lib/pagy/offset/calendar/unit.rb
index b4496eb58..2b726cf1b 100644
--- a/gem/lib/pagy/offset/calendar/unit.rb
+++ b/gem/lib/pagy/offset/calendar/unit.rb
@@ -85,8 +85,9 @@ def assign_unit_vars
end
# Apply the strftime format to the time (overridden by the i18n extra when localization is required)
+ # Calendar overriding for localization (see also the block in the calendar section of the config/pagy.rb initializer)
def localize(time, opts)
- time.strftime(opts[:format])
+ defined?(::Pagy::I18nExtra) ? ::I18n.l(time, **opts) : time.strftime(opts[:format])
end
# Number of time units to offset from the @initial time, in order to get the ordered starting time for the page.
diff --git a/gem/lib/pagy/offset/countless.rb b/gem/lib/pagy/offset/countless.rb
index 1313bb96c..494de1e67 100644
--- a/gem/lib/pagy/offset/countless.rb
+++ b/gem/lib/pagy/offset/countless.rb
@@ -15,6 +15,8 @@ def initialize(**vars) # rubocop:disable Lint/MissingSuper
assign_offset
end
+ def self.predict_last? = false
+
# Finalize the instance variables based on the fetched size
def finalize(fetched_size)
raise OverflowError.new(self, :page, "to be < #{@page}", @page) if fetched_size.zero? && @page > 1
diff --git a/gem/lib/pagy/ui_support.rb b/gem/lib/pagy/ui_support.rb
index 91e752731..d88433a66 100644
--- a/gem/lib/pagy/ui_support.rb
+++ b/gem/lib/pagy/ui_support.rb
@@ -2,9 +2,6 @@
class Pagy
module UISupport
- attr_reader :in, :last, :prev
- alias pages last
-
# Label for the current page. Allow the customization of the output (overridden by the calendar extra)
def label = @page.to_s
diff --git a/gem/lib/pagy/url_helpers.rb b/gem/lib/pagy/url_helpers.rb
index ff230ad22..884d4a54e 100644
--- a/gem/lib/pagy/url_helpers.rb
+++ b/gem/lib/pagy/url_helpers.rb
@@ -16,7 +16,11 @@ def build_nested_query(value, prefix = nil, unescaped = [])
value.map { |v| build_nested_query(v, "#{prefix}[]", unescaped) }.join('&')
when Hash
value.map do |k, v|
- build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k), unescaped)
+ new_k = prefix ? "#{prefix}[#{escape(k)}]" : escape(k)
+ if unescaped.size.positive? && new_k != k.to_s && unescaped.include?(k.to_s)
+ unescaped[unescaped.find_index(k.to_s)] = new_k
+ end
+ build_nested_query(v, new_k, unescaped)
end.delete_if(&:empty?).join('&')
when nil
escape(prefix)
diff --git a/test/files/db/test.sqlite3-wal b/test/files/db/test.sqlite3-wal
deleted file mode 100644
index e69de29bb..000000000
diff --git a/test/pagy/autoload_test.rb b/test/pagy/autoload_test.rb
index 7bda513b4..e785e25e3 100644
--- a/test/pagy/autoload_test.rb
+++ b/test/pagy/autoload_test.rb
@@ -12,8 +12,7 @@ def app(**)
describe 'Autoload thread safety' do
threads = []
shared_result = []
-
- times = 1000
+ times = 1000
times.times do
threads << Thread.new do
@@ -33,10 +32,12 @@ def app(**)
shared_result << e.class
end
end
-
threads.each(&:join)
it "autoload safely" do
_(shared_result).must_equal Array.new(times, :success)
end
+ it "uses super" do
+ _ { MockApp.new.pagy_unknown_method }.must_raise NoMethodError
+ end
end
diff --git a/test/pagy/extras/standalone_limit_test.rb b/test/pagy/extras/standalone_limit_test.rb
deleted file mode 100644
index cff2ed108..000000000
--- a/test/pagy/extras/standalone_limit_test.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../test_helper'
-require 'pagy/extras/standalone'
-require 'pagy/extras/limit'
-
-require_relative '../../mock_helpers/app'
-
-describe 'pagy/extras/standalone_limit' do
- let(:app) { MockApp.new }
-
- describe '#pagy_page_url' do
- it 'renders url with params and fragment' do
- pagy = Pagy::Offset.new(count: 1000, page: 3, params: { a: 3, b: 4 })
- _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal "/foo?page=5&a=3&b=4&limit=20#fragment"
- _(app.pagy_page_url(pagy, 5, absolute: true, fragment: '#fragment')).must_equal "http://example.com:3000/foo?page=5&a=3&b=4&limit=20#fragment"
- pagy = Pagy::Offset.new(count: 1000, page: 3, params: { a: [1, 2, 3] }, url: 'http://www.pagy-standalone.com/subdir')
- _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal "http://www.pagy-standalone.com/subdir?a%5B%5D=1&a%5B%5D=2&a%5B%5D=3&page=5&limit=20#fragment"
- _(app.pagy_page_url(pagy, 5, absolute: true, fragment: '#fragment')).must_equal "http://www.pagy-standalone.com/subdir?a%5B%5D=1&a%5B%5D=2&a%5B%5D=3&page=5&limit=20#fragment"
- pagy = Pagy::Offset.new(count: 1000, page: 3, params: { a: nil }, url: '')
- _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal "?a&page=5&limit=20#fragment"
- _(app.pagy_page_url(pagy, 5, absolute: true, fragment: '#fragment')).must_equal "?a&page=5&limit=20#fragment"
- end
- end
-end
diff --git a/test/pagy/extras/standalone_test.rb b/test/pagy/extras/standalone_test.rb
deleted file mode 100644
index bf05698ab..000000000
--- a/test/pagy/extras/standalone_test.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../test_helper'
-require 'pagy/extras/standalone'
-
-require_relative '../../mock_helpers/app'
-
-class EmptyController
- include Pagy::Backend
-end
-
-class FilledController
- def params
- { a: 'a', b: 'b' }
- end
- include Pagy::Backend
-end
-
-describe 'pagy/extras/standalone' do
- let(:app) { MockApp.new }
-
- describe 'defines #params if missing' do
- it 'defines a dummy #params' do
- _(EmptyController.new.params).must_equal({})
- end
- it 'does not define a dummy #params' do
- _(FilledController.new.params).must_equal({ a: 'a', b: 'b' })
- end
- end
-
- describe '#pagy_page_url' do
- it 'renders basic url' do
- pagy = Pagy::Offset.new count: 1000, page: 3
- _(app.pagy_page_url(pagy, 5)).must_equal '/foo?page=5'
- _(app.pagy_page_url(pagy, 5, absolute: true)).must_equal 'http://example.com:3000/foo?page=5'
- pagy = Pagy::Offset.new count: 1000, page: 3, url: 'http://www.pagy-standalone.com/subdir'
- _(app.pagy_page_url(pagy, 5)).must_equal 'http://www.pagy-standalone.com/subdir?page=5'
- _(app.pagy_page_url(pagy, 5, absolute: true)).must_equal 'http://www.pagy-standalone.com/subdir?page=5'
- pagy = Pagy::Offset.new count: 1000, page: 3, url: ''
- _(app.pagy_page_url(pagy, 5)).must_equal '?page=5'
- _(app.pagy_page_url(pagy, 5, absolute: true)).must_equal '?page=5'
- end
-
- it 'renders url with params' do
- pagy = Pagy::Offset.new count: 1000, page: 3, params: { a: 3, b: 4 }
- _(app.pagy_page_url(pagy, 5)).must_equal '/foo?page=5&a=3&b=4'
- _(app.pagy_page_url(pagy, 5, absolute: true)).must_equal 'http://example.com:3000/foo?page=5&a=3&b=4'
- pagy = Pagy::Offset.new count: 1000, page: 3, params: { a: 3, b: 4 }, url: 'http://www.pagy-standalone.com/subdir'
- _(app.pagy_page_url(pagy, 5)).must_equal "http://www.pagy-standalone.com/subdir?a=3&b=4&page=5"
- _(app.pagy_page_url(pagy, 5, absolute: true)).must_equal "http://www.pagy-standalone.com/subdir?a=3&b=4&page=5"
- pagy = Pagy::Offset.new count: 1000, page: 3, params: { a: 3, b: 4 }, url: ''
- _(app.pagy_page_url(pagy, 5)).must_equal "?a=3&b=4&page=5"
- _(app.pagy_page_url(pagy, 5, absolute: true)).must_equal "?a=3&b=4&page=5"
- end
- it 'renders url with fragment' do
- pagy = Pagy::Offset.new count: 1000, page: 3
- _(app.pagy_page_url(pagy, 6, fragment: '#fragment')).must_equal '/foo?page=6#fragment'
- _(app.pagy_page_url(pagy, 6, absolute: true, fragment: '#fragment')).must_equal 'http://example.com:3000/foo?page=6#fragment'
- pagy = Pagy::Offset.new count: 1000, page: 3, url: 'http://www.pagy-standalone.com/subdir'
- _(app.pagy_page_url(pagy, 6, fragment: '#fragment')).must_equal 'http://www.pagy-standalone.com/subdir?page=6#fragment'
- _(app.pagy_page_url(pagy, 6, absolute: true, fragment: '#fragment')).must_equal 'http://www.pagy-standalone.com/subdir?page=6#fragment'
- pagy = Pagy::Offset.new count: 1000, page: 3, url: ''
- _(app.pagy_page_url(pagy, 6, fragment: '#fragment')).must_equal '?page=6#fragment'
- _(app.pagy_page_url(pagy, 6, absolute: true, fragment: '#fragment')).must_equal '?page=6#fragment'
- end
- it 'renders url with params and fragment' do
- pagy = Pagy::Offset.new count: 1000, page: 3, params: { a: 3, b: 4 }
- _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal '/foo?page=5&a=3&b=4#fragment'
- _(app.pagy_page_url(pagy, 5, absolute: true, fragment: '#fragment')).must_equal 'http://example.com:3000/foo?page=5&a=3&b=4#fragment'
- pagy = Pagy::Offset.new count: 1000, page: 3, params: { a: [1, 2, 3] }, url: 'http://www.pagy-standalone.com/subdir'
- _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal "http://www.pagy-standalone.com/subdir?a%5B%5D=1&a%5B%5D=2&a%5B%5D=3&page=5#fragment"
- _(app.pagy_page_url(pagy, 5, absolute: true, fragment: '#fragment')).must_equal "http://www.pagy-standalone.com/subdir?a%5B%5D=1&a%5B%5D=2&a%5B%5D=3&page=5#fragment"
- pagy = Pagy::Offset.new count: 1000, page: 3, params: { a: nil }, url: ''
- _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal "?a&page=5#fragment"
- _(app.pagy_page_url(pagy, 5, absolute: true, fragment: '#fragment')).must_equal "?a&page=5#fragment"
- end
- it 'renders url with params lambda and fragment' do
- pagy = Pagy::Offset.new count: 1000, page: 3, params: ->(p) { p.merge(a: 3, b: 4) }
- _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal '/foo?page=5&a=3&b=4#fragment'
- _(app.pagy_page_url(pagy, 5, absolute: true, fragment: '#fragment')).must_equal 'http://example.com:3000/foo?page=5&a=3&b=4#fragment'
- pagy = Pagy::Offset.new count: 1000, page: 3, params: ->(p) { p.merge(a: [1, 2, 3]) }, url: 'http://www.pagy-standalone.com/subdir'
- _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal "http://www.pagy-standalone.com/subdir?page=5&a%5B%5D=1&a%5B%5D=2&a%5B%5D=3#fragment"
- _(app.pagy_page_url(pagy, 5, absolute: true, fragment: '#fragment')).must_equal "http://www.pagy-standalone.com/subdir?page=5&a%5B%5D=1&a%5B%5D=2&a%5B%5D=3#fragment"
- pagy = Pagy::Offset.new count: 1000, page: 3, params: ->(p) { p.merge(a: nil) }, url: ''
- _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal "?page=5&a#fragment"
- _(app.pagy_page_url(pagy, 5, absolute: true, fragment: '#fragment')).must_equal "?page=5&a#fragment"
- end
- end
-end
diff --git a/test/pagy/keyset_for_ui_test.rb b/test/pagy/keyset/augmented_test.rb
similarity index 98%
rename from test/pagy/keyset_for_ui_test.rb
rename to test/pagy/keyset/augmented_test.rb
index d67454812..b530cd7de 100644
--- a/test/pagy/keyset_for_ui_test.rb
+++ b/test/pagy/keyset/augmented_test.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require_relative '../test_helper'
-require_relative '../files/models'
-require_relative '../../gem/lib/pagy/b64'
+require_relative '../../test_helper'
+require_relative '../../files/models'
+require 'pagy/b64'
[Pet, PetSequel].each do |model|
describe "Pagy Keyset with #{model}" do
diff --git a/test/pagy/extras/arel_test.rb b/test/pagy/mixins/arel_test.rb
similarity index 98%
rename from test/pagy/extras/arel_test.rb
rename to test/pagy/mixins/arel_test.rb
index 2701c946b..8d99edf80 100644
--- a/test/pagy/extras/arel_test.rb
+++ b/test/pagy/mixins/arel_test.rb
@@ -6,7 +6,7 @@
require_relative '../../mock_helpers/collection'
require_relative '../../mock_helpers/app'
-describe 'pagy/extras/arel' do
+describe 'pagy/mixins/arel' do
let(:app) { MockApp.new }
describe '#pagy_arel' do
diff --git a/test/pagy/extras/array_test.rb b/test/pagy/mixins/array_test.rb
similarity index 97%
rename from test/pagy/extras/array_test.rb
rename to test/pagy/mixins/array_test.rb
index 164738463..43df953f9 100644
--- a/test/pagy/extras/array_test.rb
+++ b/test/pagy/mixins/array_test.rb
@@ -4,7 +4,7 @@
require_relative '../../mock_helpers/app'
-describe 'pagy/extras/array' do
+describe 'pagy/mixins/array' do
let(:app) { MockApp.new }
describe '#pagy_array' do
diff --git a/test/pagy/extras/bootstrap_test.rb b/test/pagy/mixins/bootstrap_test.rb
similarity index 95%
rename from test/pagy/extras/bootstrap_test.rb
rename to test/pagy/mixins/bootstrap_test.rb
index aabaa6d0d..50810121c 100644
--- a/test/pagy/extras/bootstrap_test.rb
+++ b/test/pagy/mixins/bootstrap_test.rb
@@ -6,7 +6,7 @@
EXTRA = 'bootstrap'
PREFIX = '_bootstrap'
-describe "pagy/extras/#{EXTRA}" do
+describe "pagy/mixins/#{EXTRA}" do
include NavTests
describe "#pagy#{PREFIX}_nav" do
diff --git a/test/pagy/extras/bootstrap_test.rb.yaml b/test/pagy/mixins/bootstrap_test.rb.yaml
similarity index 98%
rename from test/pagy/extras/bootstrap_test.rb.yaml
rename to test/pagy/mixins/bootstrap_test.rb.yaml
index 83bca1267..4720d160f 100644
--- a/test/pagy/extras/bootstrap_test.rb.yaml
+++ b/test/pagy/mixins/bootstrap_test.rb.yaml
@@ -1,5 +1,5 @@
---
-pagy/extras/bootstrap___pagy_bootstrap_combo_nav_js_test_0001_renders_first,_intermediate_and_last_pages:
+pagy/mixins/bootstrap___pagy_bootstrap_combo_nav_js_test_0001_renders_first,_intermediate_and_last_pages:
:plain_1: ''
-pagy/extras/bootstrap___pagy_bootstrap_nav_test_0001_renders_first,_intermediate_and_last_pages:
+pagy/mixins/bootstrap___pagy_bootstrap_nav_test_0001_renders_first,_intermediate_and_last_pages:
:plain_1:
-pagy/extras/bootstrap___pagy_bootstrap_nav_js_test_0002_renders_first,_intermediate_and_last_pages_with_required_steps:
+pagy/mixins/bootstrap___pagy_bootstrap_nav_js_test_0002_renders_first,_intermediate_and_last_pages_with_required_steps:
:plain_1:
:extras_1:
@@ -132,14 +132,14 @@ pagy/extras/bootstrap___pagy_bootstrap_nav_js_test_0002_renders_first,_intermedi
:extras_50:
:keyset:
-pagy/extras/bootstrap___pagy_bootstrap_nav_js_test_0001_renders_single_and_multiple_pages_when_used_with_Pagy__Countless:
+pagy/mixins/bootstrap___pagy_bootstrap_nav_js_test_0001_renders_single_and_multiple_pages_when_used_with_Pagy__Countless:
:plain_1_0:
:extras_1_0:
:plain_2_23:
:extras_2_23:
-pagy/extras/bootstrap___pagy_bootstrap_nav_js_test_0001_renders_single_and_multiple_pages_when_used_with_Pagy__Offset__Countless:
+pagy/mixins/bootstrap___pagy_bootstrap_nav_js_test_0001_renders_single_and_multiple_pages_when_used_with_Pagy__Offset__Countless:
:plain_1_0:
:extras_1_0:
diff --git a/test/pagy/extras/bulma_test.rb b/test/pagy/mixins/bulma_test.rb
similarity index 95%
rename from test/pagy/extras/bulma_test.rb
rename to test/pagy/mixins/bulma_test.rb
index 3faa6b28b..a22f44751 100644
--- a/test/pagy/extras/bulma_test.rb
+++ b/test/pagy/mixins/bulma_test.rb
@@ -6,7 +6,7 @@
EXTRA = 'bulma'
PREFIX = '_bulma'
-describe "pagy/extras/#{EXTRA}" do
+describe "pagy/mixins/#{EXTRA}" do
include NavTests
describe "#pagy#{PREFIX}_nav" do
diff --git a/test/pagy/extras/bulma_test.rb.yaml b/test/pagy/mixins/bulma_test.rb.yaml
similarity index 98%
rename from test/pagy/extras/bulma_test.rb.yaml
rename to test/pagy/mixins/bulma_test.rb.yaml
index 79906586b..8aef5974c 100644
--- a/test/pagy/extras/bulma_test.rb.yaml
+++ b/test/pagy/mixins/bulma_test.rb.yaml
@@ -1,5 +1,5 @@
---
-pagy/extras/bulma___pagy_bulma_combo_nav_js_test_0001_renders_first,_intermediate_and_last_pages:
+pagy/mixins/bulma___pagy_bulma_combo_nav_js_test_0001_renders_first,_intermediate_and_last_pages:
:plain_1: ''
-pagy/extras/bulma___pagy_bulma_nav_js_test_0001_renders_single_and_multiple_pages_when_used_with_Pagy__Countless:
+pagy/mixins/bulma___pagy_bulma_nav_js_test_0001_renders_single_and_multiple_pages_when_used_with_Pagy__Countless:
:plain_1_0:
:extras_1_0:
:extras_2_23:
-pagy/extras/bulma___pagy_bulma_nav_js_test_0002_renders_first,_intermediate_and_last_pages_with_required_steps:
+pagy/mixins/bulma___pagy_bulma_nav_js_test_0002_renders_first,_intermediate_and_last_pages_with_required_steps:
:plain_1:
:extras_1:
:keyset:
-pagy/extras/bulma___pagy_bulma_nav_test_0001_renders_first,_intermediate_and_last_pages:
+pagy/mixins/bulma___pagy_bulma_nav_test_0001_renders_first,_intermediate_and_last_pages:
:plain_1:
-pagy/extras/bulma___pagy_bulma_nav_js_test_0001_renders_single_and_multiple_pages_when_used_with_Pagy__Offset__Countless:
+pagy/mixins/bulma___pagy_bulma_nav_js_test_0001_renders_single_and_multiple_pages_when_used_with_Pagy__Offset__Countless:
:plain_1_0:
:extras_1_0:
-pagy/extras/calendar__Counts_feature_test_0003_works_with_anchor_string:
+pagy/mixins/calendar__Counts_feature_test_0003_works_with_anchor_string:
:year:
-pagy/extras/calendar__Counts_feature_test_0002_works_with_MockApp__CalendarCountsSkip:
+pagy/mixins/calendar__Counts_feature_test_0002_works_with_MockApp__CalendarCountsSkip:
:year: