pagy 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +74 -12
- data/images/efficiency-table.png +0 -0
- data/images/ips-chart.png +0 -0
- data/images/memory-chart.png +0 -0
- data/images/objects-chart.png +0 -0
- data/lib/locales/pagy.yml +4 -11
- data/lib/pagy.rb +38 -42
- data/lib/pagy/backend.rb +14 -32
- data/lib/pagy/frontend.rb +48 -113
- data/lib/templates/nav.html.erb +22 -0
- data/lib/templates/{default.html.haml → nav.html.haml} +8 -10
- data/lib/templates/{default.html.slim → nav.html.slim} +7 -8
- data/lib/templates/nav_bootstrap.html.erb +24 -0
- data/lib/templates/{bootstrap.html.haml → nav_bootstrap.html.haml} +8 -9
- data/lib/templates/{bootstrap.html.slim → nav_bootstrap.html.slim} +8 -10
- data/pagy.gemspec +4 -4
- metadata +17 -12
- data/lib/templates/bootstrap.html.erb +0 -28
- data/lib/templates/default.html.erb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a984f9eb44e5f2d637f92e5b5317edbff0691c2b2d4e89d8bf02f2e1a11939b8
|
4
|
+
data.tar.gz: 01dc6a1f9ca9bba43400f00a645a47c02cf4aa11868705193a9b4c33a50e1811
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2ed0782436c442bb3127598cb1f8b8aaeb7afaf373836933e61a93b834bc91125a1f44fb34fad18b362a989c59b2c1ba3683483cf2098b36657be75420f3fc3
|
7
|
+
data.tar.gz: e7399e4568cf8fbfa537a4a193c928d0810ce47ea4d5c877765d5669d077618e7ab1e715d9892c7bf51e8e827a06b897afef12d417c2994f7be6f65c69cd7fe1
|
data/README.md
CHANGED
@@ -1,30 +1,92 @@
|
|
1
1
|
# Pagy
|
2
2
|
|
3
|
-
|
3
|
+
Pagy is the ultimate pagination gem that outperforms the others in each and every benchmark and comparison.
|
4
4
|
|
5
|
-
|
5
|
+
### Benchmarks
|
6
6
|
|
7
|
-
|
7
|
+
The best way to quickly get an idea about its features is comparing it to the other well known gems.
|
8
8
|
|
9
|
-
|
9
|
+
The values shown in the charts below have been recorded while each gem was producing the exact same output: same environment conditions, same task, just different gems _(see the complete [Gems Comparison](http://ddnexus.github.io/pagination-comparison/gems.html))_
|
10
10
|
|
11
|
-
|
11
|
+
#### Pagy is a lot faster
|
12
|
+
|
13
|
+

|
14
|
+
|
15
|
+
#### Pagy uses a lot less memory
|
16
|
+
|
17
|
+

|
18
|
+
|
19
|
+
#### Pagy is a lot simpler
|
20
|
+
|
21
|
+

|
22
|
+
|
23
|
+
#### Pagy is a lot more efficient
|
24
|
+
|
25
|
+

|
26
|
+
|
27
|
+
_The [IPS/Kb ratio](http://ddnexus.github.io/pagination-comparison/gems.html#efficiency-ratio) is calculated out of speed (IPS) and Memory (Kb): it shows how well each gem uses any Kb of memory it allocates/consumes._
|
28
|
+
|
29
|
+
#### Pagy does not suffer the typical limitations of the other gems:
|
30
|
+
|
31
|
+
- it works with collections/scopes that already used `limit` and `offset`
|
32
|
+
- it works with both helpers or templates (your choice)
|
33
|
+
- it raises real `Pagy::OutOfRangeError` exceptions that you can rescue from
|
34
|
+
- it does not impose any difficult-to-override logic or output
|
35
|
+
|
36
|
+
### Features
|
37
|
+
|
38
|
+
#### Straightforward code
|
39
|
+
|
40
|
+
- Pagy is just ~120 lines of simple ruby, organized in 3 flat modules very easy to understand and use
|
41
|
+
- it produces its own HTML, URLs, pluralization and interpolation with its own specialized and fast code
|
42
|
+
- 100% of its methods are public API, accessible and overridable **right where you use them** (no need of monkey patching or subclassing)
|
43
|
+
|
44
|
+
#### Totally agnostic
|
45
|
+
|
46
|
+
- it doesn't need to know anything about your models, ORM or Storage, so it doesn't add any code to them
|
47
|
+
- it works with all kind of collections, even pre-paginated, records, Arrays, JSON data... and just whatever you can count
|
48
|
+
- it works with all Rack frameworks (Rails, Sinatra, Padrino, ecc.) out of the box
|
49
|
+
- it works with any possible non-Rack envoronment by just overriding one or two one-liner methods
|
50
|
+
|
51
|
+
#### Simple Usage
|
52
|
+
|
53
|
+
You can use pagy in a quite familiar way:
|
54
|
+
|
55
|
+
Paginate your collection in some controller:
|
12
56
|
|
13
57
|
```ruby
|
14
|
-
|
58
|
+
@pagy, @records = pagy(Product.some_scope)
|
59
|
+
```
|
60
|
+
|
61
|
+
Render the navigation links with a super-fast helper in some view:
|
62
|
+
|
63
|
+
```HTML+ERB
|
64
|
+
<%= pagy_nav(@pagy) %>
|
65
|
+
```
|
66
|
+
|
67
|
+
Or - if you prefer - render the navigation links with a template:
|
68
|
+
|
69
|
+
```HTML+ERB
|
70
|
+
<%= render 'pagy/nav', locals: {pagy: @pagy} %>
|
15
71
|
```
|
16
72
|
|
17
|
-
|
73
|
+
## Support, Comments and Feature Requests
|
74
|
+
|
75
|
+
[](https://gitter.im/ruby-pagy/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
18
76
|
|
19
|
-
|
77
|
+
## Useful Links
|
20
78
|
|
21
|
-
|
79
|
+
- [Documentation](https://ddnexus.github.io/pagy/index)
|
80
|
+
- [Pagination Comparison App](http://github.com/ddnexus/pagination-comparison)
|
22
81
|
|
23
|
-
|
82
|
+
## Help Wanted
|
24
83
|
|
25
|
-
|
84
|
+
Pagy is a fresh project and your help would be great. If you like it, you have a few options to contribute:
|
26
85
|
|
27
|
-
|
86
|
+
- write a tutorial or a post or even just a tweet (pagy is young and needs to be known)
|
87
|
+
- write a "How To" topic (the documentation is covering the basics and there is a lot of space for additions)
|
88
|
+
- submit a pull request to make pagy even faster, save more memory or improve its usability
|
89
|
+
- create an issue if anything should be improved/fixed
|
28
90
|
|
29
91
|
## License
|
30
92
|
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/locales/pagy.yml
CHANGED
@@ -1,17 +1,10 @@
|
|
1
|
-
#
|
2
|
-
# into this file at Pagy.root.join('locale', 'pagy.yml')
|
3
|
-
# If you want to customize it you should set:
|
4
|
-
# Pagy::Opts.i18n_file = 'path/to/your/file.yaml'
|
5
|
-
|
6
|
-
# If you need to use this method with pluralization different than english
|
7
|
-
# (i.e. not 'zero', 'one', 'other' plurals) then you should define the
|
8
|
-
# Pagy::Opts.i18n_plurals proc to return the plural key based on the passed count.
|
1
|
+
# Standard pagy dictionary
|
9
2
|
|
10
3
|
en:
|
11
4
|
pagy:
|
12
5
|
nav:
|
13
|
-
prev: "‹
|
14
|
-
next: "Next
|
6
|
+
prev: "‹ Prev"
|
7
|
+
next: "Next ›"
|
15
8
|
gap: "…"
|
16
9
|
info:
|
17
10
|
single_page:
|
@@ -19,7 +12,7 @@ en:
|
|
19
12
|
one: "Displaying <b>1</b> %{item_name}"
|
20
13
|
other: "Displaying <b>all %{count}</b> %{item_name}"
|
21
14
|
multiple_pages: "Displaying %{item_name} <b>%{from}-%{to}</b> of <b>%{count}</b> in total"
|
22
|
-
|
15
|
+
item_name:
|
23
16
|
zero: "items"
|
24
17
|
one: "item"
|
25
18
|
other: "items"
|
data/lib/pagy.rb
CHANGED
@@ -1,61 +1,57 @@
|
|
1
|
-
|
2
|
-
require 'ostruct'
|
1
|
+
# See Pagy API documentation: https://ddnexus.github.io/pagy/api/pagy
|
3
2
|
|
4
|
-
|
5
|
-
# items-per-page-limit, etc.), does some simple aritmetic and creates a very
|
6
|
-
# small object (~3k) with all is needed for pagination and navigation.
|
7
|
-
# Notice that it doesn't actually do any pagination, nor navigation... that is
|
8
|
-
# done with a few helpers in the Pagy::Backend and Pagy::Frontend modules.
|
3
|
+
require 'pathname'
|
9
4
|
|
10
|
-
class Pagy ; VERSION = '0.
|
5
|
+
class Pagy ; VERSION = '0.5.0'
|
11
6
|
|
12
7
|
autoload :Backend, 'pagy/backend'
|
13
8
|
autoload :Frontend, 'pagy/frontend'
|
14
9
|
|
15
10
|
class OutOfRangeError < StandardError; end
|
16
11
|
|
17
|
-
# root pathname to get the path of pagy files like templates or
|
12
|
+
# root pathname to get the path of pagy files like templates or dictionaries
|
18
13
|
def self.root; Pathname.new(__FILE__).dirname end
|
19
14
|
|
20
|
-
# default
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
# initial/final: max pages to show from the first/last page
|
26
|
-
# before/after: max pages before/after the current page
|
27
|
-
Opts = OpenStruct.new(limit:20, offset:0, initial:1, before:4, after:4, final:1)
|
15
|
+
# default core vars
|
16
|
+
VARS = { items:20, outset:0, initial:1, before:4, after:4, final:1 }
|
17
|
+
|
18
|
+
# default I18n vars
|
19
|
+
I18N = { file: Pagy.root.join('locales', 'pagy.yml').to_s, plurals: -> (c) {c==0 && 'zero' || c==1 && 'one' || 'other'} }
|
28
20
|
|
29
|
-
|
21
|
+
|
22
|
+
attr_reader :count, :page, :items, :vars, :pages, :last, :offset, :from, :to, :prev, :next
|
30
23
|
|
31
24
|
# merge and validate the options, do some simple aritmetic and set the instance variables
|
32
|
-
def initialize(
|
33
|
-
@
|
34
|
-
@
|
35
|
-
|
36
|
-
@
|
37
|
-
instance_variable_set(:"@#{k}", @
|
25
|
+
def initialize(vars)
|
26
|
+
@vars = VARS.merge(vars) # default vars + instance vars
|
27
|
+
@vars[:page] = (@vars[:page]||1).to_i # set page to 1 if nil
|
28
|
+
{count:0, items:1, outset:0, initial:0, before:0, page:1, after:0, final:0}.each do |k,min|
|
29
|
+
@vars[k] >= min rescue nil || raise(ArgumentError, "expected :#{k} >= #{min}; got #{@vars[k].inspect}")
|
30
|
+
instance_variable_set(:"@#{k}", @vars.delete(k)) # set all the core variables
|
38
31
|
end
|
39
|
-
@pages
|
32
|
+
@pages = @last = [(@count.to_f / @items).ceil, 1].max # cardinal and ordinal meanings
|
40
33
|
(1..@last).cover?(@page) || raise(OutOfRangeError, "expected :page in 1..#{@last}; got #{@page.inspect}")
|
41
|
-
@offset
|
42
|
-
@
|
43
|
-
@from
|
44
|
-
@to
|
45
|
-
@prev
|
46
|
-
@next
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
34
|
+
@offset = @items * (@page - 1) + @outset # pagination offset + outset (initial offset)
|
35
|
+
@items = @count % @items if @page == @last # adjust items for last page
|
36
|
+
@from = @count == 0 ? 0 : @offset+1 - @outset # page begins from item
|
37
|
+
@to = @offset + @items - @outset # page ends to item
|
38
|
+
@prev = (@page-1 unless @page == 1) # nil if no prev page
|
39
|
+
@next = (@page+1 unless @page == @last) # nil if no next page
|
40
|
+
end
|
41
|
+
|
42
|
+
# return the array of page numbers and :gap items e.g. [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
43
|
+
def series
|
44
|
+
@series ||= [].tap do |series|
|
45
|
+
[*0..@initial, *@page-@before..@page+@after, *@last-@final+1..@last+1].sort!.each_cons(2) do |a, b|
|
46
|
+
if a<0 || a==b || a>@last # skip out of range and duplicates
|
47
|
+
elsif a+1 == b; series.push(a) # no gap -> no additions
|
48
|
+
elsif a+2 == b; series.push(a, a+1) # 1 page gap -> fill with missing page
|
49
|
+
else series.push(a, :gap) # n page gap -> add :gap
|
50
|
+
end # skip the end boundary (last+1)
|
51
|
+
end
|
52
|
+
series.shift # remove the start boundary (0)
|
53
|
+
series[series.index(@page)] = @page.to_s # convert the current page to String
|
56
54
|
end
|
57
|
-
@series.shift # remove the start-boundary (0)
|
58
|
-
@series[@series.index(@page)] = @page.to_s # convert the current page to String
|
59
55
|
end
|
60
56
|
|
61
57
|
end
|
data/lib/pagy/backend.rb
CHANGED
@@ -1,42 +1,24 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Including this module (usually in your controller) is handy but totally optional.
|
4
|
-
# It basically just encapsulates a couple of verbose statements in one single slick
|
5
|
-
# #pagy method, but it does not add any functionality on its own.
|
6
|
-
#
|
7
|
-
# Using the module allows you to have a predefined method and a few sub-methods
|
8
|
-
# (i.e. methods called only by the predefined method) handy if you need to override
|
9
|
-
# some aspect of the predefined #pagy method.
|
10
|
-
#
|
11
|
-
# However, you can just explicitly write your own pagy method in just a couple of
|
12
|
-
# lines, specially if you need to override two or more methods. For example:
|
13
|
-
#
|
14
|
-
# def pagy(scope, opts={})
|
15
|
-
# pagy = Pagy.new scope.count, page: params[:page], **opts
|
16
|
-
# return pagy, scope.offset(pagy.offset).limit(pagy.limit)
|
17
|
-
# end
|
1
|
+
# See Pagy::Backend API documentation: https://ddnexus.github.io/pagy/api/backend
|
18
2
|
|
3
|
+
class Pagy
|
19
4
|
module Backend ; private # the whole module is private so no problem with including it in a controller
|
20
5
|
|
21
|
-
|
22
|
-
|
23
|
-
|
6
|
+
# return pagy object and items
|
7
|
+
def pagy(collection, vars=nil)
|
8
|
+
pagy = Pagy.new(vars ? pagy_get_vars(collection).merge!(vars) : pagy_get_vars(collection)) # conditional merge is faster and saves memory
|
9
|
+
return pagy, pagy_get_items(collection, pagy)
|
24
10
|
end
|
25
11
|
|
26
|
-
|
27
|
-
|
12
|
+
# sub-method called only by #pagy: here for easy customization of variables by overriding
|
13
|
+
def pagy_get_vars(collection)
|
14
|
+
# return the variables to initialize the pagy object
|
15
|
+
{ count: collection.count, page: params[:page] }
|
28
16
|
end
|
29
17
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def pagy_get_i18n_key(obj) end
|
35
|
-
|
36
|
-
# this should work with ActiveRecord, Sequel, Mongoid...
|
37
|
-
# override it if obj does not implement it that way
|
38
|
-
def pagy_get_items(obj, pagy)
|
39
|
-
obj.offset(pagy.offset).limit(pagy.limit)
|
18
|
+
# sub-method called only by #pagy: here for easy customization of record-extraction by overriding
|
19
|
+
def pagy_get_items(collection, pagy)
|
20
|
+
# this should work with ActiveRecord, Sequel, Mongoid...
|
21
|
+
collection.offset(pagy.offset).limit(pagy.items)
|
40
22
|
end
|
41
23
|
|
42
24
|
end
|
data/lib/pagy/frontend.rb
CHANGED
@@ -1,165 +1,100 @@
|
|
1
|
+
# See Pagy::Frontend API documentation: https://ddnexus.github.io/pagy/api/frontend
|
2
|
+
|
3
|
+
# this file will get autoloaded, so environment constants like ::I18n will be already set
|
1
4
|
require 'yaml'
|
2
5
|
class Pagy
|
3
6
|
|
4
|
-
# This module supplies a few methods to deal with the navigation aspect of the pagination.
|
5
|
-
# You will usually include it in some helper module, eventually overriding the #pagy_url_for
|
6
|
-
# in order to fit its behavior with your app needs (e.g.: removing and adding some param or
|
7
|
-
# allowing fancy routes, etc.)
|
8
|
-
#
|
9
7
|
# All the code here has been optimized for performance: it may not look very pretty
|
10
8
|
# (as most code dealing with many long strings), but its performance makes it very sexy! ;)
|
11
9
|
module Frontend
|
12
10
|
|
13
11
|
# Generic pagination: it returns the html with the series of links to the pages
|
14
|
-
def pagy_nav(pagy
|
15
|
-
|
16
|
-
link = pagy_link_proc(pagy)
|
17
|
-
tags = []
|
12
|
+
def pagy_nav(pagy)
|
13
|
+
tags = []; link = pagy_link_proc(pagy)
|
18
14
|
|
19
15
|
tags << (pagy.prev ? %(<span class="page prev">#{link.call pagy.prev, pagy_t('pagy.nav.prev'.freeze), 'aria-label="previous"'.freeze}</span>)
|
20
16
|
: %(<span class="page prev disabled">#{pagy_t('pagy.nav.prev'.freeze)}</span>))
|
21
17
|
pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
22
|
-
tags <<
|
23
|
-
|
24
|
-
|
25
|
-
when :gap ; %(<span class="page gap">#{pagy_t('pagy.nav.gap'.freeze)}</span>) # page gap
|
18
|
+
tags << if item.is_a?(Integer); %(<span class="page">#{link.call item}</span>) # page link
|
19
|
+
elsif item.is_a?(String) ; %(<span class="page active">#{item}</span>) # current page
|
20
|
+
elsif item == :gap ; %(<span class="page gap">#{pagy_t('pagy.nav.gap'.freeze)}</span>) # page gap
|
26
21
|
end
|
27
22
|
end
|
28
23
|
tags << (pagy.next ? %(<span class="page next">#{link.call pagy.next, pagy_t('pagy.nav.next'.freeze), 'aria-label="next"'.freeze}</span>)
|
29
24
|
: %(<span class="page next disabled">#{pagy_t('pagy.nav.next'.freeze)}</span>))
|
30
|
-
%(<nav class="
|
25
|
+
%(<nav class="pagination" role="navigation" aria-label="pager">#{tags.join(' '.freeze)}</nav>)
|
31
26
|
end
|
32
27
|
|
33
28
|
|
34
29
|
# Pagination for bootstrap: it returns the html with the series of links to the pages
|
35
|
-
def pagy_nav_bootstrap(pagy
|
36
|
-
|
37
|
-
link = pagy_link_proc(pagy, 'class="page-link"'.freeze)
|
38
|
-
tags = []
|
30
|
+
def pagy_nav_bootstrap(pagy)
|
31
|
+
tags = []; link = pagy_link_proc(pagy, 'class="page-link"'.freeze)
|
39
32
|
|
40
33
|
tags << (pagy.prev ? %(<li class="page-item prev">#{link.call pagy.prev, pagy_t('pagy.nav.prev'.freeze), 'aria-label="previous"'.freeze}</li>)
|
41
34
|
: %(<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.prev'.freeze)}</a></li>))
|
42
35
|
pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
43
|
-
tags <<
|
44
|
-
|
45
|
-
|
46
|
-
when :gap ; %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap'.freeze)}</a></li>) # page gap
|
36
|
+
tags << if item.is_a?(Integer); %(<li class="page-item">#{link.call item}</li>) # page link
|
37
|
+
elsif item.is_a?(String) ; %(<li class="page-item active">#{link.call item}</li>) # active page
|
38
|
+
elsif item == :gap ; %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap'.freeze)}</a></li>) # page gap
|
47
39
|
end
|
48
40
|
end
|
49
41
|
tags << (pagy.next ? %(<li class="page-item next">#{link.call pagy.next, pagy_t('pagy.nav.next'.freeze), 'aria-label="next"'.freeze}</li>)
|
50
42
|
: %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.next'.freeze)}</a></li>))
|
51
|
-
%(<nav class="
|
43
|
+
%(<nav class="pagination" role="navigation" aria-label="pager"><ul class="pagination">#{tags.join}</ul></nav>)
|
52
44
|
end
|
53
45
|
|
54
46
|
|
55
47
|
# return examples: "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
|
56
|
-
def pagy_info(pagy
|
57
|
-
name =
|
58
|
-
name = pagy_t('pagy.info.item'.freeze, count: pagy.count) if name.start_with?('translation missing:'.freeze)
|
48
|
+
def pagy_info(pagy)
|
49
|
+
name = pagy_t(pagy.vars[:item_path] || 'pagy.info.item_name'.freeze, count: pagy.count)
|
59
50
|
key = pagy.pages == 1 ? 'single_page'.freeze : 'multiple_pages'.freeze
|
60
|
-
pagy_t
|
51
|
+
pagy_t("pagy.info.#{key}", item_name: name, count: pagy.count, from: pagy.from, to: pagy.to)
|
61
52
|
end
|
62
53
|
|
63
54
|
|
64
55
|
# this works with all Rack-based frameworks (Sinatra, Padrino, Rails, ...)
|
65
56
|
def pagy_url_for(n)
|
66
57
|
url = File.join(request.script_name.to_s, request.path_info)
|
67
|
-
params = request.GET.merge('page' => n.to_s)
|
68
|
-
url << '?' << Rack::Utils.build_nested_query(params)
|
58
|
+
params = request.GET.merge('page'.freeze => n.to_s)
|
59
|
+
url << '?' << Rack::Utils.build_nested_query(pagy_get_params(params))
|
69
60
|
end
|
70
61
|
|
71
62
|
|
72
|
-
#
|
73
|
-
|
74
|
-
#
|
75
|
-
# It implements only the very basic functionality of the I18n.t method
|
76
|
-
# but it's still good enough for the limited Pagy's needs and it is faster.
|
77
|
-
#
|
78
|
-
# You can still use this method with a pluralization different than English
|
79
|
-
# (i.e. not 'zero', 'one', 'other' plurals). In that case you should define the
|
80
|
-
# Pagy::Opts.i18n_plurals proc to return the plural key based on the passed count.
|
81
|
-
#
|
82
|
-
# If you need full I18n functionality, you should override this method with something like:
|
83
|
-
# def pagy_t(*a); I18n.t(*a) end
|
84
|
-
# and add your translations to the I18n usual locales files
|
85
|
-
|
86
|
-
hash = YAML.load_file Opts.i18n_file || Pagy.root.join('locales', 'pagy.yml')
|
87
|
-
I18N = hash[hash.keys.first].freeze
|
88
|
-
|
89
|
-
# Similar to I18n.t for interpolation and pluralization, with the following constraints:
|
90
|
-
# - the path/keys option is supported only in dot-separated string or symbol format
|
91
|
-
# - the :scope and :default options are not supported
|
92
|
-
# - no exception are raised: the errors are returned as translated strings
|
93
|
-
def pagy_t(path, vars={})
|
94
|
-
value = I18N.dig(*path.to_s.split('.'.freeze))
|
95
|
-
if value.is_a?(Hash)
|
96
|
-
vars.has_key?(:count) or return value
|
97
|
-
plural = (Opts.i18n_plurals ||= -> (c) {c==0 && 'zero' || c==1 && 'one' || 'other'}).call(vars[:count])
|
98
|
-
value.has_key?(plural) or return %(invalid pluralization data: "#{path}" cannot be used with count: #{vars[:count]}; key "#{plural}" is missing.)
|
99
|
-
value = value[plural]
|
100
|
-
end
|
101
|
-
value or return %(translation missing: "#{path}")
|
102
|
-
sprintf value, Hash.new{|h,k| "%{#{k}}"}.merge!(vars) # interpolation
|
103
|
-
end
|
104
|
-
|
63
|
+
# sub-method called only by #pagy_url_for: here for easy customization of params by overriding
|
64
|
+
def pagy_get_params(p) p end
|
105
65
|
|
106
|
-
# NOTICE: This method is used internally, so you need to know about it only if you
|
107
|
-
# are going to override a *_nav helper or a template AND change the link tags.
|
108
|
-
#
|
109
|
-
# You call this method in order to get a proc that you will call to produce the page links.
|
110
|
-
# The reasaon it is a 2 step process instead of a single method call is performance.
|
111
|
-
#
|
112
|
-
# Call the method to get the proc (once):
|
113
|
-
# link = pagy_link_proc( pagy [, extra_attributes_string ])
|
114
|
-
#
|
115
|
-
# Call the proc to get the links (multiple times):
|
116
|
-
# link.call( page_number [, label [, extra_attributes_string ]])
|
117
|
-
#
|
118
|
-
# You can pass extra attribute strings to get inserted in the link tags at many different levels.
|
119
|
-
# Depending by the scope you want your attributes to be added, (from wide to narrow) you can:
|
120
|
-
#
|
121
|
-
# 1. For all pagy objects: set the global option :link_extra:
|
122
|
-
#
|
123
|
-
# Pagy::Opts.extra_link = 'data-remote="true"'
|
124
|
-
# link.call(page_number=2)
|
125
|
-
# #=> <a href="...?page=2" data-remote="true">2</a>
|
126
|
-
#
|
127
|
-
# 2. For one pagy object: pass a :link_extra option to a pagy constructor (Pagy.new or pagy controller method):
|
128
|
-
#
|
129
|
-
# @pagy, @records = pagy(my_scope, extra_link: 'data-remote="true"')
|
130
|
-
# link.call(page_number)
|
131
|
-
# #=> <a href="...?page=2" data-remote="true">2</a>
|
132
|
-
#
|
133
|
-
# 3. For one pagy_nav render: pass a :link_extra option to a *_nav method:
|
134
|
-
#
|
135
|
-
# pagy_nav(@pagy, extra_link: 'data-remote="true"') #
|
136
|
-
# link.call(page_number)
|
137
|
-
# #=> <a href="...?page=2" data-remote="true">2</a>
|
138
|
-
#
|
139
|
-
# 4. For all the calls to the returned pagy_link_proc: pass an extra attributes string when you get the proc:
|
140
|
-
#
|
141
|
-
# link = pagy_link_proc(pagy, 'class="page-link"')
|
142
|
-
# link.call(page_number)
|
143
|
-
# #=> <a href="...?page=2" data-remote="true" class="page-link">2</a>
|
144
|
-
#
|
145
|
-
# 5. For a single link.call: pass an extra attributes string when you call the proc:
|
146
|
-
#
|
147
|
-
# link.call(page_number, 'aria-label="my-label"')
|
148
|
-
# #=> <a href="...?page=2" data-remote="true" class="page-link" aria-label="my-label">2</a>
|
149
|
-
#
|
150
|
-
# WARNING: since we use only strings for performance, the attribute strings get concatenated, not merged!
|
151
|
-
# Be careful not to pass the same attribute at different levels multiple times. That would generate a duplicated
|
152
|
-
# attribute which is illegal html (although handled by all mayor browsers by ignoring all the duplicates but the first)
|
153
66
|
|
154
67
|
MARKER = "-pagy-#{'pagy'.hash}-".freeze
|
155
68
|
|
156
|
-
|
157
|
-
|
69
|
+
# returns a specialized proc to generate the HTML links
|
70
|
+
def pagy_link_proc(pagy, lx=''.freeze) # "lx" means "link extra"
|
71
|
+
p_prev, p_next, p_lx = pagy.prev, pagy.next, pagy.vars[:link_extra]
|
158
72
|
a, b = %(<a href="#{pagy_url_for(MARKER)}"#{p_lx ? %( #{p_lx}) : ''.freeze}#{lx.empty? ? lx : %( #{lx})}).split(MARKER)
|
159
73
|
-> (n, text=n, x=''.freeze) { "#{a}#{n}#{b}#{ if n == p_prev ; ' rel="prev"'.freeze
|
160
74
|
elsif n == p_next ; ' rel="next"'.freeze
|
161
|
-
else ''.freeze
|
162
|
-
|
75
|
+
else ''.freeze end }#{x.empty? ? x : %( #{x})}>#{text}</a>" }
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# define #pagy_t depending on I18N[:gem] and I18n
|
80
|
+
if I18N[:gem] || I18N[:gem].nil? && defined?(I18n)
|
81
|
+
I18n.load_path << I18N[:file]
|
82
|
+
def pagy_t(*a); I18n.t(*a) end
|
83
|
+
else
|
84
|
+
# load data from the first locale in the file
|
85
|
+
I18N_DATA = YAML.load_file(I18N[:file]).first[1].freeze
|
86
|
+
# Similar to I18n.t for interpolation and pluralization but without translation
|
87
|
+
# Use only for single-language apps: it is specialized for pagy and 5x faster than I18n.t
|
88
|
+
def pagy_t(path, vars={})
|
89
|
+
value = I18N_DATA.dig(*path.to_s.split('.'.freeze)) or return %(translation missing: "#{path}")
|
90
|
+
if value.is_a?(Hash)
|
91
|
+
vars.key?(:count) or return value
|
92
|
+
plural = I18N[:plurals].call(vars[:count])
|
93
|
+
value.key?(plural) or return %(invalid pluralization data: "#{path}" cannot be used with count: #{vars[:count]}; key "#{plural}" is missing.)
|
94
|
+
value = value[plural] or return %(translation missing: "#{path}")
|
95
|
+
end
|
96
|
+
sprintf value, Hash.new{|h,k| "%{#{k}}"}.merge!(vars) # interpolation
|
97
|
+
end
|
163
98
|
end
|
164
99
|
|
165
100
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<%#
|
2
|
+
This template is i18n-ready: if you don't use i18n, then you can replace the pagy_t
|
3
|
+
calls with the actual strings ("‹ Prev", "Next ›", "…").
|
4
|
+
|
5
|
+
The link variable is set to a proc that returns the link tag.
|
6
|
+
Usage: link.call( page_number [, text [, extra_attributes_string ]])
|
7
|
+
-%>
|
8
|
+
<% link = pagy_link_proc(pagy) -%>
|
9
|
+
<%# -%><nav aria-label="pager" class="pagination" role="navigation">
|
10
|
+
<% if pagy.prev -%> <span class="page prev"><%== link.call(pagy.prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"') %></span>
|
11
|
+
<% else -%> <span class="page prev disabled"><%== pagy_t('pagy.nav.prev') %></span>
|
12
|
+
<% end -%>
|
13
|
+
<% pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36] -%>
|
14
|
+
<% if item.is_a?(Integer) -%> <span class="page"><%== link.call(item) %></span>
|
15
|
+
<% elsif item.is_a?(String) -%> <span class="page current"><%= item %></span>
|
16
|
+
<% elsif item == :gap -%> <span class="page gap"><%== pagy_t('pagy.nav.gap') %></span>
|
17
|
+
<% end -%>
|
18
|
+
<% end -%>
|
19
|
+
<% if pagy.next -%> <span class="page next"><%== link.call(pagy.next, pagy_t('pagy.nav.next'), 'aria-label="next"') %></span>
|
20
|
+
<% else -%> <span class="page next disabled"><%== pagy_t('pagy.nav.next') %></span>
|
21
|
+
<% end -%>
|
22
|
+
<%# -%></nav>
|
@@ -13,18 +13,16 @@
|
|
13
13
|
- else
|
14
14
|
%span.page.prev.disabled!= pagy_t('pagy.nav.prev')
|
15
15
|
|
16
|
-
- pagy.series.each do |item|
|
17
|
-
-
|
16
|
+
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
17
|
+
- if item.is_a?(Integer) # page link
|
18
|
+
%span.page
|
19
|
+
!= link.call(item)
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
!= link.call(item)
|
21
|
+
- elsif item.is_a?(String) # current page
|
22
|
+
%span.page.current= item
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
- when :gap # page gap
|
27
|
-
%span.page.gap!= pagy_t('pagy.nav.gap')
|
24
|
+
- elsif item == :gap # page gap
|
25
|
+
%span.page.gap!= pagy_t('pagy.nav.gap')
|
28
26
|
|
29
27
|
- if pagy.next
|
30
28
|
%span.page.next!= link.call(pagy.next, pagy_t('pagy.nav.next'), 'aria-label="next"')
|
@@ -13,16 +13,15 @@ nav.pagination role="navigation" aria-label="pager"
|
|
13
13
|
- else
|
14
14
|
span.page.prev.disabled ==> pagy_t('pagy.nav.prev')
|
15
15
|
|
16
|
-
- pagy.series.each do |item|
|
17
|
-
-
|
18
|
-
|
19
|
-
span.page ==> link.call(item)
|
16
|
+
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
17
|
+
- if item.is_a?(Integer) # page link
|
18
|
+
span.page ==> link.call(item)
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
- elsif item.is_a?(String) # current page
|
21
|
+
span.page.current ==> item
|
23
22
|
|
24
|
-
|
25
|
-
|
23
|
+
- elsif item == :gap # page gap
|
24
|
+
span.page.gap ==> pagy_t('pagy.nav.gap')
|
26
25
|
|
27
26
|
- if pagy.next
|
28
27
|
span.page.next == link.call(pagy.next, pagy_t('pagy.nav.next'), 'aria-label="next"')
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<%#
|
2
|
+
This template is i18n-ready: if you don't use i18n, then you can replace the pagy_t
|
3
|
+
calls with the actual strings ("‹ Prev", "Next ›", "…").
|
4
|
+
|
5
|
+
The link variable is set to a proc that returns the link tag.
|
6
|
+
Usage: link.call( page_number [, text [, extra_attributes_string ]])
|
7
|
+
-%>
|
8
|
+
<% link = pagy_link_proc(pagy, 'class="page-link"') -%>
|
9
|
+
<%# -%><nav aria-label="pager" class="pagination" role="navigation">
|
10
|
+
<%# -%> <ul class="pagination">
|
11
|
+
<% if pagy.prev -%> <li class="page-item prev"><%== link.call(pagy.prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"') %></li>
|
12
|
+
<% else -%> <li class="page-item prev disabled"><a href="#" class="page-link"><%== pagy_t('pagy.nav.prev') %></a></li>
|
13
|
+
<% end -%>
|
14
|
+
<% pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36] -%>
|
15
|
+
<% if item.is_a?(Integer) -%> <li class="page-item"><%== link.call(item) %></li>
|
16
|
+
<% elsif item.is_a?(String) -%> <li class="page-item active"><%== link.call(item) %></li>
|
17
|
+
<% elsif item == :gap -%> <li class="page-item disabled gap"><a href="#" class="page-link"><%== pagy_t('pagy.nav.gap') %></a></li>
|
18
|
+
<% end -%>
|
19
|
+
<% end -%>
|
20
|
+
<% if pagy.next -%> <li class="page-item next"><%== link.call(pagy.next, pagy_t('pagy.nav.next'), 'aria-label="next"') %></li>
|
21
|
+
<% else -%> <li class="page-item next disabled"><a href="#" class="page-link"><%== pagy_t('pagy.nav.next') %></a></li>
|
22
|
+
<% end -%>
|
23
|
+
<%# -%> </ul>
|
24
|
+
<%# -%></nav>
|
@@ -16,17 +16,16 @@
|
|
16
16
|
%li.page-item.prev.disabled
|
17
17
|
%a.page-link{:href => '#'}!= pagy_t('pagy.nav.prev')
|
18
18
|
|
19
|
-
- pagy.series.each do |item|
|
20
|
-
-
|
21
|
-
-
|
22
|
-
%li.page-item!= link.call(item)
|
19
|
+
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
20
|
+
- if item.is_a?(Integer) # page link
|
21
|
+
%li.page-item!= link.call(item)
|
23
22
|
|
24
|
-
|
25
|
-
|
23
|
+
- elsif item.is_a?(String) # current page
|
24
|
+
%li.page-item.active!= link.call(item)
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
- elsif item == :gap # page gap
|
27
|
+
%li.page-item.disabled.gap
|
28
|
+
%a.page-link{:href => "#"}!= pagy_t('pagy.nav.gap')
|
30
29
|
|
31
30
|
- if pagy.next
|
32
31
|
%li.page-item.next!= link.call(pagy.next, pagy_t('pagy.nav.next'), 'aria-label="next"')
|
@@ -16,18 +16,16 @@ nav.pagination role="navigation" aria-label="pager"
|
|
16
16
|
li.page-item.prev.disabled
|
17
17
|
a.page-link href="#" == pagy_t('pagy.nav.prev')
|
18
18
|
|
19
|
-
- pagy.series.each do |item|
|
20
|
-
-
|
19
|
+
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
20
|
+
- if item.is_a?(Integer) # page link
|
21
|
+
li.page-item == link.call(item)
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
- elsif item.is_a?(String) # current page
|
24
|
+
li.page-item.active == link.call(item)
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
- when :gap # page gap
|
29
|
-
li.page-item.disabled.gap
|
30
|
-
a.page-link href="#" == pagy_t('pagy.nav.gap')
|
26
|
+
- elsif item == :gap # page gap
|
27
|
+
li.page-item.disabled.gap
|
28
|
+
a.page-link href="#" == pagy_t('pagy.nav.gap')
|
31
29
|
|
32
30
|
- if pagy.next
|
33
31
|
li.page-item.next == link.call(pagy.next, pagy_t('pagy.nav.next'), 'aria-label="next"')
|
data/pagy.gemspec
CHANGED
@@ -11,14 +11,14 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.email = ['dd.nexus@gmail.com']
|
12
12
|
s.date = Date.today.to_s
|
13
13
|
|
14
|
-
s.summary =
|
15
|
-
s.description =
|
14
|
+
s.summary = 'The Ultimate Pagination Ruby Gem'
|
15
|
+
s.description = 'Agnostic pagination in plain ruby: it works with any framework, ORM and DB type, with all kinds of collections, even pre-paginated, scopes, Arrays, JSON data... and just whatever you can count. Easy to use and customize, very fast and very light.'
|
16
16
|
s.homepage = 'https://github.com/ddnexus/pagy'
|
17
17
|
s.license = 'MIT'
|
18
18
|
s.require_paths = ['lib']
|
19
19
|
|
20
|
-
s.files = `git ls-files -z`.split("\x0")
|
21
|
-
|
20
|
+
s.files = `git ls-files -z`.split("\x0").select{|f| f.start_with?('lib', 'pagy.gemspec', 'LICENSE', 'README', 'images') }
|
21
|
+
|
22
22
|
|
23
23
|
s.add_development_dependency 'bundler', '~> 1.16'
|
24
24
|
s.add_development_dependency 'rake', '~> 10.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pagy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Domizio Demichelis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -122,9 +122,10 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
|
-
description: '
|
126
|
-
|
127
|
-
|
125
|
+
description: 'Agnostic pagination in plain ruby: it works with any framework, ORM
|
126
|
+
and DB type, with all kinds of collections, even pre-paginated, scopes, Arrays,
|
127
|
+
JSON data... and just whatever you can count. Easy to use and customize, very fast
|
128
|
+
and very light.'
|
128
129
|
email:
|
129
130
|
- dd.nexus@gmail.com
|
130
131
|
executables: []
|
@@ -133,16 +134,20 @@ extra_rdoc_files: []
|
|
133
134
|
files:
|
134
135
|
- LICENSE.txt
|
135
136
|
- README.md
|
137
|
+
- images/efficiency-table.png
|
138
|
+
- images/ips-chart.png
|
139
|
+
- images/memory-chart.png
|
140
|
+
- images/objects-chart.png
|
136
141
|
- lib/locales/pagy.yml
|
137
142
|
- lib/pagy.rb
|
138
143
|
- lib/pagy/backend.rb
|
139
144
|
- lib/pagy/frontend.rb
|
140
|
-
- lib/templates/
|
141
|
-
- lib/templates/
|
142
|
-
- lib/templates/
|
143
|
-
- lib/templates/
|
144
|
-
- lib/templates/
|
145
|
-
- lib/templates/
|
145
|
+
- lib/templates/nav.html.erb
|
146
|
+
- lib/templates/nav.html.haml
|
147
|
+
- lib/templates/nav.html.slim
|
148
|
+
- lib/templates/nav_bootstrap.html.erb
|
149
|
+
- lib/templates/nav_bootstrap.html.haml
|
150
|
+
- lib/templates/nav_bootstrap.html.slim
|
146
151
|
- pagy.gemspec
|
147
152
|
homepage: https://github.com/ddnexus/pagy
|
148
153
|
licenses:
|
@@ -167,5 +172,5 @@ rubyforge_project:
|
|
167
172
|
rubygems_version: 2.7.4
|
168
173
|
signing_key:
|
169
174
|
specification_version: 4
|
170
|
-
summary:
|
175
|
+
summary: The Ultimate Pagination Ruby Gem
|
171
176
|
test_files: []
|
@@ -1,28 +0,0 @@
|
|
1
|
-
<%#
|
2
|
-
This template is i18n-ready: if you don't use i18n, then you can replace the pagy_t
|
3
|
-
calls with the actual strings ("‹ Prev", "Next ›", "…").
|
4
|
-
|
5
|
-
The link variable is set to a proc that returns the link tag.
|
6
|
-
Usage: link.call( page_number [, text [, extra_attributes_string ]])
|
7
|
-
-%>
|
8
|
-
<% link = pagy_link_proc(pagy, 'class="page-link"') -%>
|
9
|
-
<%# -%><nav aria-label="pager" class="pagination" role="navigation">
|
10
|
-
<%# -%> <ul class="pagination">
|
11
|
-
<% if pagy.prev -%> <li class="page-item prev"><%== link.call(pagy.prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"') %></li>
|
12
|
-
<% else -%> <li class="page-item prev disabled"><a href="#" class="page-link"><%== pagy_t('pagy.nav.prev') %></a></li>
|
13
|
-
<% end -%>
|
14
|
-
<% pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36] -%>
|
15
|
-
<% case item
|
16
|
-
when Integer -%>
|
17
|
-
<%# -%> <li class="page-item"><%== link.call(item) %></li>
|
18
|
-
<% when String -%>
|
19
|
-
<%# -%> <li class="page-item active"><%== link.call(item) %></li>
|
20
|
-
<% when :gap -%>
|
21
|
-
<%# -%> <li class="page-item disabled gap"><a href="#" class="page-link"><%== pagy_t('pagy.nav.gap') %></a></li>
|
22
|
-
<% end -%>
|
23
|
-
<% end -%>
|
24
|
-
<% if pagy.next -%> <li class="page-item next"><%== link.call(pagy.next, pagy_t('pagy.nav.next'), 'aria-label="next"') %></li>
|
25
|
-
<% else -%> <li class="page-item next disabled"><a href="#" class="page-link"><%== pagy_t('pagy.nav.next') %></a></li>
|
26
|
-
<% end -%>
|
27
|
-
<%# -%> </ul>
|
28
|
-
<%# -%></nav>
|
@@ -1,26 +0,0 @@
|
|
1
|
-
<%#
|
2
|
-
This template is i18n-ready: if you don't use i18n, then you can replace the pagy_t
|
3
|
-
calls with the actual strings ("‹ Prev", "Next ›", "…").
|
4
|
-
|
5
|
-
The link variable is set to a proc that returns the link tag.
|
6
|
-
Usage: link.call( page_number [, text [, extra_attributes_string ]])
|
7
|
-
-%>
|
8
|
-
<% link = pagy_link_proc(pagy) -%>
|
9
|
-
<%# -%><nav aria-label="pager" class="pagination" role="navigation">
|
10
|
-
<% if pagy.prev -%> <span class="page prev"><%== link.call(pagy.prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"') %></span>
|
11
|
-
<% else -%> <span class="page prev disabled"><%== pagy_t('pagy.nav.prev') %></span>
|
12
|
-
<% end -%>
|
13
|
-
<% pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36] -%>
|
14
|
-
<% case item
|
15
|
-
when Integer -%>
|
16
|
-
<%# -%> <span class="page"><%== link.call(item) %></span>
|
17
|
-
<% when String -%>
|
18
|
-
<%# -%> <span class="page current"><%= item %></span>
|
19
|
-
<% when :gap -%>
|
20
|
-
<%# -%> <span class="page gap"><%== pagy_t('pagy.nav.gap') %></span>
|
21
|
-
<% end -%>
|
22
|
-
<% end -%>
|
23
|
-
<% if pagy.next -%> <span class="page next"><%== link.call(pagy.next, pagy_t('pagy.nav.next'), 'aria-label="next"') %></span>
|
24
|
-
<% else -%> <span class="page next disabled"><%== pagy_t('pagy.nav.next') %></span>
|
25
|
-
<% end -%>
|
26
|
-
<%# -%></nav>
|