Giter VIP home page Giter VIP logo

pagy's Introduction

Pagy

Gem Version ruby Build Status Coverage Rubocop Status MIT license CII Best Practices Commits Downloads Stars

๐Ÿ† The Best Pagination Ruby Gem ๐Ÿฅ‡


โœด What's new in 8.0+ โœด

  • WARNING: The foundation, materialize, semantic and uikit CSS extras have been discontinued and will be removed in v9 (See the details)
  • New Pagy Playground to showcase, clone and develop pagy APPs without any setup on your side (try the pagy demo)
  • New :max_pages variable to limit the pagination regardless the actual count
  • Better frontend helpers
  • See the Changelog for possible breaking changes

๐Ÿš€ ๐Ÿš€ ๐Ÿš€ ๐Ÿš€ ๐Ÿš€

Each dot in the visualization above represents the resources that Pagy consumes for one full rendering. The other gems consume hundreds of times as much for the same rendering.

The IPS/Kb ratio is calculated out of speed (IPS) and Memory (Kb): it shows how well each gem uses each Kb of memory it allocates/consumes.

Notice: the above charts refers to the comparison of the basic pagy v3.0.0 helper with will_paginate v3.1.7 and kaminari v1.1.1.

While it's not up-to-date, you can expect roughly similar results with the latest versions, maybe a bit less dramatic in performance due to the multiple features added to pagy since v3 (e.g. customizable and translated aria-labels). However, consider that the difference become A LOT bigger in favor of pagy if you use *nav_js helpers, Pagy::Countless or JSON and client side pagination that are not part of the comparison because missing in the other gems.

See the Detailed Gems Comparison for full details.


๐Ÿคฉ It does it all. Better.

Code Structure

  • Pagy has a very slim core code very easy to understand and use.
  • It has a quite fat set of optional extras that you can explicitly require for very efficient and modular customization ( see extras)
  • It has no dependencies: it produces its own HTML, URLs, i18n with its own specialized and fast code
  • Its methods are accessible and overridable right where you use them (no pesky monkey-patching needed)

Unlike the other gems

  • Pagy is very modular and does not load any unnecessary code ( see why...)_
  • It doesn't impose limits even with collections|scopes that already used limit and offset ( see how...)
  • It raises Pagy::OverflowError exceptions that you can rescue from ( see how...) or use the overflow extra for a few ready to use common behaviors
  • It does not impose any difficult-to-override logic or output

๐Ÿ˜Ž It's easy to use and customize

Code for basic pagination...
# Include it in the controllers (e.g. application_controller.rb)
include Pagy::Backend

# Include it in the helpers (e.g. application_helper.rb)
include Pagy::Frontend

# Wrap your collections with pagy in your actions
@pagy, @records = pagy(Product.all)

Optionally set your defaults in the pagy initializer:

# Optionally override some pagy default with your own in the pagy initializer
Pagy::DEFAULT[:items] = 10 # items per page
Pagy::DEFAULT[:size]  = [1, 4, 4, 1] # nav bar links
# Better user experience handled automatically
require 'pagy/extras/overflow'
Pagy::DEFAULT[:overflow] = :last_page
<%# Render a view helper in your views (skipping nav links for empty pages) %>
<%== pagy_nav(@pagy) if @pagy.pages > 1 %>

Or, choose from the following view helpers:

View Helper Name Preview (Bootstrap Style shown)
pagy_nav(@pagy) pagy_nav
pagy_nav_js(@pagy) pagy_nav_js
pagy_info(@pagy) pagy_info
pagy_combo_nav_js(@pagy) pagy_combo_nav_js
pagy_items_selector_js pagy_items_selector_js
pagy_nav(@calendar[:year])
pagy_nav(@calendar[:month])
(other units: :quarter, :week, :day and custom)
calendar extra

(See the Quick Start)

Customization for CSS frameworks...
# Require a CSS framework extra in the pagy initializer (e.g. bootstrap)
require 'pagy/extras/bootstrap'
<%# Use it in your views %>
<%== pagy_bootstrap_nav(@pagy) %>

(See all the CSS Framework Extras)

Customization for special collections...
# Require some special backend extra in the pagy initializer (e.g. elasticsearch_rails)
require 'pagy/extras/elasticsearch_rails'

# Extend your models (e.g. application_record.rb)
extend Pagy::ElasticsearchRails

# Use it in your actions
response         = Article.pagy_search(params[:q])
@pagy, @response = pagy_elasticsearch_rails(response)

