-
Notifications
You must be signed in to change notification settings - Fork 411
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
318 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
--- | ||
title: Standalone | ||
--- | ||
# Standalone Extra | ||
|
||
This extra allows you to use pagy completely standalone, i.e. without any request object, nor Rack environment/gem, nor any defined `params` method, even in the irb/rails console witout an app. | ||
|
||
You may need it in order to paginate a collection outside of a regular rack request or controller, like in an unconventional API module, or in the irb/rails console or for testing/playing with backend and frontend methods. | ||
|
||
You trigger the standalone mode by setting an `:url` variable, which will be used directly and verbatim, instead of extracting it from the `request` `Rack::Request` object. You can also pass other params by using the `:params` variable as usual. That will be used to produce the final URLs in the usual way. | ||
|
||
This extra will also create a dummy `params` method (if not already defined) in the module where you will include the `Pagy::Backend` (usually a controller). | ||
|
||
## Synopsis | ||
|
||
See [extras](../extras.md) for general usage info. | ||
|
||
In the `pagy.rb` initializer: | ||
|
||
```ruby | ||
require 'pagy/extras/standalone' | ||
|
||
# optional: set a default url | ||
Pagy::Vars[:url] = 'http://www.example.com/subdir' | ||
|
||
# pass a :url variable to work in standalone mode (no need of any request object nor Rack env) | ||
@pagy, @records = pagy(Product.all, url: 'http://www.example.com/subdir', params: {...}) | ||
``` | ||
|
||
In a console, even without any app nor initializer: | ||
|
||
```ruby | ||
require 'pagy' | ||
require 'pagy/extras/standalone' | ||
include Pagy::Console | ||
pagy_extras :array, :metadata, ... | ||
|
||
### then you can use it like inside an app | ||
pagy, items = pagy_array((1..1000).to_a, page: 3) | ||
pagy_navs(pagy) | ||
=> "<nav class=\"pagy-nav pagination\" role=\"navigation\" aria-label=\"pager\"><span class=\"page prev\"><a href=\"http://www.example.com/subdir?page=2&items=20\" rel=\"prev\" aria-label=\"previous\">‹ Prev</a></span> <span class=\"page\"><a href=\"http://www.example.com/subdir?page=1&items=20\" >1</a></span> <span class=\"page\"><a href=\"http://www.example.com/subdir?page=2&items=20\" rel=\"prev\" >2</a></span> <span class=\"page active\">3</span> <span class=\"page\"><a href=\"http://www.example.com/subdir?page=4&items=20\" rel=\"next\" >4</a></span> <span class=\"page\"><a href=\"http://www.example.com/subdir?page=5&items=20\" >5</a></span> <span class=\"page\"><a href=\"http://www.example.com/subdir?page=6&items=20\" >6</a></span> <span class=\"page\"><a href=\"http://www.example.com/subdir?page=7&items=20\" >7</a></span> <span class=\"page gap\">…</span> <span class=\"page\"><a href=\"http://www.example.com/subdir?page=50&items=20\" >50</a></span> <span class=\"page next\"><a href=\"http://www.example.com/subdir?page=4&items=20\" rel=\"next\" aria-label=\"next\">Next ›</a></span></nav>" | ||
|
||
pagy_metadata(pagy) | ||
=> | ||
{:scaffold_url=>"http://www.example.com/subdir?page=__pagy_page__", | ||
:first_url=>"http://www.example.com/subdir?page=1", | ||
:prev_url=>"http://www.example.com/subdir?page=2", | ||
:page_url=>"http://www.example.com/subdir?page=3", | ||
:next_url=>"http://www.example.com/subdir?page=4", | ||
:last_url=>"http://www.example.com/subdir?page=50", | ||
:count=>1000, | ||
:page=>3, | ||
:items=>20, | ||
:vars=> | ||
{:page=>3, | ||
:items=>20, | ||
:outset=>0, | ||
:size=>[1, 4, 4, 1], | ||
... | ||
``` | ||
|
||
## Files | ||
|
||
- [standalone.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/standalone.rb) | ||
|
||
## Variables | ||
|
||
| Variable | Description | Default | | ||
|:---------|:-----------------------------------------|:--------| | ||
| `:url` | url string (can be absolute or relative) | `nil` | | ||
|
||
You can use the `:params` variable to add params to the final URLs. | ||
|
||
## Methods | ||
|
||
### Overridden pagy_url_for | ||
|
||
The `standalone` extra overrides the `pagy_url_for` method used internally. If it finds a set `:url` variable it assumes there is no `request` object, so it uses the `:url` var verbatim to produce the final URL, only adding the query string, composed by merging the `:page` param to the `:params` variable. If there is no `:url` variable set it works like usual, i.e. it uses the rake `request` object to extract the base_url, path from the request, merging the params returned from the `params` controller method, the `:params` variable and the `:page` param to it. | ||
|
||
### Dummy params method | ||
|
||
This extra creates a dummy `params` method (if not already defined) in the module where you will include the `Pagy::Backend` (usually a controller). The method is called by pagy to retrive backend variables coming from the request, and expects a hash, so the dummy param method returns an empty hash avoiding an error. | ||
|
||
## Pagy::Console module | ||
|
||
Include it in your console window to include `Pagy::Backend`, `Pagy::Frontend` and set a dummy default `:url` variable. | ||
|
||
### pagy_extras(*extras) | ||
|
||
Simple utility method to save some typing in the console. It will require the extras arguments: | ||
|
||
```ruby | ||
pagy_extra :array, :bootstrap, :support, :headers, ... | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/standalone | ||
# frozen_string_literal: true | ||
|
||
require 'uri' | ||
class Pagy | ||
|
||
# extracted from Rack::Utils and reformatted for rubocop | ||
module QueryUtils | ||
module_function | ||
def escape(str) | ||
URI.encode_www_form_component(str) | ||
end | ||
def build_nested_query(value, prefix = nil) | ||
case value | ||
when Array | ||
value.map { |v| build_nested_query(v, "#{prefix}[]") }.join('&') | ||
when Hash | ||
value.map { |k, v| build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) }.delete_if(&:empty?).join('&') | ||
when nil | ||
prefix | ||
else | ||
raise ArgumentError, 'value must be a Hash' if prefix.nil? | ||
"#{prefix}=#{escape(value)}" | ||
end | ||
end | ||
end | ||
|
||
module UseStandaloneExtra | ||
# without any :url var it works exactly as the regular #pagy_url_for; | ||
# with a defined :url variable it does not use rack/request | ||
def pagy_url_for(pagy, page, deprecated_url=nil, absolute: nil) | ||
absolute = Pagy.deprecated_arg(:url, deprecated_url, :absolute, absolute) if deprecated_url | ||
pagy, page = Pagy.deprecated_order(pagy, page) if page.is_a?(Pagy) | ||
p_vars = pagy.vars | ||
if p_vars[:url] | ||
url_string = p_vars[:url] | ||
params = {} | ||
else | ||
url_string = "#{request.base_url if absolute}#{request.path}" | ||
params = request.GET | ||
end | ||
params = params.merge(p_vars[:params]) | ||
params[p_vars[:page_param].to_s] = page | ||
params[p_vars[:items_param].to_s] = p_vars[:items] if defined?(UseItemsExtra) | ||
query_string = "?#{QueryUtils.build_nested_query(pagy_get_params(params))}" unless params.empty? | ||
"#{url_string}#{query_string}#{p_vars[:fragment]}" | ||
end | ||
end | ||
Helpers.prepend UseStandaloneExtra | ||
|
||
# defines a dummy #params method if not already defined in the including module | ||
module Backend | ||
def self.included(controller) | ||
controller.define_method(:params){{}} unless controller.method_defined?(:params) | ||
end | ||
end | ||
|
||
# include Pagy::Console in irb/rails console for a ready to use pagy environment | ||
module Console | ||
def self.included(main) | ||
main.include(Backend) | ||
main.include(Frontend) | ||
VARS[:url] = 'http://www.example.com/subdir' | ||
end | ||
|
||
def pagy_extras(*extras) | ||
extras.each {|extra| require "pagy/extras/#{extra}"} | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative '../../test_helper' | ||
require 'pagy/extras/standalone' | ||
|
||
module PagyConsole | ||
include Pagy::Console | ||
# we are not in the console so we need module_function | ||
module_function :pagy_extras | ||
end | ||
|
||
describe 'pagy/extras/standalone_console' do | ||
|
||
describe 'Pagy::Console' do | ||
it 'defines default :url' do | ||
_(Pagy::VARS[:url]).must_equal 'http://www.example.com/subdir' | ||
end | ||
it 'includes Pagy::Backend and Pagy::Frontend' do | ||
_(PagyConsole <= Pagy::Backend).must_equal true | ||
_(PagyConsole <= Pagy::Frontend).must_equal true | ||
end | ||
it 'requires extras' do | ||
PagyConsole.pagy_extras :array, :navs | ||
_(Pagy::Backend.method_defined?(:pagy_array)) | ||
_(Pagy::Frontend.method_defined?(:pagy_nav_js)) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative '../../test_helper' | ||
require 'pagy/extras/standalone' | ||
|
||
class EmptyController | ||
include Pagy::Backend | ||
end | ||
class FilledController | ||
def params | ||
{a: 'a', b: 'b'} | ||
end | ||
include Pagy::Backend | ||
end | ||
|
||
describe 'pagy/extras/standalone' do | ||
let(:view) { MockView.new } | ||
|
||
describe 'defines #params if missing' do | ||
it 'defines a dummy #params' do | ||
_(EmptyController.new.params).must_equal({}) | ||
end | ||
it 'does not define a dummy #params' do | ||
_(FilledController.new.params).must_equal({a: 'a', b: 'b'}) | ||
end | ||
end | ||
|
||
describe '#pagy_url_for' do | ||
|
||
it 'renders basic url' do | ||
pagy = Pagy.new count: 1000, page: 3 | ||
_(view.pagy_url_for(pagy, 5)).must_equal '/foo?page=5' | ||
_(view.pagy_url_for(pagy, 5, absolute: true)).must_equal 'http://example.com:3000/foo?page=5' | ||
pagy = Pagy.new count: 1000, page: 3, url: 'http://www.pagy-standalone.com/subdir' | ||
_(view.pagy_url_for(pagy, 5)).must_equal 'http://www.pagy-standalone.com/subdir?page=5' | ||
_(view.pagy_url_for(pagy, 5, absolute: true)).must_equal 'http://www.pagy-standalone.com/subdir?page=5' | ||
pagy = Pagy.new count: 1000, page: 3, url: '' | ||
_(view.pagy_url_for(pagy, 5)).must_equal '?page=5' | ||
_(view.pagy_url_for(pagy, 5, absolute: true)).must_equal '?page=5' | ||
end | ||
|
||
it 'renders url with params' do | ||
pagy = Pagy.new count: 1000, page: 3, params: {a: 3, b: 4} | ||
_(view.pagy_url_for(pagy, 5)).must_equal '/foo?page=5&a=3&b=4' | ||
_(view.pagy_url_for(pagy, 5, absolute: true)).must_equal 'http://example.com:3000/foo?page=5&a=3&b=4' | ||
pagy = Pagy.new count: 1000, page: 3, params: {a: 3, b: 4}, url: 'http://www.pagy-standalone.com/subdir' | ||
_(view.pagy_url_for(pagy, 5)).must_equal "http://www.pagy-standalone.com/subdir?a=3&b=4&page=5" | ||
_(view.pagy_url_for(pagy, 5, absolute: true)).must_equal "http://www.pagy-standalone.com/subdir?a=3&b=4&page=5" | ||
pagy = Pagy.new count: 1000, page: 3, params: {a: 3, b: 4}, url: '' | ||
_(view.pagy_url_for(pagy, 5)).must_equal "?a=3&b=4&page=5" | ||
_(view.pagy_url_for(pagy, 5, absolute: true)).must_equal "?a=3&b=4&page=5" | ||
end | ||
it 'renders url with fragment' do | ||
pagy = Pagy.new count: 1000, page: 3, fragment: '#fragment' | ||
_(view.pagy_url_for(pagy, 6)).must_equal '/foo?page=6#fragment' | ||
_(view.pagy_url_for(pagy, 6, absolute: true)).must_equal 'http://example.com:3000/foo?page=6#fragment' | ||
pagy = Pagy.new count: 1000, page: 3, fragment: '#fragment', url: 'http://www.pagy-standalone.com/subdir' | ||
_(view.pagy_url_for(pagy, 6)).must_equal 'http://www.pagy-standalone.com/subdir?page=6#fragment' | ||
_(view.pagy_url_for(pagy, 6, absolute: true)).must_equal 'http://www.pagy-standalone.com/subdir?page=6#fragment' | ||
pagy = Pagy.new count: 1000, page: 3, fragment: '#fragment', url: '' | ||
_(view.pagy_url_for(pagy, 6)).must_equal '?page=6#fragment' | ||
_(view.pagy_url_for(pagy, 6, absolute: true)).must_equal '?page=6#fragment' | ||
end | ||
it 'renders url with params and fragment' do | ||
pagy = Pagy.new count: 1000, page: 3, params: {a: 3, b: 4}, fragment: '#fragment' | ||
_(view.pagy_url_for(pagy, 5)).must_equal '/foo?page=5&a=3&b=4#fragment' | ||
_(view.pagy_url_for(pagy, 5, absolute: true)).must_equal 'http://example.com:3000/foo?page=5&a=3&b=4#fragment' | ||
pagy = Pagy.new count: 1000, page: 3, params: {a: [1,2,3]}, fragment: '#fragment', url: 'http://www.pagy-standalone.com/subdir' | ||
_(view.pagy_url_for(pagy, 5)).must_equal "http://www.pagy-standalone.com/subdir?a[]=1&a[]=2&a[]=3&page=5#fragment" | ||
_(view.pagy_url_for(pagy, 5, absolute: true)).must_equal "http://www.pagy-standalone.com/subdir?a[]=1&a[]=2&a[]=3&page=5#fragment" | ||
pagy = Pagy.new count: 1000, page: 3, params: {a: nil}, fragment: '#fragment', url: '' | ||
_(view.pagy_url_for(pagy, 5)).must_equal "?a&page=5#fragment" | ||
_(view.pagy_url_for(pagy, 5, absolute: true)).must_equal "?a&page=5#fragment" | ||
end | ||
end | ||
end |