Skip to content

Commit

Permalink
Refactor Get module as Request class
Browse files Browse the repository at this point in the history
  • Loading branch information
ddnexus committed Mar 6, 2025
1 parent ddd1039 commit 22a7cd4
Show file tree
Hide file tree
Showing 32 changed files with 177 additions and 199 deletions.
35 changes: 18 additions & 17 deletions CHANGELOG-10.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ Your existing code will require several minor adjustments to function. Following

- If the `:params` variable was set to a lambda, ensure it directly modifies the passed `query_params`.
- The returned value is now ignored for a slightly better performance.
- The `:page_key` and `:limit_key` (legacy `:page_param` and `:limit_param`) are strings now, not symbols.
- The `:outset` and `:cycle` variables have been removed.
- They were seldom used, mostly useless, and implementing them in your own code is trivial.
- The `:anchor_string` variable has been removed
Expand All @@ -111,23 +112,23 @@ understanding.

{.compact}

| Type | Search (old) | Replace with (new) | Why? |
|-----------|----------------------|----------------------|---------------------------------------------------------------------------------------------------|
| Method | `pagy(...)` | `pagy(:offset, ...)` | Because it's explicit and consistent with the other paginators (however `:offset` can be omitted) |
| Function | `Pagy.root` | `Pagy::ROOT` | Because we don't need to call a method just to get a constant Pathname |
| Accessor | `pagy.vars` | `pagy.options` | Because they are actually `options` that don't change during execution |
| Accessor | `pagy.pages` | `pagy.last` | Because they are just an alias that we removed for simplicity |
| Exception | `VariableError` | `OptionError` | Because it's consistent with the `options` argument |
| Accessor | `e.variable` | `e.option` | Because it's consistent with its `OptionError` class |
| Naming | `*prev*` | `*previous*` | Because we don't use abbreviated words anymore (check: option, accessor, methods, CSS) |
| Option | `size: 7` | `slots: 7` | Because it's actually the number of page slots, and avoids confusion with other `size`s |
| Option | `ends: false` | `compact: true` | Because it's an opt-in option of the `series`, boolean inverse of `ends` |
| Option | `:count_args` | `:count_arguments` | Because we don't use abbreviated words anymore |
| Option | `:page_param` | `:page_sym` | Because `page_param` make people think "page param value" |
| Option | `:limit_param` | `:limit_sym` | Because `limit_param` make people think "limit param value" |
| Variable | `@pagy_locale = ...` | `Pagy::I18n = ...` | Because the `Pagy::I18n` API is now fully compatible with the `i18n` gem |
| Path | `'javascripts'` | `'javascript'` | Because it is the "javascript" dir, with one single file in a few formats |
| Path | `'stylesheets'` | `'stylesheet'` | Because it is the "stylescript" dir, with one single file in a few formats |
| Type | Search (old) | Replace with (new) | Why? |
|-----------|----------------------|----------------------|----------------------------------------------------------------------------------------------------|
| Method | `pagy(...)` | `pagy(:offset, ...)` | Because it's explicit and consistent with the other paginators (however `:offset` can be omitted). |
| Function | `Pagy.root` | `Pagy::ROOT` | Because we don't need to call a method just to get a constant Pathname. |
| Accessor | `pagy.vars` | `pagy.options` | Because they are actually `options` that don't change during execution. |
| Accessor | `pagy.pages` | `pagy.last` | Because they are just an alias that we removed for simplicity. |
| Exception | `VariableError` | `OptionError` | Because it's consistent with the `options` argument. |
| Accessor | `e.variable` | `e.option` | Because it's consistent with its `OptionError` class. |
| Naming | `*prev*` | `*previous*` | Because we don't use abbreviated words anymore (check: option, accessor, methods, CSS). |
| Option | `size: 7` | `slots: 7` | Because it's actually the number of page slots, and avoids confusion with other `size`s. |
| Option | `ends: false` | `compact: true` | Because it's an opt-in option of the `series`, boolean inverse of `ends`. |
| Option | `:count_args` | `:count_arguments` | Because we don't use abbreviated words anymore. |
| Option | `:page_param` | `:page_key` | Because `page_param` make people think "page param value". Value is a string now, not a symbol. |
| Option | `:limit_param` | `:limit_key` | Because `limit_param` make people think "limit param value". Value is a string now, not a symbol. |
| Variable | `@pagy_locale = ...` | `Pagy::I18n = ...` | Because the `Pagy::I18n` API is now fully compatible with the `i18n` gem |
| Path | `'javascripts'` | `'javascript'` | Because it is the "javascript" dir, with one single file in a few formats |
| Path | `'stylesheets'` | `'stylesheet'` | Because it is the "stylescript" dir, with one single file in a few formats |