(See all the Search Extras)

Customization for client-side|JSON rendering...
# Require the metadata extra in the pagy initializer
require 'pagy/extras/metadata'

# Use it in your actions
pagy, records = pagy(Product.all)
render json: { data: records,
               pagy: pagy_metadata(pagy) }

(See all the Backend Tools)

Customization for headers pagination for APIs...
# Require the headers extra in the pagy initializer
require 'pagy/extras/headers'

# Use it in your actions
pagy, records = pagy(Product.all)
pagy_headers_merge(pagy)
render json: records

(See all the Backend Tools)

Customization for JSON:API pagination...
# Require the jsonapi extra in the pagy initializer
require 'pagy/extras/jsonapi'

# Use it in your actions
pagy, records = pagy(Product.all)
render json: { data:  records,
               links: pagy_jsonapi_links(pagy) }
# besides the query params will be nested. E.g.: ?page[number]=2&page[size]=100

(See all the Backend Tools)

More customization with extras...

Extras add special options and manage different components, behaviors, Frontend or Backend environments... usually by just requiring them (and optionally overriding some default).

Backend Extras

  • arel: Provides better performance of grouped ActiveRecord collections
  • array: Paginate arrays efficiently.
  • calendar: Add pagination filtering by calendar time unit (year, quarter, month, week, day, custom)
  • countless: Paginate without the need of any count, saving one query per rendering
  • elasticsearch_rails: Paginate ElasticsearchRails response objects
  • headers: Add RFC-8288 compliant http response headers (and other helpers) useful for API pagination
  • jsonapi: Implement the JSON:API specifications for pagination
  • meilisearch: Paginate Meilisearch results
  • metadata: Provides the pagination metadata to Javascript frameworks like Vue.js, react.js, etc.
  • searchkick: Paginate Searchkick::Results objects

Frontend Extras

Extra Features and Tools

  • Pagy::Console: Use pagy in the irb/rails console even without any app nor configuration
  • gearbox: Automatically change the number of items per page depending on the page number
  • i18n: Use the I18n gem instead of the faster pagy-i18n implementation
  • items: Allow the client to request a custom number of items per page with an optional selector UI
  • overflow: Allow easy handling of overflowing pages
  • standalone: Use pagy without any request object, nor Rack environment/gem, nor any defined params method
  • trim: Remove the page=1 param from the first page link

See also the How To Page

๐Ÿค“ It's well documented and supported

Documentation

Support

Posts and tutorials

Screencasts


Top ๐Ÿ’ฏ Contributors


๐Ÿ‘ Credits

Many thanks to:

๐Ÿ“ฆ Repository Info

How to contribute

See Contributing

Versioning
Branches
  • The master branch is the latest rubygem-published release. It also contains docs and comment changes that don't affect the published code. It is never force-pushed.
  • The dev branch is the development branch with the new code that will be merged in the next release. It could be force-pushed.
  • Expect any other branch to be internal, experimental, force-pushed, rebased and/or deleted even without merging.

๐Ÿ’ž Related Projects

Search rubygems.org

๐Ÿ“ƒ License

MIT

pagy's People

Contributors

747 avatar artplan1 avatar ashmaroli avatar benkoshy avatar berniechiu avatar bquorning avatar cseelus avatar ddnexus avatar dependabot[bot] avatar earlopain avatar enzinia avatar espen avatar gamafranco avatar grosser avatar molfar avatar petergoldstein avatar rafaelmontas avatar rainerborene avatar renshuki avatar sabljak avatar serghost avatar simonneutert avatar sunny avatar tersor avatar thomasklemm avatar tiagotex avatar tolchi avatar wimdavies avatar workgena avatar yenshirak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pagy's Issues

Add a Zurb Foundation Frontend Extra?

I'm working on a Rails 5 app that is using Zurb Foundation 6.

I took a quick swing at porting the bootstrap extra to foundation, but ran into some issues and didn't have time to dig deeper into the pagy tie-in points.

For the mean time, I've dropped back to another paging gem due to tight time constraints...but I'd love to see take pagy take off due to the performance possibilities.

Overall the integration looks like it should be a fairly simple task. More information on Foundation's pagination is here: https://foundation.zurb.com/sites/docs/pagination.html

count not working when collection request has "distinct"

Hello,
I get my collection with the following request:

@contents = Content.select("distinct on (contents.name) contents.*")
This request is working and does the expected behavior when run in console.

