From ae310381ca8f034d58bbfaaf21429bfcead0b84f Mon Sep 17 00:00:00 2001 From: Domizio Demichelis Date: Mon, 20 Jan 2025 08:55:12 +0700 Subject: [PATCH] WIP: Static frozen Pagy::DEFAULT --- docs/api/{keyset_for_ui.md => keynav.md} | 12 ++-- docs/extras/keyset_for_ui.md | 4 +- gem/apps/demo.ru | 8 +-- gem/apps/keynav.ru | 15 ++-- gem/apps/keyset.ru | 6 +- gem/apps/keyset_sequel.ru | 6 +- gem/apps/rails.ru | 22 +++--- gem/apps/repro.ru | 7 +- gem/config/pagy.rb | 34 +++------ gem/lib/pagy.rb | 4 +- gem/lib/pagy/backend.rb | 13 ++-- gem/lib/pagy/backend/constructors/offset.rb | 2 +- gem/lib/pagy/backend/helpers/headers.rb | 12 ++-- gem/lib/pagy/backend/helpers/metadata.rb | 8 +-- gem/lib/pagy/console.rb | 7 +- gem/lib/pagy/extras/gearbox.rb | 76 +++++++++----------- gem/lib/pagy/extras/i18n.rb | 8 +-- gem/lib/pagy/extras/size.rb | 62 ++++++++-------- gem/lib/pagy/frontend/pagy/limit_selector.rb | 4 +- gem/lib/pagy/keyset/keynav.rb | 4 +- gem/lib/pagy/modules/url.rb | 2 +- gem/lib/pagy/offset.rb | 4 +- gem/lib/pagy/offset/calendar/day.rb | 4 +- gem/lib/pagy/offset/calendar/month.rb | 4 +- gem/lib/pagy/offset/calendar/quarter.rb | 4 +- gem/lib/pagy/offset/calendar/week.rb | 4 +- gem/lib/pagy/offset/calendar/year.rb | 4 +- gem/lib/pagy/offset/search.rb | 6 +- test/pagy/backend/countless_test.rb | 16 +---- test/pagy/backend/jsonapi_test.rb | 36 +++++----- test/pagy/backend/keyset_test.rb | 11 ++- test/pagy/backend/limit_test.rb | 46 ++++-------- test/pagy/backend/limit_test.rb.yaml | 5 +- test/pagy/backend/metadata_test.rb | 6 -- test/pagy/backend/metadata_test.rb.yaml | 16 +++++ test/pagy/console_test.rb | 3 - test/pagy/extras/gearbox_test.rb | 63 ++++++++-------- test/pagy/offset/overflow_test.rb | 13 +--- 38 files changed, 245 insertions(+), 316 deletions(-) rename docs/api/{keyset_for_ui.md => keynav.md} (90%) diff --git a/docs/api/keyset_for_ui.md b/docs/api/keynav.md similarity index 90% rename from docs/api/keyset_for_ui.md rename to docs/api/keynav.md index 8796380de..02444fd9f 100644 --- a/docs/api/keyset_for_ui.md +++ b/docs/api/keynav.md @@ -31,13 +31,13 @@ You should also familiarize with the [Pagy::Keyset](keyset.md) class. ## Glossary -This section integrates the [Keyset Glossary](keyset_for_ui.md#glossary) +This section integrates the [Keyset Glossary](keynav#glossary) -| Term | Description | -|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `keyset augmented pagination` | The pagy exclusive technique to use `keyset pagination` with numeric pages, supporting `pagy_*navs` and other Frontend helpers.
The best technique for performance AND functionality! | -| `page` | The array of variables from the client prepared by the `keyset_for_ui` extra, to paginate the requested page. | -| `cutoffs` | The array of `cutoff`s of the known pagination state, used to keep track of the visited pages during the navigation. They are cached in the `sessionStorge` of the client. | +| Term | Description | +|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `keynav pagination` | The pagy exclusive technique to use `keyset pagination` with numeric pages, supporting `pagy_*navs` and other Frontend helpers.
The best technique for performance AND functionality! | +| `page` | The array of variables from the client prepared by the `keyset_for_ui` extra, to paginate the requested page. | +| `cutoffs` | The array of `cutoff`s of the known pagination state, used to keep track of the visited pages during the navigation. They are cached in the `sessionStorge` of the client. | ## How Pagy Keyset For UI works diff --git a/docs/extras/keyset_for_ui.md b/docs/extras/keyset_for_ui.md index d75592834..aa507a2f6 100644 --- a/docs/extras/keyset_for_ui.md +++ b/docs/extras/keyset_for_ui.md @@ -17,9 +17,9 @@ and other Frontend helpers. ## Overview -This is a wrapper around the [Pagy::Keyset::Keynav API](/docs/api/keyset_for_ui.md). Please refer to the following resources: +This is a wrapper around the [Pagy::Keyset::Keynav API](/docs/api/keynav). Please refer to the following resources: -[!ref Keyset For UI: Documentation](/docs/api/keyset_for_ui.md) +[!ref Keyset For UI: Documentation](/docs/api/keynav) [!ref Keyset Pagination: Concepts and Overview](/docs/api/keyset.md) diff --git a/gem/apps/demo.ru b/gem/apps/demo.ru index bcebd0a12..909864f80 100644 --- a/gem/apps/demo.ru +++ b/gem/apps/demo.ru @@ -46,12 +46,10 @@ STYLES = { pagy: { extra: 'pagy', prefix: '', css_anchor: 'pagy-scss' }, # Sinatra setup require 'sinatra/base' -# Pagy initializer -Pagy::DEFAULT[:limit_requestable] = true - # Sinatra application class PagyDemo < Sinatra::Base include Pagy::Backend + PAGY_DEFAULT = { maxable_limit: 100 }.freeze get '/' do redirect '/pagy' @@ -59,7 +57,7 @@ class PagyDemo < Sinatra::Base get '/template' do collection = MockCollection.new - @pagy, @records = pagy_offset(collection) + @pagy, @records = pagy_offset(collection, **PAGY_DEFAULT) erb :template, locals: { pagy: @pagy, style: 'pagy' } end @@ -85,7 +83,7 @@ class PagyDemo < Sinatra::Base get("/#{style}") do collection = MockCollection.new - @pagy, @records = pagy_offset(collection) + @pagy, @records = pagy_offset(collection, **PAGY_DEFAULT) erb :helpers, locals: { style:, prefix: } end diff --git a/gem/apps/keynav.ru b/gem/apps/keynav.ru index 037e1a1ae..c0b459238 100644 --- a/gem/apps/keynav.ru +++ b/gem/apps/keynav.ru @@ -30,15 +30,12 @@ gemfile(ENV['PAGY_INSTALL_BUNDLE'] == 'true') do gem 'sqlite3' end -# Pagy initializer -Pagy::DEFAULT[:limit] = 4 -Pagy::DEFAULT[:limit_requestable] = true - # Sinatra setup require 'sinatra/base' # Sinatra application class PagyKeynav < Sinatra::Base include Pagy::Backend + PAGY_DEFAULT = { limit: 4, maxable_limit: 100 }.freeze get('/javascripts/:file') do format = params[:file].split('.').last @@ -54,9 +51,9 @@ class PagyKeynav < Sinatra::Base get '/' do Time.zone = 'UTC' - @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc } - @pagy, @pets = pagy_keynav_js(Pet.order(@order)) - @ids = @pets.pluck(:id) + @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc } + @pagy, @pets = pagy_keynav_js(Pet.order(@order), **PAGY_DEFAULT) + @ids = @pets.pluck(:id) erb :main end @@ -164,10 +161,10 @@ require 'active_record' # ActiveSupport::JSON::Encoding.time_precision = 6 # Log -output = ENV['APP_ENV'].equal?('showcase') ? IO::NULL : $stdout +output = ENV['APP_ENV'].equal?('showcase') ? IO::NULL : $stdout ActiveRecord::Base.logger = Logger.new(output) # SQLite DB files -dir = ENV['APP_ENV'].equal?('development') ? '.' : Dir.pwd # app dir in dev or pwd otherwise +dir = ENV['APP_ENV'].equal?('development') ? '.' : Dir.pwd # app dir in dev or pwd otherwise abort "ERROR: Cannot create DB files: the directory #{dir.inspect} is not writable." \ unless File.writable?(dir) # Connection diff --git a/gem/apps/keyset.ru b/gem/apps/keyset.ru index 144ce4505..ee0c6232b 100644 --- a/gem/apps/keyset.ru +++ b/gem/apps/keyset.ru @@ -30,15 +30,13 @@ gemfile(ENV['PAGY_INSTALL_BUNDLE'] == 'true') do gem 'sqlite3' end -# Pagy initializer -Pagy::DEFAULT[:limit] = 10 -Pagy::DEFAULT[:limit_requestable] = true - # Sinatra setup require 'sinatra/base' # Sinatra application class PagyKeyset < Sinatra::Base include Pagy::Backend + PAGY_DEFAULT = { limit: 10, maxable_limit: 100 }.freeze + # Root route/action get '/' do Time.zone = 'UTC' diff --git a/gem/apps/keyset_sequel.ru b/gem/apps/keyset_sequel.ru index 96fa69c79..d5170a86d 100644 --- a/gem/apps/keyset_sequel.ru +++ b/gem/apps/keyset_sequel.ru @@ -30,16 +30,14 @@ gemfile(ENV['PAGY_INSTALL_BUNDLE'] == 'true') do gem 'sqlite3' end -# Pagy initializer -Pagy::DEFAULT[:limit] = 10 -Pagy::DEFAULT[:limit_requestable] = true - # Sinatra setup require 'sinatra/base' require 'logger' # Sinatra application class PagyKeysetSequel < Sinatra::Base include Pagy::Backend + PAGY_DEFAULT = { limit: 10, maxable_limit: 100 }.freeze + # Root route/action get '/' do @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc } diff --git a/gem/apps/rails.ru b/gem/apps/rails.ru index cdc03e080..5ee8ed592 100644 --- a/gem/apps/rails.ru +++ b/gem/apps/rails.ru @@ -52,17 +52,12 @@ class PagyRails < Rails::Application # :nodoc: end # AR config -dir = Rails.env.development? ? '.' : Dir.pwd # app dir in dev or pwd otherwise +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 -Pagy::DEFAULT[:limit] = 10 -Pagy::DEFAULT[:limit_requestable] = true -Pagy::Offset::DEFAULT[:overflow] = :empty_page - # Activerecord initializer ActiveRecord::Base.logger = Logger.new(OUTPUT) ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: "#{dir}/tmp/pagy-rails.sqlite3") @@ -80,11 +75,15 @@ end # Models class Post < ActiveRecord::Base # :nodoc: has_many :comments -end # :nodoc: +end + +# :nodoc: class Comment < ActiveRecord::Base # :nodoc: belongs_to :post -end # :nodoc: +end + +# :nodoc: # Unused model, useful to test overriding conflicts module Calendar @@ -107,10 +106,13 @@ end class CommentsController < ActionController::Base # :nodoc: include Rails.application.routes.url_helpers include Pagy::Backend + PAGY_DEFAULT = { limit: 10, + maxable_limit: 100, + overflow: :empty_page }.freeze def index - @pagy, @comments = pagy_offset(Comment.all) - pagy_headers_merge(@pagy) + @pagy, @comments = pagy_offset(Comment.all, **PAGY_DEFAULT) + # pagy_headers_merge(@pagy) render inline: TEMPLATE end end diff --git a/gem/apps/repro.ru b/gem/apps/repro.ru index 4756f1e21..e63001498 100644 --- a/gem/apps/repro.ru +++ b/gem/apps/repro.ru @@ -29,16 +29,15 @@ gemfile(ENV['PAGY_INSTALL_BUNDLE'] == 'true') do gem 'sinatra' end -# Edit this section adding/removing the extras and Pagy::DEFAULT as needed +# Edit this section adding the extras as needed # pagy initializer -Pagy::Offset::DEFAULT[:overflow] = :empty_page -Pagy::DEFAULT[:limit_requestable] = true # Sinatra setup require 'sinatra/base' # Sinatra application class PagyRepro < Sinatra::Base include Pagy::Backend + PAGY_DEFAULT = { maxable_limit: 100, overflow: :empty_page }.freeze get('/javascripts/:file') do format = params[:file].split('.').last @@ -53,7 +52,7 @@ class PagyRepro < Sinatra::Base # Edit this action as needed get '/' do collection = MockCollection.new - @pagy, @records = pagy_offset(collection) + @pagy, @records = pagy_offset(collection, **PAGY_DEFAULT) erb :main end diff --git a/gem/config/pagy.rb b/gem/config/pagy.rb index 1cf9dd53e..f72bdec7d 100644 --- a/gem/config/pagy.rb +++ b/gem/config/pagy.rb @@ -2,6 +2,14 @@ # Pagy initializer file (9.3.3) +# IMPORTANT: +# Customizing the static and frozen Pagy::DEFAULT is not supported since version 10.0.0. +# Pass the variables to the constructor, or pass your own DEFAULT hash. +# For example: +# +# PAGY_DEFAULT = { ... } +# pagy_offset(collection, **PAGY_DEFAULT, ...) + # Extras # See https://ddnexus.github.io/pagy/categories/extra @@ -10,17 +18,9 @@ # require 'pagy/extras/size' # must be required before the other extras # Gearbox extra: Automatically change the limit per page depending on the page number +# (e.g. `gearbox_limit: [15, 30, 60, 100]` # See https://ddnexus.github.io/pagy/docs/extras/gearbox # require 'pagy/extras/gearbox' -# set to false only if you want to make :gearbox_extra an opt-in variable -# Pagy::DEFAULT[:gearbox_extra] = false # default true -# Pagy::DEFAULT[:gearbox_limit] = [15, 30, 60, 100] # default - -# Jsonapi extra: Implements JSON:API specifications -# See https://ddnexus.github.io/pagy/docs/extras/jsonapi -# require 'pagy/extras/jsonapi' # must be required after the other extras -# set to false only if you want to make :jsonapi an opt-in variable -# Pagy::DEFAULT[:jsonapi] = false # default true # I18n extra: uses the standard i18n gem which is ~18x slower using ~10x more memory # than the default pagy internal i18n (see below) @@ -53,19 +53,3 @@ # { locale: 'xyz', # not built-in # filepath: 'path/to/pagy-xyz.yml', # pluralize: lambda{ |count| ... } ) - -# Changing DEFAULT for a class will also load the class -# DEFAULTs get inherited from the super classes and merged with the instance argument variables -# -# Pagy::DEFAULT[...] = ... -# Pagy::Offset::DEFAULT[...] = ... -# Pagy::Offset::Calendar::Day::DEFAULT[...] = ... -# Pagy::Offset::Calendar::Month::DEFAULT[...] = ... -# Pagy::Offset::Calendar::Quarter::DEFAULT[...] = ... -# Pagy::Offset::Calendar::Week::DEFAULT[...] = ... -# Pagy::Offset::Calendar::Year::DEFAULT[...] = ... -# Pagy::Offset::Countless::DEFAULT[...] = ... -# Pagy::Offset::Search::ElasticsearchRails::DEFAULT[...] = ... -# Pagy::Offset::Search::Meilisearch::DEFAULT[...] = ... -# Pagy::Offset::Search::Searchkick::DEFAULT[...] = ... -# Pagy::Keyset::Keynav::DEFAULT[...] = ... diff --git a/gem/lib/pagy.rb b/gem/lib/pagy.rb index 914a11f1c..e57ca2dd0 100644 --- a/gem/lib/pagy.rb +++ b/gem/lib/pagy.rb @@ -15,9 +15,9 @@ class Pagy autoload :Keyset, path.join('keyset') autoload :Console, path.join('console') - DEFAULT = { limit: 20, # rubocop:disable Style/MutableConstant + DEFAULT = { limit: 20, limit_sym: :limit, - page_sym: :page } + page_sym: :page }.freeze PAGE_TOKEN = 'P ' LABEL_TOKEN = 'L' LIMIT_TOKEN = 'L ' diff --git a/gem/lib/pagy/backend.rb b/gem/lib/pagy/backend.rb index d8e3c22c7..8294e7648 100644 --- a/gem/lib/pagy/backend.rb +++ b/gem/lib/pagy/backend.rb @@ -11,14 +11,11 @@ module Backend include Url - # Get the limit from request, vars or DEFAULT + # Get the limit from request or DEFAULT def pagy_get_limit(vars) - if vars.key?(:limit_requestable) ? vars[:limit_requestable] : DEFAULT[:limit_requestable] - limit = pagy_requested_limit(vars) || DEFAULT[:limit] - [limit.to_i, (vars[:limit_max] ||= DEFAULT[:limit_max] || 100)].compact.min - else - DEFAULT[:limit] - end + return vars[:limit] || DEFAULT[:limit] unless vars[:maxable_limit] && (limit = pagy_requested_limit(vars)) + + [limit.to_i, vars[:maxable_limit]].min end # Get the limit from the request @@ -35,7 +32,7 @@ def pagy_get_page(vars, force_integer: true) end def pagy_jsonapi?(vars) - return false unless params[:page] && (vars.key?(:jsonapi) ? vars[:jsonapi] : DEFAULT[:jsonapi]) # rubocop:disable Layout/EmptyLineAfterGuardClause + return false unless params[:page] && vars[:jsonapi] # rubocop:disable Layout/EmptyLineAfterGuardClause params[:page].respond_to?(:fetch) || raise(JsonapiReservedParamError, params[:page]) end diff --git a/gem/lib/pagy/backend/constructors/offset.rb b/gem/lib/pagy/backend/constructors/offset.rb index 9d3c08186..a928bc4cc 100644 --- a/gem/lib/pagy/backend/constructors/offset.rb +++ b/gem/lib/pagy/backend/constructors/offset.rb @@ -9,8 +9,8 @@ class Pagy # Return Pagy object and paginated results def pagy_offset(collection, **vars) vars[:count] ||= pagy_get_count(collection, vars) - vars[:limit] ||= pagy_get_limit(vars) vars[:page] ||= pagy_get_page(vars) + vars[:limit] = pagy_get_limit(vars) pagy = Offset.new(**vars) [pagy, pagy_get_items(collection, pagy)] end diff --git a/gem/lib/pagy/backend/helpers/headers.rb b/gem/lib/pagy/backend/helpers/headers.rb index afe0de737..99ef7fdff 100644 --- a/gem/lib/pagy/backend/helpers/headers.rb +++ b/gem/lib/pagy/backend/helpers/headers.rb @@ -3,10 +3,10 @@ require_relative 'links' class Pagy - DEFAULT[:headers] = { page: 'current-page', - limit: 'page-items', - count: 'total-count', - pages: 'total-pages' } + HEADERS = { page: 'current-page', + limit: 'page-items', + count: 'total-count', + pages: 'total-pages' }.freeze # Add specialized backend methods to add pagination response headers Backend.module_eval do private @@ -18,9 +18,7 @@ def pagy_headers_merge(pagy) # Generate a hash of RFC-8288 compliant http headers 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] + headers = pagy.vars[:headers] || HEADERS { '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] && !/^Pagy::Offset::Calendar/.match?(pagy.class.name) diff --git a/gem/lib/pagy/backend/helpers/metadata.rb b/gem/lib/pagy/backend/helpers/metadata.rb index eb4aa2450..28757cfe6 100644 --- a/gem/lib/pagy/backend/helpers/metadata.rb +++ b/gem/lib/pagy/backend/helpers/metadata.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class Pagy - DEFAULT[:metadata] = %i[url_template first_url prev_url page_url next_url last_url - count page limit pages last in from to prev next vars series sequels] + METADATA = %i[url_template first_url prev_url page_url next_url last_url + count page limit pages last in from to prev next vars series sequels].freeze # Add a specialized backend method for pagination metadata Backend.module_eval do @@ -11,9 +11,7 @@ class Pagy # Return the metadata hash def pagy_metadata(pagy, absolute: nil) url_template = pagy_page_url(pagy, PAGE_TOKEN, absolute:) - # 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 - keys = pagy.vars[:metadata] || DEFAULT[:metadata] + keys = pagy.vars[:metadata] || METADATA keys -= %i[count limit] if /^Pagy::Offset::Calendar/.match?(pagy.class.name) {}.tap do |metadata| keys.each do |key| diff --git a/gem/lib/pagy/console.rb b/gem/lib/pagy/console.rb index d2ad312a2..f08dc9467 100644 --- a/gem/lib/pagy/console.rb +++ b/gem/lib/pagy/console.rb @@ -10,7 +10,12 @@ def self.included(main) main.include(Backend) main.include(Frontend) main.define_method(:params) { {} } - DEFAULT[:request] = { url_prefix: 'http://www.example.com/subdir', query_params: { example: '123' } } + # :nocov: + main.define_method(:default_request) do + { request: { url_prefix: 'http://www.example.com/subdir', + query_params: { example: '123' } } } + end + # :nocov: end # Require the extras passed as arguments diff --git a/gem/lib/pagy/extras/gearbox.rb b/gem/lib/pagy/extras/gearbox.rb index 682bb1583..14f4f01a2 100644 --- a/gem/lib/pagy/extras/gearbox.rb +++ b/gem/lib/pagy/extras/gearbox.rb @@ -1,56 +1,48 @@ # frozen_string_literal: true class Pagy - Offset::DEFAULT[:gearbox_extra] = true # extra enabled by default - Offset::DEFAULT[:gearbox_limit] = [15, 30, 60, 100] - # Automatically change the limit depending on the page number # accepts an array as the :gearbox_limit variable, that will determine the limit for the first pages module GearboxExtra - module OffsetOverride - # Assign @limit based on the :gearbox_limit variable - def assign_limit - return super if !@vars[:gearbox_extra] || @vars[:limit_requestable] - - gears = @vars[:gearbox_limit] - raise VariableError.new(self, :gearbox_limit, 'to be an Array of positives', gears) \ - unless gears.is_a?(Array) && gears.all? { |num| num.positive? rescue false } # rubocop:disable Style/RescueModifier + # Assign @limit based on the :gearbox_limit variable + def assign_limit + return super if !(gears = @vars[:gearbox_limit]) || @vars[:maxable_limit] - @limit = gears[@page - 1] || gears.last - end + raise VariableError.new(self, :gearbox_limit, 'to be an Array of positives', gears) \ + unless gears.is_a?(Array) && gears.all? { |num| num.positive? rescue false } # rubocop:disable Style/RescueModifier - # Asgnsi @offset based on the :gearbox_limit variable - def assign_offset - return super if !@vars[:gearbox_extra] || @vars[:limit_requestable] + @limit = gears[@page - 1] || gears.last + end - gears = @vars[:gearbox_limit] - @offset = if @page <= gears.count - gears[0, @page - 1].sum - else - gears.sum + (gears.last * (@page - gears.count - 1)) - end + @outset - end + # Assign @offset based on the :gearbox_limit variable + def assign_offset + return super if !(gears = @vars[:gearbox_limit]) || @vars[:maxable_limit] - # Assign @last based on the :gearbox_limit variable and @count - def assign_last - return super if !@vars[:gearbox_extra] || @vars[:limit_requestable] + @offset = if @page <= gears.count + gears[0, @page - 1].sum + else + gears.sum + (gears.last * (@page - gears.count - 1)) + end + @outset + end - gears = @vars[:gearbox_limit] - # This algorithm is thousands of times faster than the one in the geared_pagination gem - @last = (if count > (sum = gears.sum) - [((count - sum).to_f / gears.last).ceil, 1].max + gears.count - else - pages = 0 - remainder = count - while remainder.positive? - pages += 1 - remainder -= gears[pages - 1] - end - [pages, 1].max - end) - @last = @vars[:max_pages] if @vars[:max_pages] && @last > @vars[:max_pages] - end + # Assign @last based on the :gearbox_limit variable and @count + def assign_last + return super if !(gears = @vars[:gearbox_limit]) || @vars[:maxable_limit] + + # This algorithm is thousands of times faster than the one in the geared_pagination gem + @last = (if count > (sum = gears.sum) + [((count - sum).to_f / gears.last).ceil, 1].max + gears.count + else + pages = 0 + remainder = count + while remainder.positive? + pages += 1 + remainder -= gears[pages - 1] + end + [pages, 1].max + end) + @last = @vars[:max_pages] if @vars[:max_pages] && @last > @vars[:max_pages] end - Offset.prepend(OffsetOverride) end + Offset.prepend(GearboxExtra) end diff --git a/gem/lib/pagy/extras/i18n.rb b/gem/lib/pagy/extras/i18n.rb index 98c0741a3..e057fb5cc 100644 --- a/gem/lib/pagy/extras/i18n.rb +++ b/gem/lib/pagy/extras/i18n.rb @@ -4,13 +4,11 @@ class Pagy # Use ::I18n gem module I18nExtra # Frontend overriding for translation - module FrontendOverride - def pagy_t(key, **) - ::I18n.t(key, **) - end + def pagy_t(key, **) + ::I18n.t(key, **) end - Frontend.prepend I18nExtra::FrontendOverride end + Frontend.prepend I18nExtra # 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/size.rb b/gem/lib/pagy/extras/size.rb index b805cd89c..d25156502 100644 --- a/gem/lib/pagy/extras/size.rb +++ b/gem/lib/pagy/extras/size.rb @@ -1,41 +1,39 @@ # frozen_string_literal: true class Pagy - class Offset - # Implement the legacy bar using the array size. - # Unless you have very specific requirements, use the faster and better looking default bar. - module SizeExtra - # Implements the old series algorithm - def series(size: @vars[:size], **) - return super unless size.is_a?(Array) - return [] if size == [] - raise VariableError.new(self, :size, 'to be an Array of 4 Integers or []', size) \ - unless size.is_a?(Array) && size.size == 4 && size.all? { |num| !num.negative? rescue false } # rubocop:disable Style/RescueModifier + # Implement the legacy bar using the array size. + # Unless you have very specific requirements, use the faster and better looking default bar. + module SizeExtra + # Implements the old series algorithm + def series(size: @vars[:size], **) + return super unless size.is_a?(Array) + return [] if size == [] + raise VariableError.new(self, :size, 'to be an Array of 4 Integers or []', size) \ + unless size.is_a?(Array) && size.size == 4 && size.all? { |num| !num.negative? rescue false } # rubocop:disable Style/RescueModifier - [].tap do |series| - # This algorithm is up to ~5x faster and ~2.3x lighter than the previous one (pagy < 4.3) - # However the behavior of the legacy nav bar was taken straight from WillPaginate and Kaminari: - # it's ill-concieved and complicates the experience of devs and users. - left_gap_start = 1 + size[0] - left_gap_end = @page - size[1] - 1 - right_gap_start = @page + size[2] + 1 - right_gap_end = @last - size[3] - left_gap_end = right_gap_end if left_gap_end > right_gap_end - right_gap_start = left_gap_start if left_gap_start > right_gap_start - start = 1 - if (left_gap_end - left_gap_start).positive? - series.push(*start...left_gap_start, :gap) - start = left_gap_end + 1 - end - if (right_gap_end - right_gap_start).positive? - series.push(*start...right_gap_start, :gap) - start = right_gap_end + 1 - end - series.push(*start..@last) - series[series.index(@page)] = @page.to_s + [].tap do |series| + # This algorithm is up to ~5x faster and ~2.3x lighter than the previous one (pagy < 4.3) + # However the behavior of the legacy nav bar was taken straight from WillPaginate and Kaminari: + # it's ill-concieved and complicates the experience of devs and users. + left_gap_start = 1 + size[0] + left_gap_end = @page - size[1] - 1 + right_gap_start = @page + size[2] + 1 + right_gap_end = @last - size[3] + left_gap_end = right_gap_end if left_gap_end > right_gap_end + right_gap_start = left_gap_start if left_gap_start > right_gap_start + start = 1 + if (left_gap_end - left_gap_start).positive? + series.push(*start...left_gap_start, :gap) + start = left_gap_end + 1 end + if (right_gap_end - right_gap_start).positive? + series.push(*start...right_gap_start, :gap) + start = right_gap_end + 1 + end + series.push(*start..@last) + series[series.index(@page)] = @page.to_s end end - prepend SizeExtra end + Offset.prepend SizeExtra end diff --git a/gem/lib/pagy/frontend/pagy/limit_selector.rb b/gem/lib/pagy/frontend/pagy/limit_selector.rb index 9a981bdfa..5eca8ec34 100644 --- a/gem/lib/pagy/frontend/pagy/limit_selector.rb +++ b/gem/lib/pagy/frontend/pagy/limit_selector.rb @@ -4,7 +4,7 @@ class Pagy Frontend.module_eval do # 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_requestable] + return '' unless pagy.vars[:maxable_limit] id = %( id="#{id}") if id vars = pagy.vars @@ -13,7 +13,7 @@ def pagy_limit_selector_js(pagy, id: nil, item_name: nil) url_token = pagy_page_url(pagy, PAGE_TOKEN) vars[:limit] = limit # restore the limit - limit_input = %(#{A_TAG}) %( page_and_limit } : page_and_limit) if page_and_limit.size.positive? case pagy_params diff --git a/gem/lib/pagy/offset.rb b/gem/lib/pagy/offset.rb index 8b68339d2..02e3c4f4f 100644 --- a/gem/lib/pagy/offset.rb +++ b/gem/lib/pagy/offset.rb @@ -11,11 +11,11 @@ class Offset < Pagy autoload :Search, path.join('search') # Core default: constant for easy access, but mutable for customizable defaults - DEFAULT = { count_args: [:all], # AR friendly # rubocop:disable Style/MutableConstant + DEFAULT = { count_args: [:all], # AR friendly ends: true, outset: 0, page: 1, - size: 7 } + size: 7 }.freeze attr_reader :from, :offset, :to diff --git a/gem/lib/pagy/offset/calendar/day.rb b/gem/lib/pagy/offset/calendar/day.rb index 2c5476614..92101a719 100644 --- a/gem/lib/pagy/offset/calendar/day.rb +++ b/gem/lib/pagy/offset/calendar/day.rb @@ -5,10 +5,10 @@ class Offset class Calendar # Day unit subclass class Day < Unit - DEFAULT = { size: 31, # rubocop:disable Style/MutableConstant + DEFAULT = { size: 31, ends: false, order: :asc, - format: '%d' } + format: '%d' }.freeze protected diff --git a/gem/lib/pagy/offset/calendar/month.rb b/gem/lib/pagy/offset/calendar/month.rb index 5e3bbee4f..8d982bd9a 100644 --- a/gem/lib/pagy/offset/calendar/month.rb +++ b/gem/lib/pagy/offset/calendar/month.rb @@ -5,10 +5,10 @@ class Offset class Calendar # :nodoc: # Month unit subclass class Month < Unit - DEFAULT = { size: 12, # rubocop:disable Style/MutableConstant + DEFAULT = { size: 12, ends: false, order: :asc, - format: '%b' } + format: '%b' }.freeze protected diff --git a/gem/lib/pagy/offset/calendar/quarter.rb b/gem/lib/pagy/offset/calendar/quarter.rb index 579dedf71..6a0688d81 100644 --- a/gem/lib/pagy/offset/calendar/quarter.rb +++ b/gem/lib/pagy/offset/calendar/quarter.rb @@ -5,10 +5,10 @@ class Offset class Calendar # :nodoc: # Quarter unit subclass class Quarter < Unit - DEFAULT = { size: 4, # rubocop:disable Style/MutableConstant + DEFAULT = { size: 4, ends: false, order: :asc, - format: 'Q%q' } # '%q' token + format: 'Q%q' }.freeze # '%q' token # The label for any page, with the substitution of the '%q' token def label_for(page, opts = {}) diff --git a/gem/lib/pagy/offset/calendar/week.rb b/gem/lib/pagy/offset/calendar/week.rb index be0706a41..503440f08 100644 --- a/gem/lib/pagy/offset/calendar/week.rb +++ b/gem/lib/pagy/offset/calendar/week.rb @@ -5,8 +5,8 @@ class Offset class Calendar # Week unit subclass class Week < Unit - DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant - format: '%Y-%W' } + DEFAULT = { order: :asc, + format: '%Y-%W' }.freeze protected diff --git a/gem/lib/pagy/offset/calendar/year.rb b/gem/lib/pagy/offset/calendar/year.rb index 0c19e8127..10dfd4938 100644 --- a/gem/lib/pagy/offset/calendar/year.rb +++ b/gem/lib/pagy/offset/calendar/year.rb @@ -5,10 +5,10 @@ class Offset class Calendar # :nodoc: # Year unit subclass class Year < Unit - DEFAULT = { size: 10, # rubocop:disable Style/MutableConstant + DEFAULT = { size: 10, ends: false, order: :asc, - format: '%Y' } + format: '%Y' }.freeze protected diff --git a/gem/lib/pagy/offset/search.rb b/gem/lib/pagy/offset/search.rb index a46f5c397..d3f12d02f 100644 --- a/gem/lib/pagy/offset/search.rb +++ b/gem/lib/pagy/offset/search.rb @@ -15,7 +15,7 @@ def pagy_search(term = nil, **options, &block) end class ElasticsearchRails < Offset - DEFAULT = { search_method: :search } # rubocop:disable Style/MutableConstant + DEFAULT = { search_method: :search }.freeze # Get the count from different version of ElasticsearchRails def self.total_count(results) @@ -25,11 +25,11 @@ def self.total_count(results) end class Meilisearch < Offset - DEFAULT = { search_method: :ms_search } # rubocop:disable Style/MutableConstant + DEFAULT = { search_method: :ms_search }.freeze end class Searchkick < Offset - DEFAULT = { search_method: :search } # rubocop:disable Style/MutableConstant + DEFAULT = { search_method: :search }.freeze end end end diff --git a/test/pagy/backend/countless_test.rb b/test/pagy/backend/countless_test.rb index 111999ef8..3574bf7e9 100644 --- a/test/pagy/backend/countless_test.rb +++ b/test/pagy/backend/countless_test.rb @@ -8,12 +8,9 @@ let(:app) { MockApp.new } let(:last_page) { 1000 / 20 } before do - @default_page_sym = Pagy::DEFAULT[:page_sym] + @default_page_sym = :page @collection = MockCollection.new end - after do - Pagy::DEFAULT[:page_sym] = @default_page_sym - end describe '#pagy_countless' do it 'shows current and next for first page' do @@ -62,22 +59,13 @@ describe '#pagy_countless_get_vars' do let(:app) { MockApp.new(params: { a: 'a', page: 3, page_number: 4 }) } - it 'sets :page_sym from defaults' do - Pagy::DEFAULT[:page_sym] = :page_number - pagy, paged = app.send(:pagy_countless, @collection) - _(pagy.count).must_be_nil - _(pagy.page).must_equal 4 - _(paged).must_equal Array(61..80) - end it 'sets :page_sym from vars' do - Pagy::DEFAULT[:page_sym] = :page pagy, paged = app.send(:pagy_countless, @collection, page_sym: :page_number) _(pagy.count).must_be_nil _(pagy.page).must_equal 4 _(paged).must_equal Array(61..80) end it 'bypasses :page_sym with :page variable' do - Pagy::DEFAULT[:page_sym] = :another_page_number pagy, paged = app.send(:pagy_countless, @collection, page_sym: :page_number, page: 1) _(pagy.count).must_be_nil _(pagy.page).must_equal 1 @@ -93,7 +81,6 @@ end describe 'Keep last' do it 'shows series including last page' do - Pagy::DEFAULT[:page_sym] = :page pagy, = MockApp.new(params: {page: '25 50'}).send(:pagy_countless, @collection) _(pagy.series).must_equal [1, :gap, 24, "25", 26, :gap, 50] _(pagy.count).must_be_nil @@ -101,7 +88,6 @@ _(pagy.next).must_equal 26 end it 'shows series including last page' do - Pagy::DEFAULT[:page_sym] = :page pagy, = MockApp.new(params: { a: 'a', page: ' 3'}).send(:pagy_countless, @collection) _(pagy.series).must_equal ["1", 2, 3] _(pagy.count).must_be_nil diff --git a/test/pagy/backend/jsonapi_test.rb b/test/pagy/backend/jsonapi_test.rb index 2eb574e5e..46cfd9a82 100644 --- a/test/pagy/backend/jsonapi_test.rb +++ b/test/pagy/backend/jsonapi_test.rb @@ -5,83 +5,79 @@ require_relative '../../files/models' require_relative '../../mock_helpers/app' -Pagy::DEFAULT[:limit_requestable] = true -Pagy::DEFAULT[:jsonapi] = true - describe 'jsonapi' do before do @collection = MockCollection.new + @pagy_default = { maxable_limit: 100, jsonapi: true } end it 'raises PageParamError with page number' do app = MockApp.new(params: { page: 2 }) - _ { _pagy, _records = app.send(:pagy_offset, @collection) }.must_raise Pagy::JsonapiReservedParamError + _ { _pagy, _records = app.send(:pagy_offset, @collection, **@pagy_default) }.must_raise Pagy::JsonapiReservedParamError end describe 'JsonApi' do it 'uses the :jsonapi with page:nil' do app = MockApp.new(params: { page: nil }) - pagy, _records = app.send(:pagy_offset, @collection, limit_requestable: false) + pagy, _records = app.send(:pagy_offset, @collection, jsonapi: true) _(app.send(:pagy_page_url, pagy, 1)).must_rematch :url_1 - pagy, _records = app.send(:pagy_offset, @collection) + pagy, _records = app.send(:pagy_offset, @collection, **@pagy_default) _(app.send(:pagy_page_url, pagy, 1)).must_rematch :url_2 end it 'uses the :jsonapi with page:3' do app = MockApp.new(params: { page: { page: 3 } }) - pagy, _records = app.send(:pagy_offset, @collection, limit_requestable: false) + pagy, _records = app.send(:pagy_offset, @collection, jsonapi: true) _(app.send(:pagy_page_url, pagy, 2)).must_rematch :url_1 - pagy, _records = app.send(:pagy_offset, @collection) + pagy, _records = app.send(:pagy_offset, @collection, **@pagy_default) _(app.send(:pagy_page_url, pagy, 2)).must_rematch :url_2 end end describe 'Skip JsonApi' do it 'skips the :jsonapi with page:nil' do - Pagy::DEFAULT[:jsonapi] = false app = MockApp.new(params: { page: nil }) - pagy, _records = app.send(:pagy_offset, @collection, limit_requestable: false) - _(app.send(:pagy_page_url, pagy, 1)).must_equal '/foo?page=1' pagy, _records = app.send(:pagy_offset, @collection) + _(app.send(:pagy_page_url, pagy, 1)).must_equal '/foo?page=1' + pagy, _records = app.send(:pagy_offset, @collection, maxable_limit: 100) _(app.send(:pagy_page_url, pagy, 1)).must_equal '/foo?page=1&limit=20' - Pagy::DEFAULT[:jsonapi] = true end it 'skips the :jsonapi with page:3' do app = MockApp.new(params: { page: 3 }) - pagy, _records = app.send(:pagy_offset, @collection, jsonapi: false, limit_requestable: false) + pagy, _records = app.send(:pagy_offset, @collection) _(app.send(:pagy_page_url, pagy, 2)).must_equal '/foo?page=2' - pagy, _records = app.send(:pagy_offset, @collection, jsonapi: false) + pagy, _records = app.send(:pagy_offset, @collection, **@pagy_default, jsonapi: false) _(app.send(:pagy_page_url, pagy, 2)).must_equal '/foo?page=2&limit=20' end end describe 'JsonApi with custom named params' do it 'gets custom named params' do app = MockApp.new(params: { page: { number: 3, size: 10 } }) - pagy, _records = app.send(:pagy_offset, @collection, page_sym: :number, limit_sym: :size) + pagy, _records = app.send(:pagy_offset, @collection, **@pagy_default, page_sym: :number, limit_sym: :size) _(pagy.page).must_equal 3 _(pagy.limit).must_equal 10 end it 'sets custom named params' do app = MockApp.new(params: { page: { number: 3, size: 10 } }) - pagy, _records = app.send(:pagy_offset, @collection, page_sym: :number, limit_sym: :size) + pagy, _records = app.send(:pagy_offset, @collection, **@pagy_default, page_sym: :number, limit_sym: :size) _(app.send(:pagy_page_url, pagy, 4)).must_rematch :url end end describe '#pagy_links' do it 'returns the ordered links' do app = MockApp.new(params: { page: { number: 3, size: 10 } }) - pagy, _records = app.send(:pagy_offset, @collection, page_sym: :number, limit_sym: :size) + pagy, _records = app.send(:pagy_offset, @collection, **@pagy_default, page_sym: :number, limit_sym: :size) result = app.send(:pagy_links, pagy) _(result.keys).must_equal %i[first prev next last] _(result).must_rematch :result end it 'sets the prev value to null when the link is unavailable' do app = MockApp.new(params: { page: { page: 1 } }) - pagy, _records = app.send(:pagy_offset, @collection) + pagy, _records = app.send(:pagy_offset, @collection, **@pagy_default) result = app.send(:pagy_links, pagy) _(result[:prev]).must_be_nil end it 'sets the next value to null when the link is unavailable' do app = MockApp.new(params: { page: { page: 50 } }) - pagy, _records = app.send(:pagy_offset, @collection) + pagy, _records = app.send(:pagy_offset, @collection, **@pagy_default) result = app.send(:pagy_links, pagy) _(result[:next]).must_be_nil end @@ -91,6 +87,7 @@ app = MockApp.new(params: { page: { latest: 'WzIwXQ', size: 10 } }) pagy, _records = app.send(:pagy_keyset, Pet.order(:id), + **@pagy_default, page_sym: :latest, limit_sym: :size) result = app.send(:pagy_links, pagy) @@ -102,6 +99,7 @@ app = MockApp.new(params: { page: { size: 50 } }) pagy, _records = app.send(:pagy_keyset, Pet.order(:id), + **@pagy_default, page_sym: :latest, limit_sym: :size) result = app.send(:pagy_links, pagy) diff --git a/test/pagy/backend/keyset_test.rb b/test/pagy/backend/keyset_test.rb index c0b955b38..8b0dd2c7f 100644 --- a/test/pagy/backend/keyset_test.rb +++ b/test/pagy/backend/keyset_test.rb @@ -4,8 +4,6 @@ require_relative '../../files/models' require_relative '../../mock_helpers/app' -Pagy::DEFAULT[:limit_requestable] = true - describe 'keyset' do [Pet, PetSequel].each do |model| describe '#pagy_keyset' do @@ -23,7 +21,8 @@ app = MockApp.new(params: { page: "WzEwXQ", limit: 10 }) pagy, records = app.send(:pagy_keyset, model.order(:id), - tuple_comparison: true) + tuple_comparison: true, + maxable_limit: 100) _(records.first.id).must_equal 11 _(pagy.next).must_equal "WzIwXQ" end @@ -31,19 +30,19 @@ describe 'URL helpers' do it 'returns the URLs for first page' do app = MockApp.new(params: { page: nil, limit: 10 }) - pagy, _records = app.send(:pagy_keyset, model.order(:id)) + pagy, _records = app.send(:pagy_keyset, model.order(:id), maxable_limit: 100) _(app.send(:pagy_keyset_first_url, pagy)).must_equal "/foo?limit=10" _(app.send(:pagy_keyset_next_url, pagy)).must_equal "/foo?limit=10&page=WzEwXQ" end it 'returns the URLs for second page' do app = MockApp.new(params: { page: "WzEwXQ", limit: 10 }) - pagy, _records = app.send(:pagy_keyset, model.order(:id)) + pagy, _records = app.send(:pagy_keyset, model.order(:id), maxable_limit: 100) _(app.send(:pagy_keyset_first_url, pagy)).must_equal "/foo?limit=10" _(app.send(:pagy_keyset_next_url, pagy)).must_equal "/foo?limit=10&page=WzIwXQ" end it 'returns the URLs for last page' do app = MockApp.new(params: { page: "WzQwXQ", limit: 10 }) - pagy, _records = app.send(:pagy_keyset, model.order(:id)) + pagy, _records = app.send(:pagy_keyset, model.order(:id), maxable_limit: 100) _(app.send(:pagy_keyset_first_url, pagy)).must_equal "/foo?limit=10" _(app.send(:pagy_keyset_next_url, pagy)).must_be_nil end diff --git a/test/pagy/backend/limit_test.rb b/test/pagy/backend/limit_test.rb index daabc3061..a063aa6e4 100644 --- a/test/pagy/backend/limit_test.rb +++ b/test/pagy/backend/limit_test.rb @@ -8,8 +8,6 @@ require_relative '../../mock_helpers/collection' require_relative '../../mock_helpers/app' -Pagy::DEFAULT[:limit_requestable] = true - def test_limit_vars_params(limit, vars, params) app = MockApp.new params: params _(app.params.to_param).must_equal params.to_param @@ -31,7 +29,7 @@ def test_limit_vars_params(limit, vars, params) end end -describe 'limit_requestable' do +describe 'maxable_limit' do let(:app) { MockApp.new } describe "controller_methods" do before do @@ -45,13 +43,13 @@ def test_limit_vars_params(limit, vars, params) end it 'uses the params' do limit = 12 - vars = {} + vars = { maxable_limit: 100 } params = { a: "a", page: 3, limit: limit } test_limit_vars_params(limit, vars, params) end it 'uses the params without page' do limit = 12 - vars = {} + vars = { maxable_limit: 100 } params = { a: "a", limit: limit } test_limit_vars_params(limit, vars, params) end @@ -61,36 +59,22 @@ def test_limit_vars_params(limit, vars, params) params = { a: "a", page: 3, limit: 12 } test_limit_vars_params(limit, vars, params) end - it 'uses the limit_max default' do - limit = 100 - vars = {} - params = { a: "a", page: 3, limit: 120 } - test_limit_vars_params(limit, vars, params) - end - it 'limit to 100 if :limit_max is not set explicitly' do - limit = 100 - vars = { limit_max: nil } - params = { a: "a", limit: 1000 } - test_limit_vars_params(limit, vars, params) - end it 'uses limit_sym from vars' do limit = 14 - vars = { limit_sym: :custom } + vars = { maxable_limit: 100, limit_sym: :custom } params = { a: "a", page: 3, limit_sym: :custom, custom: limit } test_limit_vars_params(limit, vars, params) end it 'uses limit_sym from default' do limit = 15 - vars = {} + vars = { limit_sym: :custom, maxable_limit: 100 } params = { a: "a", page: 3, custom: 15 } - Pagy::DEFAULT[:limit_sym] = :custom test_limit_vars_params(limit, vars, params) - Pagy::DEFAULT[:limit_sym] = :limit # reset default end - it 'doesn\'t use the :limit_requestable' do + it 'doesn\'t use the :maxable_limit' do limit = 20 - vars = { limit_requestable: false } + vars = {} params = { a: "a", page: 3, limit: 35 } test_limit_vars_params(limit, vars, params) @@ -100,31 +84,31 @@ def test_limit_vars_params(limit, vars, params) describe 'view_methods' do describe '#pagy_page_url' do it 'renders basic url' do - pagy = Pagy::Offset.new count: 1000, page: 3 + pagy = Pagy::Offset.new(count: 1000, page: 3, maxable_limit: 100) _(app.pagy_page_url(pagy, 5)).must_equal '/foo?page=5&limit=20' end it 'renders basic url and limit var' do - pagy = Pagy::Offset.new count: 1000, page: 3, limit: 50 + pagy = Pagy::Offset.new(count: 1000, page: 3, limit: 50, maxable_limit: 100) _(app.pagy_page_url(pagy, 5)).must_equal '/foo?page=5&limit=50' end it 'renders url with limit_sym' do - pagy = Pagy::Offset.new count: 1000, page: 3, limit_sym: :custom + pagy = Pagy::Offset.new(count: 1000, page: 3, limit_sym: :custom, maxable_limit: 100) _(app.pagy_page_url(pagy, 5)).must_equal '/foo?page=5&custom=20' end it 'renders url with fragment' do - pagy = Pagy::Offset.new count: 1000, page: 3 + pagy = Pagy::Offset.new(count: 1000, page: 3, maxable_limit: 100) _(app.pagy_page_url(pagy, 6, fragment: '#fragment')).must_equal '/foo?page=6&limit=20#fragment' end it 'renders url with params and fragment' do - pagy = Pagy::Offset.new count: 1000, page: 3, params: { a: 3, b: 4 }, limit: 40 + pagy = Pagy::Offset.new(count: 1000, page: 3, params: { a: 3, b: 4 }, limit: 40, maxable_limit: 100) _(app.pagy_page_url(pagy, 5, fragment: '#fragment')).must_equal "/foo?page=5&limit=40&a=3&b=4#fragment" end end - it 'renders limit selector' do - pagy, = app.send(:pagy_offset, MockCollection.new, page: 3) + it 'renders or skips the output depending on maxable_limit' do + pagy, = app.send(:pagy_offset, MockCollection.new, page: 3, maxable_limit: 100) _(app.pagy_limit_selector_js(pagy)).must_rematch :selector_1 _(app.pagy_limit_selector_js(pagy, id: 'test-id', item_name: 'products')).must_rematch :selector_2 - pagy, = app.send(:pagy_offset, MockCollection.new, page: 3, limit_requestable: false) + pagy, = app.send(:pagy_offset, MockCollection.new, page: 3) _(app.pagy_limit_selector_js(pagy, id: 'test-id')).must_equal '' end end diff --git a/test/pagy/backend/limit_test.rb.yaml b/test/pagy/backend/limit_test.rb.yaml index 5cd2c7db1..3c060f2c2 100644 --- a/test/pagy/backend/limit_test.rb.yaml +++ b/test/pagy/backend/limit_test.rb.yaml @@ -1,5 +1,5 @@ --- -limit_requestable__view_methods_test_0001_renders_limit_selector: +maxable_limit__view_methods_test_0001_renders_or_skips_the_output_depending_on_maxable_limit: :selector_1: '' +maxable_limit__view_methods_test_0001_renders_limit_selector: + :selector_1: '' + :selector_2: '' diff --git a/test/pagy/backend/metadata_test.rb b/test/pagy/backend/metadata_test.rb index 2633b039a..4d8fadd87 100644 --- a/test/pagy/backend/metadata_test.rb +++ b/test/pagy/backend/metadata_test.rb @@ -35,12 +35,6 @@ def self.test_order pagy, _records = app.send(:pagy_offset, @collection, metadata: %i[url_template page count prev next pages]) _(app.send(:pagy_metadata, pagy)).must_rematch :metadata end - # It permanently changes the DEFAULT from now on (alpha order test required) - it 'works with set DEFAULT' do - Pagy::DEFAULT[:metadata] = %i[url_template page count prev next pages] - pagy, _records = app.send(:pagy_offset, @collection) - _(app.send(:pagy_metadata, pagy)).must_rematch :set_default - end it 'checks for unknown metadata for Pagy::Offset::Calendar' do calendar, _pagy, _records = calendar_app.send(:pagy_calendar, Event.all, year: { metadata: %i[page unknown_key] }) diff --git a/test/pagy/backend/metadata_test.rb.yaml b/test/pagy/backend/metadata_test.rb.yaml index f4869f3a7..1b3a0e867 100644 --- a/test/pagy/backend/metadata_test.rb.yaml +++ b/test/pagy/backend/metadata_test.rb.yaml @@ -110,3 +110,19 @@ metadata___pagy_metadata_for_Pagy_test_0006_returns_only_specific_metadata_for_P :prev: 2 :next: 4 :pages: 26 +metadata___pagy_metadata_for_Pagy_test_0005_returns_only_specific_metadata_for_Pagy__Offset__Calendar: + :metadata: + :url_template: "/foo?month_page=P " + :page: 3 + :from: !ruby/object:ActiveSupport::TimeWithZone + utc: 2021-12-01 05:00:00.000000000 Z + zone: &4 !ruby/object:ActiveSupport::TimeZone + name: EST + time: 2021-12-01 00:00:00.000000000 Z + :to: !ruby/object:ActiveSupport::TimeWithZone + utc: 2022-01-01 05:00:00.000000000 Z + zone: *4 + time: 2022-01-01 00:00:00.000000000 Z + :prev: 2 + :next: 4 + :pages: 26 diff --git a/test/pagy/console_test.rb b/test/pagy/console_test.rb index 384acd23a..319e3f171 100644 --- a/test/pagy/console_test.rb +++ b/test/pagy/console_test.rb @@ -11,9 +11,6 @@ module PagyConsole describe 'pagy/console' do describe 'Pagy::Console' do - it 'defines default :url' do - _(Pagy::DEFAULT[:request][:url_prefix]).must_equal 'http://www.example.com/subdir' - end it 'includes Pagy::Backend and Pagy::Frontend' do assert_operator(PagyConsole, :<, Pagy::Backend) assert_operator(PagyConsole, :<, Pagy::Frontend) diff --git a/test/pagy/extras/gearbox_test.rb b/test/pagy/extras/gearbox_test.rb index 46f05ea3c..837413a0a 100644 --- a/test/pagy/extras/gearbox_test.rb +++ b/test/pagy/extras/gearbox_test.rb @@ -4,56 +4,53 @@ require 'pagy/extras/gearbox' require 'pagy/offset/countless' -Pagy::DEFAULT[:limit_requestable] = false - describe 'pagy/extras/gearbox' do describe '#assign_limit' do it 'raises VariableErrors for wrong limit types' do - _ { Pagy::Offset.new(count: 3, page: 1, gearbox_limit: [-1, 10]) }.must_raise Pagy::VariableError - _ { Pagy::Offset.new(count: 3, page: 1, gearbox_limit: [0, 10]) }.must_raise Pagy::VariableError - _ { Pagy::Offset.new(count: 3, page: 1, gearbox_limit: [5, "10"]) }.must_raise Pagy::VariableError + _ { Pagy::Offset.new(count: 3, page: 1, gearbox_limit: [-1, 10]) }.must_raise Pagy::VariableError + _ { Pagy::Offset.new(count: 3, page: 1, gearbox_limit: [0, 10]) }.must_raise Pagy::VariableError + _ { Pagy::Offset.new(count: 3, page: 1, gearbox_limit: [5, "10"]) }.must_raise Pagy::VariableError end it 'can skips gearbox in Pagy' do - _(Pagy::Offset.new(count: 0, page: 1, limit_requestable: true).limit).must_equal 20 - _(Pagy::Offset.new(count: 0, page: 1, gearbox_extra: false).limit).must_equal 20 + _(Pagy::Offset.new(count: 0, page: 1, maxable_limit: 100).limit).must_equal 20 + _(Pagy::Offset.new(count: 0, page: 1).limit).must_equal 20 end it 'sets the limit in Pagy' do - _(Pagy::Offset.new(count: 0, page: 1).limit).must_equal 15 - _(Pagy::Offset.new(count: 15, page: 1).limit).must_equal 15 - _(Pagy::Offset.new(count: 45, page: 2).limit).must_equal 30 - _(Pagy::Offset.new(count: 1000, page: 3).limit).must_equal 60 - _(Pagy::Offset.new(count: 1000, page: 4).limit).must_equal 100 - _(Pagy::Offset.new(count: 0, page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 - _(Pagy::Offset.new(count: 3, page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 - _(Pagy::Offset.new(count: 13, page: 2, gearbox_limit: [3, 10]).limit).must_equal 10 - _(Pagy::Offset.new(count: 103, page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 - _(Pagy::Offset.new(count: 103, page: 2, gearbox_limit: [3, 10]).limit).must_equal 10 - _(Pagy::Offset.new(count: 103, page: 3, gearbox_limit: [3, 10]).limit).must_equal 10 - _(Pagy::Offset.new(count: 103, page: 11, gearbox_limit: [3, 10]).limit).must_equal 10 + _(Pagy::Offset.new(count: 0, page: 1, gearbox_limit: [15, 30, 60, 100]).limit).must_equal 15 + _(Pagy::Offset.new(count: 15, page: 1, gearbox_limit: [15, 30, 60, 100]).limit).must_equal 15 + _(Pagy::Offset.new(count: 45, page: 2, gearbox_limit: [15, 30, 60, 100]).limit).must_equal 30 + _(Pagy::Offset.new(count: 1000, page: 3, gearbox_limit: [15, 30, 60, 100]).limit).must_equal 60 + _(Pagy::Offset.new(count: 1000, page: 4, gearbox_limit: [15, 30, 60, 100]).limit).must_equal 100 + _(Pagy::Offset.new(count: 0, page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 + _(Pagy::Offset.new(count: 3, page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 + _(Pagy::Offset.new(count: 13, page: 2, gearbox_limit: [3, 10]).limit).must_equal 10 + _(Pagy::Offset.new(count: 103, page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 + _(Pagy::Offset.new(count: 103, page: 2, gearbox_limit: [3, 10]).limit).must_equal 10 + _(Pagy::Offset.new(count: 103, page: 3, gearbox_limit: [3, 10]).limit).must_equal 10 + _(Pagy::Offset.new(count: 103, page: 11, gearbox_limit: [3, 10]).limit).must_equal 10 end it 'can skips gearbox in Pagy::Offset::Countless' do - _(Pagy::Offset::Countless.new(page: 1, limit_requestable: true).limit).must_equal 20 - _(Pagy::Offset::Countless.new(page: 1, gearbox_extra: false).limit).must_equal 20 + _(Pagy::Offset::Countless.new(page: 1, maxable_limit: 100).limit).must_equal 20 + _(Pagy::Offset::Countless.new(page: 1).limit).must_equal 20 end it 'sets the limit in Pagy::Offset::Countless' do - _(Pagy::Offset::Countless.new(page: 1).limit).must_equal 15 - _(Pagy::Offset::Countless.new(page: 1).limit).must_equal 15 - _(Pagy::Offset::Countless.new(page: 2).limit).must_equal 30 - _(Pagy::Offset::Countless.new(page: 3).limit).must_equal 60 - _(Pagy::Offset::Countless.new(page: 4).limit).must_equal 100 - _(Pagy::Offset::Countless.new(page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 - _(Pagy::Offset::Countless.new(page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 - _(Pagy::Offset::Countless.new(page: 2, gearbox_limit: [3, 10]).limit).must_equal 10 - _(Pagy::Offset::Countless.new(page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 - _(Pagy::Offset::Countless.new(page: 2, gearbox_limit: [3, 10]).limit).must_equal 10 - _(Pagy::Offset::Countless.new(page: 3, gearbox_limit: [3, 10]).limit).must_equal 10 + _(Pagy::Offset::Countless.new(page: 1, gearbox_limit: [15, 30, 60, 100]).limit).must_equal 15 + _(Pagy::Offset::Countless.new(page: 2, gearbox_limit: [15, 30, 60, 100]).limit).must_equal 30 + _(Pagy::Offset::Countless.new(page: 3, gearbox_limit: [15, 30, 60, 100]).limit).must_equal 60 + _(Pagy::Offset::Countless.new(page: 4, gearbox_limit: [15, 30, 60, 100]).limit).must_equal 100 + _(Pagy::Offset::Countless.new(page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 + _(Pagy::Offset::Countless.new(page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 + _(Pagy::Offset::Countless.new(page: 2, gearbox_limit: [3, 10]).limit).must_equal 10 + _(Pagy::Offset::Countless.new(page: 1, gearbox_limit: [3, 10]).limit).must_equal 3 + _(Pagy::Offset::Countless.new(page: 2, gearbox_limit: [3, 10]).limit).must_equal 10 + _(Pagy::Offset::Countless.new(page: 3, gearbox_limit: [3, 10]).limit).must_equal 10 _(Pagy::Offset::Countless.new(page: 11, gearbox_limit: [3, 10]).limit).must_equal 10 end end describe '#assign_last' do it 'can skip gearbox for last' do - _(Pagy::Offset.new(count: 90, page: 1, limit_requestable: true).last).must_equal 5 + _(Pagy::Offset.new(count: 90, page: 1, maxable_limit: 100).last).must_equal 5 _(Pagy::Offset.new(count: 103, page: 1, gearbox_extra: false).last).must_equal 6 end it 'sets the last' do diff --git a/test/pagy/offset/overflow_test.rb b/test/pagy/offset/overflow_test.rb index fad659cfe..b92b9e4b8 100644 --- a/test/pagy/offset/overflow_test.rb +++ b/test/pagy/offset/overflow_test.rb @@ -8,24 +8,17 @@ DAY = 60 * 60 * 24 PERIOD = [Time.zone.local(2021, 11, 4), Time.zone.local(2021, 11, 4) + 10.days].freeze -Pagy::DEFAULT[:overflow] = :empty_page describe 'overflow' do - let(:pagy_vars) { { page: 100, limit: 10, count: 103 } } - let(:countless_vars) { { page: 100, limit: 10 } } - let(:calendar_vars) { { period: PERIOD, page: 100 } } + let(:pagy_vars) { { page: 100, limit: 10, count: 103, overflow: :empty_page } } + let(:countless_vars) { { page: 100, limit: 10, overflow: :empty_page} } + let(:calendar_vars) { { period: PERIOD, page: 100, overflow: :empty_page } } before do @pagy = Pagy::Offset.new(**pagy_vars) @pagy_calendar = Pagy::Offset::Calendar::Day.new(**calendar_vars) @pagy_countless = Pagy::Offset::Countless.new(**countless_vars).finalize(0) end - describe "variables" do - it 'has pagy_vars defaults' do - _(Pagy::DEFAULT[:overflow]).must_equal :empty_page # default for countless - end - end - describe "#overflow?" do it 'must be overflow?' do _(@pagy).must_be :overflow?