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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60384daf0fd49d4815f49d4ab0bf65840835bb42f7c9c38979fae883f9e0c4c5
4
- data.tar.gz: 4aaa58eba02a590254ecf82d80f750aa4cd0b3bf7da1d745f92d8a7125c19fc9
3
+ metadata.gz: a984f9eb44e5f2d637f92e5b5317edbff0691c2b2d4e89d8bf02f2e1a11939b8
4
+ data.tar.gz: 01dc6a1f9ca9bba43400f00a645a47c02cf4aa11868705193a9b4c33a50e1811
5
5
  SHA512:
6
- metadata.gz: 3006bd929cce65f58b5d0fd4c512e791fc939de6661055359c918f035f981645ce705d3d5e02f55aa7c43816d309000ee10991c54b89144768f986f6e6f18c0d
7
- data.tar.gz: 4ad8a43b6138a671e84e074155e60f80713cd68a41f71fab6ac69d5a88bc5a48fbac0b49cb0a9b50e6c15b416c72373ceafe01e9bc841f9fe2b72965deb9dfaa
6
+ metadata.gz: b2ed0782436c442bb3127598cb1f8b8aaeb7afaf373836933e61a93b834bc91125a1f44fb34fad18b362a989c59b2c1ba3683483cf2098b36657be75420f3fc3
7
+ data.tar.gz: e7399e4568cf8fbfa537a4a193c928d0810ce47ea4d5c877765d5669d077618e7ab1e715d9892c7bf51e8e827a06b897afef12d417c2994f7be6f65c69cd7fe1
data/README.md CHANGED
@@ -1,30 +1,92 @@
1
1
  # Pagy
2
2
 
3
- Dead simple pagination in pure ruby. Easy, fast and very light. No restrictions imposed: use it with any MVC framework, any ORM, any DB type, any templating system or none at all. Use the built-in templates or create yours the way you want.
3
+ Pagy is the ultimate pagination gem that outperforms the others in each and every benchmark and comparison.
4
4
 
5
- ## Alpha Version!
5
+ ### Benchmarks
6
6
 
7
- This gem is still missing documentation. TBD... soon ;).
7
+ The best way to quickly get an idea about its features is comparing it to the other well known gems.
8
8
 
