Skip to content

Commit

Permalink
WIP: Static frozen Pagy::DEFAULT
Browse files Browse the repository at this point in the history
  • Loading branch information
ddnexus committed Jan 20, 2025
1 parent 049814b commit ae31038
Show file tree
Hide file tree
Showing 38 changed files with 245 additions and 316 deletions.
12 changes: 6 additions & 6 deletions docs/api/keyset_for_ui.md → docs/api/keynav.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.<br/>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.<br/>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

Expand Down
4 changes: 2 additions & 2 deletions docs/extras/keyset_for_ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
8 changes: 3 additions & 5 deletions gem/apps/demo.ru
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,18 @@ 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'
end

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
Expand All @@ -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
Expand Down
15 changes: 6 additions & 9 deletions gem/apps/keynav.ru
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down
6 changes: 2 additions & 4 deletions gem/apps/keyset.ru
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
6 changes: 2 additions & 4 deletions gem/apps/keyset_sequel.ru
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
22 changes: 12 additions & 10 deletions gem/apps/rails.ru
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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
Expand All @@ -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
Expand Down
7 changes: 3 additions & 4 deletions gem/apps/repro.ru
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
34 changes: 9 additions & 25 deletions gem/config/pagy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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[...] = ...
4 changes: 2 additions & 2 deletions gem/lib/pagy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 '
Expand Down
13 changes: 5 additions & 8 deletions gem/lib/pagy/backend.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion gem/lib/pagy/backend/constructors/offset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 5 additions & 7 deletions gem/lib/pagy/backend/helpers/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
8 changes: 3 additions & 5 deletions gem/lib/pagy/backend/helpers/metadata.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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|
Expand Down
7 changes: 6 additions & 1 deletion gem/lib/pagy/console.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit ae31038

Please sign in to comment.