-
Notifications
You must be signed in to change notification settings - Fork 412
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added countless sub-class and extra code
- Loading branch information
Showing
9 changed files
with
187 additions
and
2 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
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,55 @@ | ||
--- | ||
title: Pagy::Countless | ||
--- | ||
# Pagy::Countless | ||
|
||
This is a `Pagy` [subclass](https://github.com/ddnexus/pagy/blob/master/lib/pagy/countless.rb) that provides pagination without the need of any `:count`. That may be especially useful for slow `COUNT(*)` query - result of large tables or poorly optimized DBs - or whenever you don't need the full set of pagination features. | ||
|
||
This class is providing support for extras that don't need the full set of pagination support or need to avoid the `:count` variable (e.g. the [countless extra](../extras/countless.md)). You should not need to use it directly because it is required and used internally. | ||
|
||
## Caveats | ||
|
||
In this class the `:count` variable is always `nil`, hence some feature that depends on it can have limited or no support: | ||
|
||
### Features with limited support | ||
|
||
#### :size variable and series method | ||
|
||
A couple if items of the `:size` array have some limitation. Regardless the actual `:size` value: | ||
|
||
- `vars[:size][2]` is capped at 1 | ||
- `vars[:size][3]` is set to 0 | ||
|
||
A few examples: | ||
|
||
- `[1,4,4,1]` would be treated like `[ 1,4,1,0]` | ||
- `[1,4,3,4]` would be treated like `[ 1,4,1,0]` | ||
- `[1,4,0,0]` would be treated like `[ 1,4,0,0]` | ||
|
||
The `series` method reflects on the above. | ||
|
||
#### :overflow variable | ||
|
||
The available values for the `:overflow` variable are `:empty_page` and `:exception`, missing `:last_page` | ||
|
||
### Features witout support | ||
|
||
The `pagy_info` and all the `pagy_nav_compact*` helpers are not supported. | ||
|
||
## How countless pagination works | ||
|
||
Instead of basing all the internal calculations on the `:count` variable (passed with the constructor), this class uses the number of actually retrieved items for the page (passed in a second step with the `finalize` method), in order to deduce if there is a `next` page, or if the current page is the `last` page, or if the current request should raise a `Pagy::OverflowError` exception. | ||
|
||
The trick is retrieving `items + 1`, and using the resulting number to calculate the variables, while eventually removing the extra item from the result. (see the [countless.rb extra](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/countless.rb)) | ||
|
||
## Methods | ||
|
||
The construction of the `Pagy::Countless` object is splitted into 2 steps: the regular `initialize` method and the `finalize` method, which will use the retrieved items number to calculate the rest of the pagination integers. | ||
|
||
### Pagy::Countless.new(vars) | ||
|
||
The initial constructor takes the usual hash of variables, calculating only the requested `items` and the `offset`, useful to query the page of items. | ||
|
||
### finalize(items) | ||
|
||
The actual calculation of all the internal variables for the pagination is calculated using the `items` number argument. The method returns the finalized instance object. |
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,49 @@ | ||
--- | ||
title: Countless | ||
--- | ||
# Countless Extra | ||
|
||
This extra uses the `Pagy::Countless` subclass in order to avoid to execute an otherwise needed count query. It is especially useful when used with large DB tables, where [Caching the count](../how-to.md#caching-the-count) may not be an option. | ||
|
||
Its usage is practically the same as the regular `Pagy::Backend` module (see the [backend doc](../api/backend.md). | ||
|
||
The pagination resulting from this extra has some limitation as documented in the [Pagy::Countless Caveats doc](../api/countless.md#caveats). | ||
|
||
## Synopsys | ||
|
||
See [extras](../extras.md) for general usage info. | ||
|
||
In the `pagy.rb` initializer: | ||
|
||
```ruby | ||
require 'pagy/extras/countless' | ||
``` | ||
|
||
In a controller: | ||
|
||
```ruby | ||
@pagy, @records = pagy_countless(some_scope, ...) | ||
``` | ||
|
||
## Files | ||
|
||
This extra is composed of 1 file: | ||
|
||
- [countless.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/countless.rb) | ||
|
||
## Methods | ||
|
||
All the methods in this module are prefixed with the `"pagy_countless"` string, to avoid any possible conflict with your own methods when you include the module in your controller. They are also all private, so they will not be available as actions. The methods prefixed with the `"pagy_countless_get_"` string are sub-methods/getter methods that are intended to be overridden, not used directly. | ||
|
||
### pagy_countless(collection, vars=nil) | ||
|
||
This method is the same as the generic `pagy` method. (see the [pagy doc](../api/backend.md#pagycollection-varsnil)) | ||
|
||
### pagy_countless_get_vars(_collection, vars) | ||
|
||
This sub-method is similar to the `pagy_get_vars` sub-method, but it is called only by the `pagy_countless` method. (see the [pagy_get_vars doc](../api/backend.md#pagy_get_varscollection-vars)). | ||
|
||
### pagy_countless_get_items(collection, pagy) | ||
|
||
This sub-method is the same as the `pagy_get_items` sub-method, but it is called only by the `pagy_countless` method. (see the [pagy_get_items doc](../api/backend.md#pagy_get_itemscollection-pagy)). | ||
|
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,30 @@ | ||
require 'pagy' | ||
|
||
class Pagy | ||
|
||
class Countless < Pagy | ||
|
||
# Merge and validate the options, do some simple aritmetic and set a few instance variables | ||
def initialize(vars={}) | ||
@vars ||= VARS.merge(vars.delete_if{|_,v| v.nil? || v == '' }) # default vars + cleaned vars (can be ovverridden) | ||
{ items:1, outset:0, page:1 }.each do |k,min| # validate instance variables | ||
(@vars[k] && instance_variable_set(:"@#{k}", @vars[k].to_i) >= min) \ | ||
or raise(ArgumentError, "expected :#{k} >= #{min}; got #{@vars[k].inspect}") | ||
end | ||
@offset = @items * (@page - 1) + @outset # pagination offset + outset (initial offset) | ||
end | ||
|
||
# Finalize the instance variables based on the retrieved items | ||
def finalize(items) | ||
items == 0 && @page > 1 and raise(OverflowError.new(self), "page #{@page} got no items") | ||
@pages = @last = (items > @items ? @page + 1 : @page) # set the @pages and @last | ||
@items = items if items < @items && items > 0 # adjust items for last non-empty page | ||
@from = items == 0 ? 0 : @offset+1 - @outset # page begins from item | ||
@to = items == 0 ? 0 : @offset + @items - @outset # page ends to item | ||
@prev = (@page-1 unless @page == 1) # nil if no prev page | ||
@next = (@page+1 unless @page == @last) # nil if no next page | ||
self | ||
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,35 @@ | ||
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/countless | ||
|
||
require 'pagy/countless' | ||
|
||
class Pagy | ||
|
||
# used by the items extra | ||
COUNTLESS = true | ||
|
||
module Backend ; private # the whole module is private so no problem with including it in a controller | ||
|
||
# Return Pagy object and items | ||
def pagy_countless(collection, vars={}) | ||
pagy = Pagy::Countless.new(pagy_countless_get_vars(collection, vars)) | ||
return pagy, pagy_countless_get_items(collection, pagy) | ||
end | ||
|
||
# Sub-method called only by #pagy_countless: here for easy customization of variables by overriding | ||
def pagy_countless_get_vars(_collection, vars) | ||
# Return the merged variables to initialize the Pagy object | ||
{ page: params[vars[:page_param]||VARS[:page_param]] }.merge!(vars) | ||
end | ||
|
||
# Sub-method called only by #pagy_countless: here for easy customization of record-extraction by overriding | ||
def pagy_countless_get_items(collection, pagy) | ||
# This should work with ActiveRecord, Sequel, Mongoid... | ||
items = collection.offset(pagy.offset).limit(pagy.items + 1).to_a | ||
items_size = items.size | ||
items.pop if items_size == pagy.items + 1 | ||
pagy.finalize(items_size) # finalize may adjust pagy.items, so must be used after checking the size | ||
items | ||
end | ||
|
||
end | ||
end |