Skip to content

Commit

Permalink
grouped the variables :initial, :before, :after and :final into one s…
Browse files Browse the repository at this point in the history
…ingle :size array; #series can work with different sizes
  • Loading branch information
ddnexus committed Apr 10, 2018
1 parent 131e0bd commit 4788c3c
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 50 deletions.
11 changes: 5 additions & 6 deletions docs/api/pagy.md
Expand Up @@ -68,10 +68,7 @@ These are the core-variables (i.e. instance variables that define the pagination
| `:page` | the requested page number | `1` |
| `:items` | the _requested_ number of items for the page | `20` |
| `:outset` | the initial offset of the collection to paginate: pass it only if the collection was pre-offset(ted) | `0` |
| `:initial` | max pages to show from the first page | `1` |
| `:before` | max pages before the current page | `4` |
| `:after` | max pages after the current page | `4` |
| `:final` | max pages to show from the last page | `1` |
| `:size` | the size of the page links to show: array of initial pages, before current page, after current page, final pages. _(see also [Control the page links](/pagy/how-to#control-the-page-links))_ | `[1,4,4,1]` |

Pagy validates the core variables passed and raises an `ArgumentError` exception if any value is wrong. It also raises a specific `Pagy::OutOfRangeError` if the `:page` is out of range, so you can rescue from it and do whatever you think fits.

Expand Down Expand Up @@ -107,9 +104,11 @@ Pagy exposes all the instance variables needed for the pagination through a few
| `vars` | the non-core variables hash |


### series
### series(size=@vars[:size])

This method is the core of the pagination. It calculates, memoize and returns an array containing the page numbers and the `:gap` items to be rendered with the navigation links (e.g. `[1, :gap, 7, 8, "9", 10, 11, :gap, 36]`). The nav helpers and the templates basically loop through this array and render the correct item by simply checking its type:
This method is the core of the pagination. It returns an array containing the page numbers and the `:gap` items to be rendered with the navigation links (e.g. `[1, :gap, 7, 8, "9", 10, 11, :gap, 36]`). It accepts an optional `size` argument (mostly useful for extras), defaulted on the `:size` variable.

The nav helpers and the templates basically loop through this array and render the correct item by simply checking its type:

- if the item is an `Integer` then it is a page link
- if the item is a `String` then it is the current page
Expand Down
18 changes: 7 additions & 11 deletions docs/how-to.md
Expand Up @@ -108,32 +108,27 @@ __Notice__: If you need to get the page number from another param or in some dif

### Control the page links

You can control the number and position of page links in the navigation through the following variables.
You can control the number and position of page links in the navigation through the `:size` variables. It is an array of 4 integers that specify which and how many page link to show.

| Variable | Description | Default |
| ---: | :--- | :--- |
| `:initial` | max pages to show from the first page | `1` |
| `:before` | max pages before the current page | `4` |
| `:after` | max pages after the current page | `4` |
| `:final` | max pages to show from the last page | `1` |
The default is `[1,4,4,1]`, which means that you will get `1` initial page, `4` pages before the current page, `4` pages after the current page, and `1` final page

As usual you can set them as global defaults by using the `Pagy::VARS` hash or pass them directly to the `pagy` method.

The navigation links will contain the number of pages set in the variables:

`:initial`...`:before` `:page` `:after`...`:final`
`size[0]`...`size[1]` current page `size[2]`...`size[3]`

For example:

```ruby
pagy = Pagy.new count:1000, page: 10, initial: 3, final: 3 # etc
pagy = Pagy.new count:1000, page: 10, size: [3,4,4,3] # etc
pagy.series
#=> [1, 2, 3, :gap, 6, 7, 8, 9, "10", 11, 12, 13, 14, :gap, 48, 49, 50]
```

As you can see by the result of the `series` method, you get 3 explicitly requested`:initial` pages, 1 `:gap` (series interupted), the 4 default `:before` pages, the current `:page` (which is a string), the 4 default `:after` pages, another `:gap` and the 3 explicitly requested `:final` pages
As you can see by the result of the `series` method, you get 3 initial pages, 1 `:gap` (series interupted), 4 pages before the current page, the current `:page` (which is a string), 4 pages after the current page, another `:gap` and 3 final pages.

You can easily try different options (also asymmetrical) in a console: just check the `series` array to see what it contains when used with different core variables.
You can easily try different options (also asymmetrical) in a console by changing the `:size`: just check the `series` array to see what it contains when used in combination with different core variables.


### Using the `pagy_nav` and `pagy_nav_bootstrap` helpers
Expand Down Expand Up @@ -290,6 +285,7 @@ You can do so by setting the `:item_path` variable to the path to lookup in the
```

__Notice__: The variables passed to a pagy object have the precedence over the variables returned by the `pagy_get_vars`. The fastest way is passing a literal string to the `pagy` method, the most convenient way is using `pagy_get_vars`.
__Notice__: The variables passed to a pagy object have the precedence over the variables returned by the `pagy_get_vars`. The fastest way is passing a literal string to the `pagy` method, the most convenient way is using `pagy_get_vars`.

### Handling Pagy::OutOfRangeError exception

Expand Down
31 changes: 15 additions & 16 deletions lib/pagy.rb
Expand Up @@ -13,7 +13,7 @@ class OutOfRangeError < StandardError; end
def self.root; Pathname.new(__FILE__).dirname end

# default core vars
VARS = { items:20, outset:0, initial:1, before:4, after:4, final:1 }
VARS = { items:20, outset:0, size:[1,4,4,1] }

# default I18n vars
I18N = { file: Pagy.root.join('locales', 'pagy.yml').to_s, plurals: -> (c) {c==0 && 'zero' || c==1 && 'one' || 'other'} }
Expand All @@ -23,11 +23,11 @@ def self.root; Pathname.new(__FILE__).dirname end

# merge and validate the options, do some simple aritmetic and set the instance variables
def initialize(vars)
@vars = VARS.merge(vars) # default vars + instance vars
@vars = VARS.merge(vars) # default vars + instance vars
@vars[:page] = (@vars[:page]||1).to_i # set page to 1 if nil
{count:0, items:1, outset:0, initial:0, before:0, page:1, after:0, final:0}.each do |k,min|
{ count:0, items:1, outset:0, page:1 }.each do |k,min| # validate core variables
@vars[k] >= min rescue nil || raise(ArgumentError, "expected :#{k} >= #{min}; got #{@vars[k].inspect}")
instance_variable_set(:"@#{k}", @vars.delete(k)) # set all the core variables
instance_variable_set(:"@#{k}", @vars.delete(k)) # set instance variables
end
@pages = @last = [(@count.to_f / @items).ceil, 1].max # cardinal and ordinal meanings
(1..@last).cover?(@page) || raise(OutOfRangeError, "expected :page in 1..#{@last}; got #{@page.inspect}")
Expand All @@ -40,18 +40,17 @@ def initialize(vars)
end

# return the array of page numbers and :gap items e.g. [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
def series
@series ||= [].tap do |series|
[*0..@initial, *@page-@before..@page+@after, *@last-@final+1..@last+1].sort!.each_cons(2) do |a, b|
if a<0 || a==b || a>@last # skip out of range and duplicates
elsif a+1 == b; series.push(a) # no gap -> no additions
elsif a+2 == b; series.push(a, a+1) # 1 page gap -> fill with missing page
else series.push(a, :gap) # n page gap -> add :gap
end # skip the end boundary (last+1)
end
series.shift # remove the start boundary (0)
series[series.index(@page)] = @page.to_s # convert the current page to String
end
def series(size=@vars[:size])
(0..3).each{|i| size[i]>=0 rescue nil || raise(ArgumentError, "expected 4 items in :size >= 0; got #{size.inspect}")}
series = []
[*0..size[0], *@page-size[1]..@page+size[2], *@last-size[3]+1..@last+1].sort!.each_cons(2) do |a, b|
if a<0 || a==b || a>@last # skip out of range and duplicates
elsif a+1 == b; series.push(a) # no gap -> no additions
elsif a+2 == b; series.push(a, a+1) # 1 page gap -> fill with missing page
else series.push(a, :gap) # n page gap -> add :gap
end # skip the end boundary (last+1)
end # shift the start boundary (0) and
series.tap{|s| s.shift; s[s.index(@page)] = @page.to_s} # convert the current page to String
end

end
6 changes: 1 addition & 5 deletions test/metrics_test.rb
Expand Up @@ -3,11 +3,7 @@
class MetricsTest < Minitest::Test

def setup
@vars = { items: 10,
initial: 3,
final: 3,
before: 2,
after: 2 }
@vars = { items: 10, size: [3,2,2,3] }
@array = (1..103).to_a.extend(Pagy::Array::PageMethod)
end

Expand Down
15 changes: 3 additions & 12 deletions test/series_test.rb
Expand Up @@ -7,24 +7,15 @@ def setup

@vars0 = { count: 103,
items: 10,
initial: 0,
final: 0,
before: 2,
after: 2 }
size: [0,2,2,0] }

@vars1 = { count: 103,
items: 10,
initial: 3,
final: 3,
before: 0,
after: 0 }
size: [3,0,0,3] }

@vars2 = { count: 103,
items: 10,
initial: 3,
final: 0,
before: 2,
after: 0 }
size: [3,2,0,0] }

end

Expand Down

0 comments on commit 4788c3c

Please sign in to comment.