9
- ## Installation
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
- Add this line to your application's Gemfile:
11
+ #### Pagy is a lot faster
12
+
13
+ ![IPS Chart](images/ips-chart.png)
14
+
15
+ #### Pagy uses a lot less memory
16
+
17
+ ![Memory Chart](images/memory-chart.png)
18
+
19
+ #### Pagy is a lot simpler
20
+
21
+ ![Objects Chart](images/objects-chart.png)
22
+
23
+ #### Pagy is a lot more efficient
24
+
25
+ ![Efficiency Table](images/efficiency-table.png)
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
- gem 'pagy'
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
- And then execute:
73
+ ## Support, Comments and Feature Requests
74
+
75
+ [![Join the chat at https://gitter.im/ruby-pagy/Lobby](https://badges.gitter.im/ruby-pagy/Lobby.svg)](https://gitter.im/ruby-pagy/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
18
76
 
19
- $ bundle
77
+ ## Useful Links
20
78
 
21
- Or install it yourself as:
79
+ - [Documentation](https://ddnexus.github.io/pagy/index)
80
+ - [Pagination Comparison App](http://github.com/ddnexus/pagination-comparison)
22
81
 
23
- $ gem install pagy
82
+ ## Help Wanted
24
83
 
25
- ## Contributing
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
- Bug reports and pull requests are welcome on GitHub at https://github.com/ddnexus/pagy.
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
- # Pagy uses an internal I18n-like :pagy_t helper that lookup
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: "&lsaquo; Prev"
14
- next: "Next &rsaquo;"
6
+ prev: "&lsaquo;&nbsp;Prev"
7
+ next: "Next&nbsp;&rsaquo;"
15
8
  gap: "&hellip;"
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
- item:
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
- require 'pathname'
2
- require 'ostruct'
1
+ # See Pagy API documentation: https://ddnexus.github.io/pagy/api/pagy
3
2
 
4
- # This class takes a few integers (such as collection-count, page-number,
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.4.2'
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 locales
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 options
21
- # limit: max items per page: it gets adjusted for the last page,
22
- # so it will pull the right items if the collection was pre-limit(ed)
23
- # offset: the initial offset of the whole collection before pagination
24
- # set it only if the collection was pre-offset(ted): it gets added to the final offset
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
- attr_reader :count, :page, :limit, :opts, :pages, :last, :offset, :from, :to, :prev, :next, :series
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(opts)
33
- @opts = Opts.to_h.merge!(opts) # global opts + instance opts (bang faster)
34
- @opts[:page] = (@opts[:page]||1).to_i # set page to 1 if nil
35
- [:count, :limit, :offset, :initial, :before, :page, :after, :final].each do |k|
36
- @opts[k] >= 0 rescue nil || raise(ArgumentError, "expected #{k} >= 0; got #{@opts[k].inspect}")
37
- instance_variable_set(:"@#{k}", @opts.delete(k)) # set all the metrics variables
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 = @last = [(@count.to_f / @limit).ceil, 1].max # cardinal and ordinal meanings
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 += @limit * (@page - 1) # initial offset + offset for pagination
42
- @limit = @count % @limit if @page == @last # adjust limit for last page (for pre-limit(ed) collections)
43
- @from = @count == 0 ? 0 : @offset+1 # page begins from item
44
- @to = @offset + @limit # page ends to item
45
- @prev = (@page-1 unless @page == 1) # nil if no prev page
46
- @next = (@page+1 unless @page == @last) # nil if no next page
47
- @series = [] # e.g. [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
48
- all = (0..@last+1) # page range with boundaries
49
- around = ([@page-@before, 1].max .. [@page+@after, @last].min).to_a # before..after pages
50
- row = (all.first(@initial+1) | around | all.last(@final+1)).sort # row of pages with boundaries
51
- row.each_cons(2) do |a, b| # loop in consecutive pairs
52
- if a+1 == b ; @series.push(a) # no gap -> no additions
53
- elsif a+2 == b ; @series.push(a, a+1) # 1 page gap -> fill with missing page
54
- else @series.push(a, :gap) # n page gap -> add :gap
55
- end # skip the end-boundary (last+1)
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
- class Pagy
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
- def pagy(obj, opts={})
22
- pagy = Pagy.new(count: pagy_get_count(obj), page: pagy_get_page, i18n_key: pagy_get_i18n_key(obj), **opts)
23
- return pagy, pagy_get_items(obj, pagy)
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
- def pagy_get_count(obj)
27
- obj.count
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
- def pagy_get_page
31
- params[:page]
32
- end
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, opts=nil)
15
- opts = opts ? pagy.opts.merge(opts) : pagy.opts
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 << case item
23
- when Integer; %(<span class="page">#{link.call item}</span>) # page link
24
- when String ; %(<span class="page active">#{item}</span>) # current page
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="#{opts[:class]||'pagination'.freeze}" role="navigation" aria-label="pager">#{tags.join(opts[:separator]||' '.freeze)}</nav>)
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, opts=nil)
36
- opts = opts ? pagy.opts.merge(opts) : pagy.opts
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 << case item
44
- when Integer; %(<li class="page-item">#{link.call item}</li>) # page link
45
- when String ; %(<li class="page-item active">#{link.call item}</li>) # active page
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="#{opts[:class]||'pagination'.freeze}" role="navigation" aria-label="pager"><ul class="pagination">#{tags.join}</ul></nav>)
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, vars=nil)
57
- name = vars && vars[:item_name] || pagy_t(pagy.opts[:i18n_key] || 'pagy.info.item'.freeze, count: pagy.count)
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 "pagy.info.#{key}", item_name: name, count: pagy.count, from: pagy.from, to: pagy.to
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
- # The :pagy_t method is the internal implementation of I18n.t, used when I18n is missing or
73
- # not needed (for example when your application is a single-locale app, e.g. only 'en', or only 'fr'...).
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
- def pagy_link_proc(pagy, lx=''.freeze) # link extra
157
- p_prev, p_next, p_lx = pagy.prev, pagy.next, pagy.opts[:link_extra]
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
- end }#{x.empty? ? x : %( #{x})}>#{text}</a>" }
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 ("&lsaquo; Prev", "Next &rsaquo;", "&hellip;").
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| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
17
- - case 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
19
+ != link.call(item)
18
20
 
19
- - when Integer # page link
20
- %span.page
21
- != link.call(item)
21
+ - elsif item.is_a?(String) # current page
22
+ %span.page.current= item
22
23
 
23
- - when String # current page
24
- %span.page.current= item
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| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
17
- - case item
18
- - when Integer # page link
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
- - when String # current page
22
- span.page.current ==> item
20
+ - elsif item.is_a?(String) # current page
21
+ span.page.current ==> item
23
22
 
24
- - when :gap # page gap
25
- span.page.gap ==> pagy_t('pagy.nav.gap')
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 ("&lsaquo; Prev", "Next &rsaquo;", "&hellip;").
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| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
20
- - case item
21
- - when Integer # page link
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
- - when String # current page
25
- %li.page-item.active!= link.call(item)
23
+ - elsif item.is_a?(String) # current page
24
+ %li.page-item.active!= link.call(item)
26
25
 
27
- - when :gap # page gap
28
- %li.page-item.disabled.gap
29
- %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')
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| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
20
- - case 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)
21
22
 
22
- - when Integer # page link
23
- li.page-item == link.call(item)
23
+ - elsif item.is_a?(String) # current page
24
+ li.page-item.active == link.call(item)
24
25
 
25
- - when String # current page
26
- li.page-item.active == link.call(item)
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 = %q{Because pagination should not suck!}
15
- s.description = %q{Dead simple pagination in pure ruby. Easy, fast and very light. No restrictions imposed: use it with any MVC framework, any ORM, any DB type, any templating system or none at all. Use the built-in templates or create yours the way you want.}
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
- .reject{|f| f.start_with? '.', 'test', 'Gemfile', 'Rakefile' }
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.2
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-02-19 00:00:00.000000000 Z
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: 'Dead simple pagination in pure ruby. Easy, fast and very light. No restrictions
126
- imposed: use it with any MVC framework, any ORM, any DB type, any templating system
127
- or none at all. Use the built-in templates or create yours the way you want.'
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/bootstrap.html.erb
141
- - lib/templates/bootstrap.html.haml
142
- - lib/templates/bootstrap.html.slim
143
- - lib/templates/default.html.erb
144
- - lib/templates/default.html.haml
145
- - lib/templates/default.html.slim
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: Because pagination should not suck!
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 ("&lsaquo; Prev", "Next &rsaquo;", "&hellip;").
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 ("&lsaquo; Prev", "Next &rsaquo;", "&hellip;").
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>