Skip to content

Commit

Permalink
Allowing users to override the request path (#404)(squashed)
Browse files Browse the repository at this point in the history
  • Loading branch information
elliotlarson authored and ddnexus committed Dec 16, 2022
1 parent 02013d4 commit 6e939a0
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 44 deletions.
50 changes: 26 additions & 24 deletions docs/api/pagy.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,37 +107,39 @@ They are all integers:

### Other Variables

| Variable | Description | Default |
|:--------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------|
| `:size` | The size of the page links to show: array of initial pages, before current page, after current page, final pages. _(see also [How to control the page links](../how-to.md#control-the-page-links))_ | `[1,4,4,1]` |
| `:page_param` | The name of the page param name used in the url. _(see [How to customize the page param](../how-to.md#customize-the-page-param))_ | `:page` |
| `:params` | It can be a `Hash` of params to add to the URL, or a `Proc` that can edit/add/delete the request params _(see [How to customize the params](../how-to.md#customize-the-params))_ | `{}` |
| `:fragment` | The arbitrary fragment string (including the "#") to add to the url. _(see [How to customize the params](../how-to.md#customize-the-params))_ | `""` |
| `:link_extra` | The extra attributes string (formatted as a valid HTML attribute/value pairs) added to the page links _(see [How to customize the link attributes](../how-to.md#customize-the-link-attributes))_ | `""` |
| `:i18n_key` | The i18n key to lookup the `item_name` that gets interpolated in a few helper outputs _(see [How to customize the item name](../how-to.md#customize-the-item-name))_ | `"pagy.item_name"` |
| `:cycle` | Enable cycling/circular/infinite pagination: `true` sets `next` to `1` when the current page is the last page | `false` |
| Variable | Description | Default |
|:----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------|
| `:size` | The size of the page links to show: array of initial pages, before current page, after current page, final pages. _(see also [How to control the page links](/docs/how-to.md#control-the-page-links))_ | `[1,4,4,1]` |
| `:page_param` | The name of the page param name used in the url. _(see [How to customize the page param](/docs/how-to.md#customize-the-page-param))_ | `:page` |
| `:params` | It can be a `Hash` of params to add to the URL, or a `Proc` that can edit/add/delete the request params _(see [How to customize the params](/docs/how-to.md#customize-the-params))_ | `{}` |
| `:fragment` | The arbitrary fragment string (including the "#") to add to the url. _(see [How to customize the params](/docs/how-to.md#customize-the-params))_ | `""` |
| `:link_extra` | The extra attributes string (formatted as a valid HTML attribute/value pairs) added to the page links _(see [How to customize the link attributes](/docs/how-to.md#customize-the-link-attributes))_ | `""` |
| `:i18n_key` | The i18n key to lookup the `item_name` that gets interpolated in a few helper outputs (see [How to customize the item name](/docs/how-to.md#customize-the-item-name)) | `"pagy.item_name"` |
| `:cycle` | Enable cycling/circular/infinite pagination: `true` sets `next` to `1` when the current page is the last page | `false` |
| `:request_path` | Allows overriding the the request path for pagination links. If left blank, helpers will use `request.path`. | `""` |

There is no specific validation for non-instance variables.

### Attribute Readers

Pagy exposes all the instance variables needed for the pagination through a few attribute readers. They all return integers (or `nil`), except the `vars` hash:

| Reader | Description |
|:---------|:-------------------------------------------------------------------------------------------------------------------|
| `count` | The collection `:count` |
| `page` | The current page number |
| `items` | The requested number of items for the page |
| `pages` | The number of total pages in the collection (same as `last` but with cardinal meaning) |
| `in` | The number of the items in the page |
| `last` | The number of the last page in the collection (same as `pages` but with ordinal meaning) |
| `offset` | The number of items skipped from the collection in order to get the start of the current page (`:outset` included) |
| `from` | The collection-position of the first item in the page (`:outset` excluded) |
| `to` | The collection-position of the last item in the page (`:outset` excluded) |
| `prev` | The previous page number or `nil` if there is no previous page |
| `next` | The next page number or `nil` if there is no next page |
| `vars` | The variables hash |
| `params` | The `:params` variable (`Hash` or `Proc`) |
| Reader | Description |
|:---------------|:-------------------------------------------------------------------------------------------------------------------|
| `count` | The collection `:count` |
| `page` | The current page number |
| `items` | The requested number of items for the page |
| `pages` | The number of total pages in the collection (same as `last` but with cardinal meaning) |
| `in` | The number of the items in the page |
| `last` | The number of the last page in the collection (same as `pages` but with ordinal meaning) |
| `offset` | The number of items skipped from the collection in order to get the start of the current page (`:outset` included) |
| `from` | The collection-position of the first item in the page (`:outset` excluded) |
| `to` | The collection-position of the last item in the page (`:outset` excluded) |
| `prev` | The previous page number or `nil` if there is no previous page |
| `next` | The next page number or `nil` if there is no next page |
| `vars` | The variables hash |
| `params` | The `:params` variable (`Hash` or `Proc`) |
| `request_path` | The request path used for pagination helpers. If blank, helpers will use `request.path` |

### Lowest limit analysis

Expand Down
12 changes: 12 additions & 0 deletions docs/how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,18 @@ pagy = Pagy.new(count: 1000, link_extra: 'data-remote="true" class="my-class"')
**IMPORTANT**: For performance reasons, the `:link_extra` variable must be a string formatted as a valid HTML attribute/value pairs. That string will get inserted verbatim in the HTML of the link. _(see more advanced details in the [pagy_link_proc documentation](api/frontend.md#pagy_link_procpagy-link_extra))_
## Customize the request path
Let's say you are using something like a [Turbo frame](https://turbo.hotwired.dev/reference/frames) and you are loading a collection with pagination on a page where you want the pagination links to point to a different path. For example, if you are on a page like `/dashboard` and you have a collection of `foos` in a Turbo frame. By default Pagy will use `request.path` when constructing pagination links, which will results in links like `/dashboard?page=2`.

If you'd like to override the path so that the links point to a different endpoint than the current page's `request.path`, you can pass in the `request_path` variable:

```ruby
@pagy, @records = pagy(my_scope, vars { request_path: '/foos' })
```

This will result in pagination links like `/foos?page=2` instead of `/dashboard?page=2`.

## Customize the params

When you need to add some custom param or alter the params embedded in the URLs of the page links, you can set the variable `:params` to a `Hash` of params to add to the URL, or a `Proc` that can edit/add/delete the request params.
Expand Down
1 change: 1 addition & 0 deletions lib/config/pagy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
# Pagy::DEFAULT[:link_extra] = 'data-remote="true"' # example
# Pagy::DEFAULT[:i18n_key] = 'pagy.item_name' # default
# Pagy::DEFAULT[:cycle] = true # example
# Pagy::DEFAULT[:request_path] = "/foo" # example


# Extras
Expand Down
16 changes: 14 additions & 2 deletions lib/pagy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ def self.root
fragment: '',
link_extra: '',
i18n_key: 'pagy.item_name',
cycle: false }
cycle: false,
request_path: '' }

attr_reader :count, :page, :items, :vars, :pages, :last, :offset, :in, :from, :to, :prev, :next, :params
attr_reader :count, :page, :items, :vars, :pages, :last, :offset, :in, :from, :to, :prev, :next, :params, :request_path

# Merge and validate the options, do some simple arithmetic and set the instance variables
def initialize(vars)
Expand All @@ -34,6 +35,7 @@ def initialize(vars)
setup_pages_var
setup_offset_var
setup_params_var
setup_request_path_var
raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last

@from = [@offset - @outset + 1, @count].min
Expand Down Expand Up @@ -116,6 +118,16 @@ def setup_params_var
raise VariableError.new(self, :params, 'must be a Hash or a Proc', @params) \
unless (@params = @vars[:params]).is_a?(Hash) || @params.is_a?(Proc)
end

def setup_request_path_var
request_path = @vars[:request_path]
return if request_path.to_s.empty?

raise VariableError.new(self, :request_path, 'must be a bare path like "/foo"', request_path) \
if !request_path.start_with?('/') || request_path.include?('?')

@request_path = request_path
end
end

require 'pagy/backend'
Expand Down
3 changes: 2 additions & 1 deletion lib/pagy/url_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module UrlHelpers
# For non-rack environments you can use the standalone extra
def pagy_url_for(pagy, page, absolute: false, html_escaped: false)
vars = pagy.vars
request_path = vars[:request_path].to_s.empty? ? request.path : vars[:request_path]
page_param = vars[:page_param].to_s
items_param = vars[:items_param].to_s
params = pagy.params.is_a?(Hash) ? pagy.params.transform_keys(&:to_s) : {}
Expand All @@ -17,7 +18,7 @@ def pagy_url_for(pagy, page, absolute: false, html_escaped: false)
params = pagy.params.call(params) if pagy.params.is_a?(Proc)
query_string = "?#{Rack::Utils.build_nested_query(params)}"
query_string = query_string.gsub('&', '&') if html_escaped # the only unescaped entity
"#{request.base_url if absolute}#{request.path}#{query_string}#{vars[:fragment]}"
"#{request.base_url if absolute}#{request_path}#{query_string}#{vars[:fragment]}"
end
end
end
33 changes: 17 additions & 16 deletions test/pagy/extras/metadata_test.rb.rematch
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
---
"[1] pagy/extras/metadata::#pagy_metadata for Pagy#test_0004_returns only specific metadata":
:scaffold_url: "/foo?page=__pagy_page__"
"[1] pagy/extras/metadata::#pagy_metadata for Pagy::Calendar#test_0002_returns only specific metadata for Pagy::Calendar":
:scaffold_url: "/foo?month_page=__pagy_page__"
:page: 3
:count: 1000
:from: !ruby/object:ActiveSupport::TimeWithZone
utc: 2021-12-01 05:00:00.000000000 Z
zone: &1 !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: *1
time: 2022-01-01 00:00:00.000000000 Z
:prev: 2
:next: 4
:pages: 50
:pages: 26
"[1] pagy/extras/metadata::#pagy_metadata for Pagy#test_0002_returns the full pagy metadata":
:scaffold_url: "/foo?page=__pagy_page__"
:first_url: "/foo?page=1"
Expand All @@ -31,6 +39,7 @@
:link_extra: ''
:i18n_key: pagy.item_name
:cycle: false
:request_path: ''
:steps: false
:countless_minimal: false
:metadata:
Expand Down Expand Up @@ -101,18 +110,10 @@
- :prev
- :next
- :series
"[1] pagy/extras/metadata::#pagy_metadata for Pagy::Calendar#test_0002_returns only specific metadata for Pagy::Calendar":
:scaffold_url: "/foo?month_page=__pagy_page__"
"[1] pagy/extras/metadata::#pagy_metadata for Pagy#test_0004_returns only specific metadata":
:scaffold_url: "/foo?page=__pagy_page__"
:page: 3
:from: !ruby/object:ActiveSupport::TimeWithZone
utc: 2021-12-01 05:00:00.000000000 Z
zone: &1 !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: *1
time: 2022-01-01 00:00:00.000000000 Z
:count: 1000
:prev: 2
:next: 4
:pages: 26
:pages: 50
4 changes: 4 additions & 0 deletions test/pagy/frontend_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ def i18n_load(*locales)
_(app.pagy_url_for(pagy, 5, absolute: true)).must_equal "http://example.com:3000/foo?page=5&a=3&b=4#fragment"
_(app.pagy_url_for(pagy, 5, absolute: true, html_escaped: true)).must_equal "http://example.com:3000/foo?page=5&a=3&b=4#fragment"
end
it 'renders url with overridden path' do
pagy = Pagy.new count: 1000, page: 3, request_path: '/bar'
_(app.pagy_url_for(pagy, 5)).must_equal '/bar?page=5'
end
end

describe '#pagy_get_params and r' do
Expand Down
10 changes: 9 additions & 1 deletion test/pagy_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ def series_for(page, *expected)
_ { Pagy.new(count: 100, page: '11') }.must_raise Pagy::OverflowError
_ { Pagy.new(count: 100, page: 12) }.must_raise Pagy::OverflowError
_ { Pagy.new(count: 100, params: 12) }.must_raise Pagy::VariableError
_ { Pagy.new(count: 100, request_path: "http://example.com/foo") }.must_raise Pagy::VariableError
_ { Pagy.new(count: 100, request_path: "/foo?bar=1") }.must_raise Pagy::VariableError
_ { Pagy.new(count: 100, request_path: "foo") }.must_raise Pagy::VariableError
begin
Pagy.new(count: 100, page: 12)
rescue Pagy::OverflowError => e
Expand Down Expand Up @@ -288,12 +291,16 @@ def series_for(page, *expected)
_(pagy.prev).must_equal(9)
_(pagy.next).must_equal 1
end
it 'initializes the request_path' do
pagy = Pagy.new(count: 100, request_path: '/foo')
_(pagy.request_path).must_equal('/foo')
end
end

describe 'accessors' do
it 'has accessors' do
[
:count, :page, :items, :vars, # input
:count, :page, :items, :vars, :request_path, # input
:offset, :pages, :last, :from, :to, :in, :prev, :next, :series # output
].each do |meth|
_(pagy).must_respond_to meth
Expand All @@ -309,6 +316,7 @@ def series_for(page, *expected)
_(Pagy::DEFAULT[:size]).must_equal [1, 4, 4, 1]
_(Pagy::DEFAULT[:page_param]).must_equal :page
_(Pagy::DEFAULT[:params]).must_equal({})
_(Pagy::DEFAULT[:request_path]).must_equal('')
end
end

Expand Down

0 comments on commit 6e939a0

Please sign in to comment.