And I would like to paginate it using api-pagination and pagy:

@contents = paginate @contents

This triggers the following error:
PG::SyntaxError: ERROR: syntax error at or near "on" LINE 1: SELECT COUNT(distinct on (contents.name) contents.) FROM "c... ^ : SELECT COUNT(distinct on (contents.name) contents.) FROM "contents"

But I cannot see which line it comes from.

Pagy and ActiveModelSerializer Question

I am trying to figure out the best way to use Pagy with active_model_serializers.

With Kaminari, the object I am trying to present knows meta information about how it was serializered, such as number of pages or what is the next page.

In the controller, I can do:

def index
  users = User.all.page(params[:page)
  render json: users
end

and the serializers can ask the presentation object (users), how many pages it is and what the records are.

With pagy, we have 2 presentation objects, which adds complexity to the controllers and view layers.

def index
  pagy, users = pagy(User.all)
  render json: users,
         pagy: pagy
end

Then in my adapter, I need to look at the option variable instead of the object that is presented to determine that pagination meta data.

  def serialize
    serialized_hash = if options.key?(:root) && options[:root].nil?
                        serialized_hash = serializer_attributes
                      else
                        { root => serializer_attributes }
                      end
    build_meta
    serialized_hash[meta_key] = meta unless meta.blank?
    serialized_hash
  end

  def build_meta
    self.meta ||= { }
    self.meta.merge!(pagination_attributes) if include_pagination_attributes?
  end

  def pagination_attributes
    obj = serializer.object
    {
      pagination: {
        current_page: obj.page,
        next_page:    obj.next,
        prev_page:    obj.prev,
        total_pages:  obj.pages,
        total_count:  obj.count
      }
    }
  end

I totally get you want to avoid adding pagination logic to active_record models. But having two presentation objects (pagy and records) instead of one increases complexity in the view and controller layers.,

I am thinking about having a Frankenstein presenter object, that combines the Pagy instance with the active_record_relation to combine them into one interface, instead of two.

Is there a cleaner solution here?

Add more info to the Pagy::OutOfRangeError

Currently the only info from the exception is the message error string. For example, if you want to redirect to the last page, you have to extract the last page number from the string, and there is no way to know the other variables.
The exception should contain at least all the variables passed to the constructor, and the instance variable available at the time of the error.

Add arbitrary params to generated links

The ability to add arbitrary params similar to will_paginates :params option or Kaminaris option with the same name would be helpful for a number of use cases:

  • Automatically navigate to a (Bootstrap) tab or really any HTML section for example when navigating more than one collection per action via HTML anchors (see kaminari/kaminari#339)
  • Skipping content via HTML anchors (see #8 (comment))
  • Passing search/form params around, when Pagy is used in combination with a search function
  • Passing in a controller/action name (see will_paginate or Kaminaris docs)

no headers created?

I am trying to switch from will_paginate to pagy, but pagy does not seem to add headers to the response to a request.
With will_paginate, I have:
image

Is there any equivalent with pagy to get that information?

I added include Pagy::Backend to my ApplicationController, and I am using ruby 2.3.0. The views are kept really simple, for example for my model "content", it looks like:
"json.array!(@contents) do |content|
json.extract! content, :id, :uid, :created_at, :updated_at, :name, :content_url, :format
end"

Bulma extra

Hello!

Have idea to add Bulma CSS framework extra for it pagination component. Helper, templates, just like a Bootstrap one.
I use Bulma in my projects and it will be great to have official extra for it.

What do you think @ddnexus, is it necessary?

Untested methods

It would be nice to have a more comprehensive test suite. The following methods arenโ€™t covered by the current tests:

  • Pagy::Frontend#pagy_nav
  • Pagy::Frontend#pagy_url_for
  • Pagy::Frontend#pagy_link_proc
  • Pagy::Backend#pagy
  • Pagy::Backend#pagy_get_vars
  • Pagy::Backend#pagy_get_items

pagy_nav_bootstrap_responsive and CSP unsafe-inline

Hello, using pagy_nav_responsive or pagy_nav_bootstrap_responsive, I got this CSP error because of an inline <script> injected in the view:

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' [โ€ฆ] 'nonce-[โ€ฆ]'". Either the 'unsafe-inline' keyword, a hash ('sha256-[โ€ฆ]'), or a nonce ('nonce-...') is required to enable inline execution.

A way to fix without introducing the inline-safe vulnerability would be to replace theses <script> (eg. here) by something like:

<script type='application/json' class='pagy-responsive'>
{ id: #{id}, tags: #{tags}, widths: #{responsive[:widths].to_json}, series: #{responsive[:series].to_json} }
</script>

So one can process them in the main JS, eg:

window.addEventListener("turbolinks:load", function() {
  Array.from(document.getElementsByClassName("pagy-responsive")).forEach(function(pagination) {
    json = JSON.parse(pagination.innerHTML);
    PagyResponsive(json.id, json.tags, json.widths, json.series);
  });
});

Is that something Pagy may want? Do I try a PR?

Thanks!

allow requesting a page that is out of range to return empty

atm it raises OutOfRangeError which is confusing for users when they click on the last page, but some item got deleted or apis consumers that iterate until they find an empty result
also impractical since now I cannot render the page but need to make up a fake pagy or redirect to a page that I don't know wether it exists (how do I get the last page valid ?)

so either of these:

  • make empty the default since that's what most expect
  • add optional empty_when_out_of_range: true or so

Pagy::Frontend::I18N.load_file Error

After updating pagy from 0.10.1 to 0.11.2, the line shown below started to throw errors:

config/initializers/pagy.rb

Pagy::Frontend::I18N.load_file('config/locales/gems/pagy/tr.yml') # load a custom file

Here is the full error description:

Traceback (most recent call last):
	58: from bin/rails:6:in `<main>'
	57: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:283:in `require'
	56: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:249:in `load_dependency'
	55: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:283:in `block in require'
	54: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:29:in `require'
	53: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:20:in `require_with_bootsnap_lfi'
	52: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/loaded_features_index.rb:65:in `register'
	51: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `block in require_with_bootsnap_lfi'
	50: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require'
	49: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/commands.rb:18:in `<main>'
	48: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/command.rb:46:in `invoke'
	47: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/command/base.rb:65:in `perform'
	46: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor.rb:387:in `dispatch'
	45: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor/invocation.rb:126:in `invoke_command'
	44: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor/command.rb:27:in `run'
	43: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/commands/console/console_command.rb:95:in `perform'
	42: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/command/actions.rb:18:in `require_application_and_environment!'
	41: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:337:in `require_environment!'
	40: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:283:in `require'
	39: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:249:in `load_dependency'
	38: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:283:in `block in require'
	37: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:29:in `require'
	36: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:20:in `require_with_bootsnap_lfi'
	35: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/loaded_features_index.rb:65:in `register'
	34: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `block in require_with_bootsnap_lfi'
	33: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require'
	32: from /home/serhat/projects/nokul/config/environment.rb:7:in `<main>'
	31: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:361:in `initialize!'
	30: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/initializable.rb:60:in `run_initializers'
	29: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:205:in `tsort_each'
	28: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:226:in `tsort_each'
	27: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:347:in `each_strongly_connected_component'
	26: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:347:in `call'
	25: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:347:in `each'
	24: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:349:in `block in each_strongly_connected_component'
	23: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:415:in `each_strongly_connected_component_from'
	22: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:415:in `call'
	21: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/initializable.rb:50:in `tsort_each_child'
	20: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/initializable.rb:50:in `each'
	19: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:421:in `block in each_strongly_connected_component_from'
	18: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:431:in `each_strongly_connected_component_from'
	17: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:422:in `block (2 levels) in each_strongly_connected_component_from'
	16: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
	15: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:228:in `block in tsort_each'
	14: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/initializable.rb:61:in `block in run_initializers'
	13: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/initializable.rb:32:in `run'
	12: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/initializable.rb:32:in `instance_exec'
	11: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:613:in `block in <class:Engine>'
	10: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:613:in `each'
	 9: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:614:in `block (2 levels) in <class:Engine>'
	 8: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:656:in `load_config_initializer'
	 7: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/notifications.rb:170:in `instrument'
	 6: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:657:in `block in load_config_initializer'
	 5: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:277:in `load'
	 4: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:249:in `load_dependency'
	 3: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:277:in `block in load'
	 2: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load'
	 1: from /home/serhat/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load'
/home/serhat/projects/nokul/config/initializers/pagy.rb:55:in `<main>': undefined method `load_file' for #<Hash:0x000055858a2ba510> (NoMethodError)

OutOfRange should be renamed Overflow

Discussing #102... the whole "OutOfRange" concept should be renamed throughout the gem as a lot more appropriate "Overflow". Here are the changes needed:

  • The Pagy::OutOfRangeError should be renamed more properly Pagy::OverflowError
  • The out_of_range extra should be renamed overflow extra
    • rename the @out_of_range as @overflow
    • out_of_range? as overflow?
    • OutOfRange module as Overflow
    • :out_of_range_mode as :overflow
  • Search and replace throughout the docs
  • Create aliases to avoid breaking legacy apps (not needed because of mayor version bump)

0 results rendered when total count of results = number of items per page

Hello, first things first, thank you for this amazing gem.

I'm not sure if this is some kind of bug with pagy or just a problem with my code. I have been using pagy in my app the last few months and never noticed that until today.

I'm using rails 5.1.6 and ruby 2.3.7p456 (2018-03-28 revision 63024) [x86_64-linux]

The thing is, I am using pagy to paginate the results of a search over one of my models. If the total count of results is strictly lesser or greater than the items per page set as an option of the pagy controller method, all the results get perfectly rendered, but if the number of results is exactly equal than the items per page set, I don't get any results rendered, The output of the helper pagy_info(@pagy) is correct, though, so it is counting the results. This problem happens for any value of items per page.

This is the relevant code:

parts_controller.rb

class PartsController < ApplicationController
  include Pagy::Backend

  def index
    parts = params[:search].present? || params[:location].present? ? PartMaster.search(params) : PartMaster.none
    if params[:button] == 'report'
      build_report(parts)
    else
      @pagy, @parts = pagy(parts, items: 30, item_path: 'activerecord.models.partmaster')
      if (params[:search].present? || params[:location].present?) && parts.blank? 
      end
    end
  end

index.html.rb

<div class="container-fluid">
  <%= render '/shared/form_search' %>
  <div id="data-variant"></div>
  <div id="loader"></div>
  <%= render partial: '/shared/table', locals: {parts: @parts} %>
  <%== pagy_nav_bootstrap(@pagy) %>
</div>

image

Gem version badge not updating

Not sure whether is a cache problem or anything we can control, but it's an issue. The current version pushed to rubygem is 0.8.0, and I see the badge for 0.7.2 in the page. Why?

The number of rows provided for a FETCH caluse must be greater then zero.

Rails 5.1.6

I use the following for connecting to my database,
gem 'tiny_tds'
gem 'activerecord-sqlserver-adapter'

I use SQL Server for my database. I recently ran into this error when trying out the pagy gem in place of will_paginate. It happens when I trying to visit a page where I don't have any notes currently tied to a record. Wasn't sure if this was an issue with the gem or something I'm doing incorrectly, but figured I'd post it here.

@pagy_notes, @notes = pagy(@player.notes.ordered, items: 10)

Then when I try to access @notes.

Error
TinyTds::Error: The number of rows provided for a FETCH clause must be greater then zero.: EXEC sp_executesql N'SELECT [notes].* FROM [notes] WHERE [notes].[hidden] = @0 AND [notes].[player_id] = @1 AND ([notes].[type] != @2) ORDER BY updated_at DESC OFFSET @3 ROWS FETCH NEXT @4 ROWS ONLY', N'@0 bit, @1 int, @2 nvarchar(75), @3 int, @4 int', @0 = 0, @1 = 10075, @2 = N'User', @3 = 0, @4 = 0

total of elements in headers not correct

Hello,

I am using api_pagination and pagy, and one of the collection I want to paginate is created with a join: therefore the count does not render the correct number of elements. I used to fix it when I used will_paginate by adding a parameter "total_entries" to the paginate function of api_pagination, but it is not working anymore.

@contents = paginate @contents, total_entries: @contents.count(:id)

I assume that pagy does not give the possibility to overwrite the count, is it the case?

Thank you !

pagy(..., page: #) seems to be ignored

I'm setting the page number manually with pagy(Collection.all, page: page) in my view (my site framework doesn't use a params variable), but this generates a NameError: "undefined local variable or method params".
I get the same error when I don't specify the page option, so I'm guessing page is being ignored?

pagy_nav_responsive undefined method

I'm so sorry I did not come to think of Pagy these past 6-8 years :)

Thx for sharing !!

I have one issue with a

$ bundle exec rails server
=> Booting Puma
=> Rails 5.2.0 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.11.4 (ruby 2.5.1-p57), codename: Love Song
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

where I'm using the Boostrap Material Design by Fezvrasta

which renders the pagination like this

<nav aria-label="Page navigation example">
  <ul class="pagination justify-content-center">
    <li class="page-item disabled">
      <a class="page-link" href="#" tabindex="-1">Previous</a>
    </li>
    <li class="page-item"><a class="page-link" href="#">1</a></li>
    <li class="page-item"><a class="page-link" href="#">2</a></li>
    <li class="page-item"><a class="page-link" href="#">3</a></li>
    <li class="page-item">
      <a class="page-link" href="#">Next</a>
    </li>
  </ul>
</nav>

I know - I could add a template of my own design, matching the Bootstrap Material Design - but like you say: I'd be accepting a speed bump by 40-80%

A feature request could be a switch in the initializer to use one of a set of pre-cooked "pagers" - like Google's Webcomponent, Fezvrasta's, Bootstrap's own original, etc. Users like I would love to do PR's with our preferred favorite :)

Except for that - it's all bells and whistles
<3

Use count(:all) for AR

When specifying which columns to retrieve using .select in Active Record it causes an issue when using .count as it will use all the columns from the select. Instead '*' should be used by setting :all.

Code example: https://github.com/espen/pagy_count_bug/blob/master/app/controllers/users_controller.rb

Failing test: https://github.com/espen/pagy_count_bug/blob/master/test/controllers/users_controller_test.rb

Error:
UsersControllerTest#test_shows_users:
ActiveRecord::StatementInvalid: PG::UndefinedFunction: ERROR:  function count(bigint, character varying) does not exist
LINE 1: SELECT COUNT(id, name) FROM "users"
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
: SELECT COUNT(id, name) FROM "users"
    app/controllers/users_controller.rb:6:in `index'
    test/controllers/users_controller_test.rb:5:in `block in <class:UsersControllerTest>'

Suggestion, use .count(:all) for AR in https://github.com/ddnexus/pagy/blob/master/lib/pagy/backend.rb#L21

Responsive extra with Turbolinks

I constantly have a repeated error in the console when resizing browser window:

Uncaught TypeError: Cannot read property 'forEach' of undefined
    at render (pagy.self-8fb09da8735807829df91dbb760bbd342dd9de76a6e68fa5d7da948200b00ea8.js?body=1:56)

Rails 5.2, Turbolinks 5.1.1

How to repeat:

  1. Use pagy responsive extra
  2. Call Pagy.init() in turbolinks:load event listener
  3. Switch page (go to link via Turbolinks)

The more we change the pages the faster the errors grow. Possibly related to binding events to window

responsive_with_turbolinks

Returns blank with ransack

Result with pagy gem returns blank array [] if number of records are equal to pagination items

For eg: if i have 20 records in database and pagination items are set to 20 per pages, it returns blank array.

Sample Code

@q = assets.ransack(params[:q])
@pagy, @assets = pagy(@q.result)

OFFSET-based paging is an anti-pattern

While simpler, OFFSET-based paging has a number of big issues. If this library is intended to be a toy, the current implementation is fine. If you want serious users, it won't work. At least consider providing some sort of interface for non-offset-based pagination if you want to leave the default.

tldr

  • The offset is recomputed each time, so you can get page drift (duplicate data)
  • Response times get worse with each page

For more details: https://use-the-index-luke.com/sql/partial-results/fetch-next-page

Offending line: https://github.com/ddnexus/pagy/blob/master/lib/pagy/backend.rb#L21

i18n string suggestions

There are a few places where grammatically contiguous variables are outside of translatable string, which makes grammatically correct rendering difficult to impossible in some languages.

For example, this line:

 elsif item.is_a?(String) ; %(<li class="current"><span class="show-for-sr">#{pagy_t('pagy.nav.current')}</span> #{item}</li>) # active page

is supposed to be displayed in English locale as

You're on page #{item}

while under Japanese word order, it must be like

็พๅœจ #{item} ใƒšใƒผใ‚ธ็›ฎใงใ™

So, the variable should be included in the string.


Places with same problem as far as I noticed:

 html << %(<span class="pagy-compact-input" style="margin: 0 0.6rem;">#{pagy_t('pagy.compact.page')} #{input} #{pagy_t('pagy.compact.of')} #{p_pages}</span> )
 html << %(#{pagy_t('pagy.items.show')} #{input} #{pagy_t('pagy.items.items')})

Paginate two or more collections in one action

If I understand it correctly, currently the only way to rename the page param, is to redefine pagy_get_params.

Will paginate for example allows to do something like this:

paginate @my_collection, param_name: 'custom_param_name'

Which in turn easily enables to have more than one collection paginated per action.

@ddnexus, would something like this be a feature that you are willing to add (yourself or via a PR) or is this outside the scope of Pagy?

THe i18n extra doesn't work

Whenever I require the file in my rails app:

=> Booting Puma
=> Rails 5.1.6 application starting in development
=> Run `rails server -h` for more startup options
Exiting
org/jruby/ext/pathname/RubyPathname.java:195:in `initialize': no implicit conversion of nil into String (TypeError)

if I don't require the i18n extra the error goes away.

What could be causing this?

Material design compatibility

Hi! Iโ€™d like the contribute by adding a material design template. Is this something youโ€™d be interested in adding?

Duplicate Rails fragment caching and link to first page without param[:page]

I have pagination active on my homepage for a gallery. It shows different images for different user groups (logged in, admin, editor, ...).

When I click homepage logo, the URL is:
localhost:3000/

However, pagination link for the first paginated page shows
localhost:3000/?page=1

This raises two problems:

  1. pages are identical in content, but seen as two different pages by search engines. We should use a canonical one.
    Current result for the first paginated page: localhost:3000/?page=1
    Expected result for the first paginated page only: localhost:3000/

Basically, pagination should start inserting ?page= into URL from number 2 upwards. This can be seen on many websites with video galleries on the homepage.

  1. I am using Rails fragment caching and inserting page number into cache_key to uniquely indentify cache fragments. This is a problem as there is no page number when clicking canonical URLlocalhost:3000 but there is one for localhost:3000/?page=1. Again, two pages, same content = two different cache fragments are generated for the same content. I got bitten by this problem where cache was expired for one of these fragments, but not for another one - users see different images.

I got around this by implementing a helper. Downside: hardcoded @pagy instance which is a problem when someone has several paginations active on a single page.
It checks whether @pagy is defined on that page and whether it has params[:page] in the URL. If @pagy is defined, but no params[:page] is in the URL, it assumes a homepage and inserts default page number 1 into cache_key fragment name.

Helper

  def cache_with_pagy_support(*args, &block)
    # pagy is not defined, we do not have any pagination, no need to slip params[:page] or page 1 into cache_key
    return cache(*args, &block) unless @pagy

    # @pagy defined, we have pagination, need to check whether page number is defined or not
    # if page is not defined, insert default page: 1 into cache_key

    # block will get executed only if @pagy is defined and params[:page] is not defined
    # we are probably at the homepage, so insert default page: 1 into cache_key
    p = params.fetch(:page) do
      args[0].push(1) # modify args, slip in 1 as page:1
      return cache(*args, &block) 
    end

    # @pagy defined, params[:page] was found, so enter that specific page number as cache_key
    args[0].push(p)
    cache(*args, &block)
  end

View

# current_role inserts 'admin', 'member', ... into cache_key name
# page number is being inserted automatically by cache_with_pagy_support itself
- cache_with_pagy_support [:images, current_role] do
  .card-deck
    = render partial: 'image', collection: @images, cached: true

This works and inserts correct keys when visiting homepage without any page param in the URL:
Write fragment views/images/member/1/ebb46c39e960e982acb853421cf145e4 (0.1ms)

Subsequent visits to either canonical homepage url without page param or with page=1 param read correct cache fragment:
Read fragment views/images/member/1/ebb46c39e960e982acb853421cf145e4 (0.1ms)

However, I don't like such solutions as they assume too much about something which may change.
Is there any way to get links without page=1 for the first page of the pagination sitewide?

will_paginate had something similar here: mislav/will_paginate#459

How to get total_count with 'items:X'

Hello,

First of all, a massive thank you for extremely dedicated help I see here on your issue tracker. I am very impressed and want to express my thanks for your amazing work :)

Mine is more of a question, not a bug per se.

I have 3 different scopes for Blog state based on whether a blogpost has a publication date assigned with it or not. Then on the page I show the user the count of all three categories of a blogpost: published, scheduled and drafts.

BlogsController:

  def index
    published ||= Blog.published(params[:user_id])
    drafts    ||= Blog.draft(params[:user_id])
    scheduled ||= Blog.scheduled(params[:user_id])

    @pagy, @published = pagy(published, items: 10)
    @pagy, @drafts    = pagy(drafts, items: 10)
    @pagy, @scheduled = pagy(scheduled, items: 10)
  end

View:

%span.badge.badge-secondary #{@published.count}
#... and so on

However, if I define items: 10 (any number really) in the Blogs controller, then if I use @published.count in my view, I always get that items number as total number of published posts, which is incorrect. What I want is real total count of all published items, not number of items per page. In Kaminari, one has total_count which circumvents this side effect.

So three questions, if I may:
a) How to get total count in a Ruby way in Pagy?
b) Do I need to redefine @pagy IVAR in my controller for all three blogpost types?
c) Is it OK to use IVAR caching ||= here?

Thank you for taking a moment to review this.

Custom I18N[:file] fails to get loaded

Replacing the autoload of pagy/frontend with a require, caused a different timing in setting the I18N_DATA. By the time you set the Pagy::I18N[:file] in the initializer the constant is already set.

Add tests

Hey, you did a great job for this simple implementation but adding features get harder when there's no tests overtime. So I just decided to write do your homework for you ๐Ÿ˜„ (kidding)

all talk and no examples

tried to add this to my app and had to do a scavenger hunt through the docs, please include basic examples like this one:

# Gemfile
gem 'pagy'

# controller
include Pagy::Backend

def index
  @page, @users = page(User.all, page: params[:page], items: 25)

# helper
include Pagy::Frontend

# view
<%= pagy_nav(@pagy).html_safe  %>

Compatibility with Searchkick?

Pagy expects an ActiveRecord scope, but Searchkick returns a Searchkick::Results object. That object responds to most Array methods, or it can return an Array if you call results on it.

When using something like this:

@pagy, @issues = pagy(
  Issue.search(search_terms, {
    where: {
      open_status: @open_status
    },
    fields: ["title^5", "body^1"],
    select: ["title", "body", "user_id", "created_at", "open_status", "assignee_id"],
    limit: 25,
    load: true
  }).results
)

It will complain about no method offset and limit for Array. I've filled in the blanks with this:

class Array
  def offset(value)
    self[value..-1]
  end

  def limit(value)
    self[0..value-1]
  end
end

That gets the page to render but it seems there's other missing pieces. All the results are on one page and the navigation renders as plain text.

Has anyone got pagy working with Searchkick? I'm sure lots of folks out there will be trying to mix these 2 great gems.

I noticed the SearchKick::Results object has a bunch of goodies for pagination:

 @options=
  {:page=>1,
   :per_page=>25,
   :padding=>0,
   :load=>true,
   :includes=>nil,
   :model_includes=>nil,
   :json=>false,
   :match_suffix=>"analyzed",
   :highlight=>nil,
   :highlighted_fields=>[],
   :misspellings=>false,
   :term=>"*",
   :scope_results=>nil,
   :index_name=>nil},
 @response=
  {"took"=>6,
   "timed_out"=>false,
   "_shards"=>{"total"=>5, "successful"=>5, "skipped"=>0, 
"failed"=>0},
   "hits"=>
    {"total"=>36,
     ...

Update: Got it working with the array extra, but having to remove the limit within searchkick undoes the benefit of pagination. The nav rendering as text in the browser seems to have been a quirk of HAML. Working on the pagy_get_items solution now.

Treat arrays like any other collection

Adding a simple check to the pagy_get_itemsmethod makes pagy handle arrays nicely.
I feel like this is something that should be added?

def pagy_get_items(collection, pagy)
    # handle arrays
    return collection[pagy.offset, pagy.items] if collection.is_a? Array
    # this should work with ActiveRecord, Sequel, Mongoid...
    collection.offset(pagy.offset).limit(pagy.items)
end

I have not done any performance testing,

  • is there a faster method to check for Arrays than is_a?
  • maybe collection.drop(pagy.offset).first(pagy.items)is faster.

Best / recommended way to add an anchor to a generated URL

I just discovered Pagy and I really like the performance & RAM benefits vs. Kaminari, which I've been using. I'm trying to add an anchor to my pagination links. With Kaminari I was able to do:

paginate @equities, window: 1, params: { anchor: 'results_section' }

to produce links like: https://swingtradebot.com/equities?page=2#results_section

What's the simplest / cleanest / recommended way to do the same with Pagy? I was hoping link_extra would do it but I was wrong. Would overriding pagy_url_for be the way to go?

Raise OutOfRangeError for page input below min

OutOfRangeError works great when page input is larger than max page (page=11 when max is 10). But when setting page input to 0 (as will happen when input is not a number) it results in ArgumentError when calling pagy() which is not so easy to work with.

page=0 => ArgumentError: expected :page >= 1; got 0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.