##### Internal API

Expand Down
11 changes: 4 additions & 7 deletions docs/guides/how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,24 +428,21 @@ with a hash with the following keys:

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

Pagy rely also on the `params` method inside the app, which should be a hash of the params from the request. Define an alias or a
method if your environment doesn't respond to it.
- `:params` (e.g. a string-key hash of the request query params)

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

The `pagy` method is intended for environments that define both `params` and `request`, as it depends on them to set several options.
The `pagy` method needs to set a few options that depend on the availability of the `self.request` method in the class/module where you included it.

If you define a method (e.g., in a model) that uses `pagy`, the call will originate either from the controller or the view.
For example, if you call the `pagy` method for a model (that included the `Pagy::Method`), it would almost certainly not have the `request` method available.

The simplest way to make it work is as follows:

```ruby YourModel
include Pagy::Method

def self.paginated(view, my_arg1, my_arg2, **)
collection = my_method(my_arg1, my_arg2)
collection = ...
view.instance_eval { pagy(:offset, collection, **) }
end
```
Expand Down
8 changes: 4 additions & 4 deletions gem/javascript/pagy.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions gem/javascript/pagy.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion gem/javascript/pagy.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions gem/javascript/pagy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const Pagy = (() => {
}));
const B64SafeEncode = (unicode) => btoa(String.fromCharCode(...new TextEncoder().encode(unicode))).replace(/[+/=]/g, (m) => m == "+" ? "-" : m == "/" ? "_" : ""), B64Decode = (base64) => new TextDecoder().decode(Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)));
const randKey = () => Math.floor(Math.random() * 36 ** 3).toString(36);
const augmentKeynav = async (nav, [storageKey, pageSym, last, spliceArgs]) => {
const augmentKeynav = async (nav, [storageKey, pageKey, last, spliceArgs]) => {
let augment;
const browserKey = document.cookie.split(/;\s+/).find((row) => row.startsWith(pagy + "="))?.split("=")[1] ?? randKey();
document.cookie = pagy + "=" + browserKey;
Expand Down Expand Up @@ -58,8 +58,8 @@ const Pagy = (() => {
};
}
for (const a of nav.querySelectorAll("a[href]")) {
const url = a.href, re = new RegExp(`(?<=\\?.*)\\b${pageSym}=(\\d+)`);
a.href = url.replace(re, pageSym + "=" + augment(url.match(re)[1]));
const url = a.href, re = new RegExp(`(?<=\\?.*)\\b${pageKey}=(\\d+)`);
a.href = url.replace(re, pageKey + "=" + augment(url.match(re)[1]));
}
return augment;
};
Expand Down
2 changes: 1 addition & 1 deletion gem/lib/pagy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
class Pagy
VERSION = '9.3.3'
ROOT = Pathname.new(__dir__).parent.freeze
DEFAULT = { limit: 20, limit_sym: :limit, page_sym: :page }.freeze
DEFAULT = { limit: 20, limit_key: 'limit', page_key: 'page' }.freeze
PAGE_TOKEN = 'P '
LIMIT_TOKEN = 'L '
LABEL_TOKEN = 'L'
Expand Down
16 changes: 8 additions & 8 deletions gem/lib/pagy/classes/calendar/calendar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ def showtime
# Return the url for the calendar (shortest unit) page at time
def url_at(time, **)
conf = Marshal.load(Marshal.dump(@conf))
page_syms = {}
page_keys = {}
@units.inject(nil) do |object, unit|
conf[unit][:period] = object&.send(:active_period) || @period
conf[unit][:page] = page_syms[:"#{unit}_#{@page_sym}"] \
conf[unit][:page] = page_keys["#{unit}_#{@page_key}"] \
= create(unit, **conf[unit]).send(:page_at, time, **)
conf[unit][:params] ||= {}
conf[unit][:params].merge!(page_syms)
conf[unit][:params].merge!(page_keys)
create(unit, **conf[unit])
end.send(:compose_page_url, 1, **)
end
Expand All @@ -68,16 +68,16 @@ def init(conf, period, params)
@units = Calendar::UNITS & @conf.keys # get the units in time length desc order
@period = period
@params = params
@page_sym = conf[:offset][:page_sym] || Pagy::DEFAULT[:page_sym]
# set all the :page_sym options for later deletion
@units.each { |unit| conf[unit][:page_sym] = :"#{unit}_#{@page_sym}" }
@page_key = conf[:offset][:page_key] || Pagy::DEFAULT[:page_key]
# set all the :page_key options for later deletion
@units.each { |unit| conf[unit][:page_key] = "#{unit}_#{@page_key}" }
calendar = {}
object = nil
@units.each_with_index do |unit, index|
params_to_delete = @units[(index + 1), @units.length].map { |sub| conf[sub][:page_sym] } + [@page_sym]
params_to_delete = @units[(index + 1), @units.length].map { |sub| conf[sub][:page_key] } + [@page_key]
conf[unit][:params] = ->(up) { up.except!(*params_to_delete.map(&:to_s)) }
conf[unit][:period] = object&.send(:active_period) || @period
conf[unit][:page] = @params[:"#{unit}_#{@page_sym}"] # requested page
conf[unit][:page] = @params["#{unit}_#{@page_key}"] # requested page
# :nocov:
# simplecov doesn't need to fail block_given?
conf[unit][:counts] = yield(unit, conf[unit][:period]) if block_given?
Expand Down
2 changes: 1 addition & 1 deletion gem/lib/pagy/classes/keyset/keynav.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def assign_page
else
@page = @last = 1
end
@update = [storage_key, @options[:page_sym]]
@update = [storage_key, @options[:page_key]]
end

# Use a compound predicate to fetch the records
Expand Down
33 changes: 33 additions & 0 deletions gem/lib/pagy/classes/request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

class Pagy
class Request
def initialize(request, options = {})
@base_url, @path, @params = if request.is_a?(Hash)
request.values_at(:base_url, :path, :params)
else
[request.base_url, request.path, request.GET]
end
@jsonapi = @params['page'] && options[:jsonapi]
raise JsonapiReservedParamError, @params['page'] if @jsonapi && !@params['page'].respond_to?(:fetch)
end
attr_reader :base_url, :path, :params

# Get the page
def page(options, force_integer: true)
page_key = options[:page_key] || DEFAULT[:page_key]
page = @jsonapi ? @params['page'][page_key] : @params[page_key]
force_integer ? (page || 1).to_i : page
end

# Get the limit
def limit(options)
limit_key = options[:limit_key] || DEFAULT[:limit_key]
return options[:limit] || DEFAULT[:limit] \
unless options[:requestable_limit] &&
(requested_limit = @jsonapi ? @params['page'][limit_key] : @params[limit_key])

[requested_limit.to_i, options[:requestable_limit]].min
end
end
end
Loading

0 comments on commit 22a7cd4

Please sign in to comment.