#

# Upgrade to 43


Pagy version 43 is a complete redesign of the legacy code. Its improvements make pagination a lot simpler and powerful, but require a quite different way to use it.

# 1. Replace the pagy.rb config file

  • Rename your pagy.rb initializer as pagy-old.rb, and add the new, concise pagy.rb initializer in its place.
  • Search the pagy-old.rb for code-occurrences of Pagy::DEFAULT[...] and move them to the new pagy.js (remove them from the pagy-old.rb)
  • Replace all the Pagy::DEFAULT[...] entries just added to the new pagy.rb with Pagy.options[...].

In the next steps we will use the pagy-old.rb as the blueprint to guide most of the changes, and we will edit the new pagy.rb as needed.

# 2. Replace your used extras

The new version doesn't use the extras anymore. They got integrated in the core code, and a few have been discontinued.

  • Search any active require 'pagy/extras/* in the pagy-old.rb file
  • When you find one, follow the specific section below to upgrade your code.
  • As you proceed, remove each entry from the pagy-old.rb.

Extras...

Search (old) Replace with (new)
pagy_array(...) pagy(:offset,...)
Search (old) Replace with (new)
pagy_arel(...) pagy(:offset, count_over: true, ...)
  • All the old helpers are now @pagy instance methods with more explicit names.
Search (old) Replace with (new)
pagy_nav(@pagy, ...) @pagy.series_nav(...)
pagy_nav_js(@pagy, ...) @pagy.series_nav_js(...)
pagy_combo_nav_js(@pagy, ...) @pagy.input_nav_js(...)
pagy_limit_selector_js(@pagy, ...) @pagy.limit_tag_js(...)
pagy_prev_url(@pagy, ...) @pagy.page_url(:previous, ...)
pagy_next_url(@pagy, ...) @pagy.page_url(:next, ...)
pagy_prev_a(@pagy, ...) @pagy.previous_tag(...)
pagy_next_a(@pagy, ...) @pagy.next_tag(...)
pagy_prev_link(@pagy, ...) discontinued: implement manually
pagy_next_link(@pagy, ...) discontinued: implement manually
size: ...
Pagy.options[:size] = ...
slots: ...
Pagy.options[:slots] = ...
ends: false
Pagy.options[:end] = false
compact: true
Pagy.options[:compact] = true
  • All the old helpers are now @pagy instance methods with more explicit names.
Search (old) Replace with (new)
pagy_boostrap_nav(@pagy, ...) @pagy.series_nav(:boostrap, ...)
pagy_boostrap_nav_js(@pagy, ...) @pagy.series_nav_js(:boostrap, ...)
pagy_boostrap_combo_nav_js(@pagy, ...) @pagy.input_nav_js(:boostrap, ...)
size: ...
Pagy.options[:size] = ...
slots: ...
Pagy.options[:slots] = ...
ends: false
Pagy.options[:end] = false
compact: true
Pagy.options[:compact] = true
  • FYI: The redundant pagy-bootstrap class has been removed from the input_nav_js body.
  • All the old helpers are now @pagy instance methods with more explicit names.
Search (old) Replace with (new)
pagy_bulma_nav(@pagy, ...) @pagy.series_nav(:bulma, ...)
pagy_bulma_nav_js(@pagy, ...) @pagy.series_nav_js(:bulma, ...)
pagy_bulma_combo_nav_js(@pagy, ...) @pagy.input_nav_js(:bulma, ...)
size: ...
Pagy.options[:size] = ...
slots: ...
Pagy.options[:slots] = ...
ends: false
Pagy.options[:end] = false
compact: true
Pagy.options[:compact] = true
  • FYI: The is-centered CSS class has been removed.
  • FYI: The previous/next links have been moved at the beginning and end of the pagination.
Search (old) Replace with (new)
pagy_countless(...) pagy(:countless, ...)
countless_minimal: true
Pagy.options[:countless_minimal] = true
headless: true
Pagy.options[:headless] = true
Search (old) Replace with (new)
pagy_calendar(...) pagy(:calendar, ...)
Pagy::Calendar::OutOfRangeError Pagy::RangeError
flag: active: true disabled: false
config: pagy: ... offset: ...
  • If your pagy-old.rb file contains any localization configuration, then uncomment and customize the following line in the pagy.rb initializer: Pagy::Calendar.localize_with_rails_i18n_gem(*your_locales).
    • Note: In non-Rails applications, calendar localization requires adding rails-i18n to your Gemfile.
  • Remove any existing Pagy::Calendar::*::DEFAULT. Pass the options to each unit when you paginate.
  • Active and passive modes are now handled by the same pagy method:
Search (old) Replace with (new)
pagy_elasticsearch_rails(...) pagy(:elasticsearch_rails, ...)
Pagy.new_from_elasticsearch_rails(...) pagy(:elasticsearch_rails, ...)
elasticsearch_rails_search: ...
Pagy.options[:elasticsearch_rails_search] = ...
search_method: ...
Pagy.options[:search_method] = ...
  • Customization of the pagy_search method name has been discontinued:
    • Remove any existing :elasticsearch_rails_pagy_search variable from your code.
    • Replace your custom method name with the standard pagy_search method.
  • Active and passive modes are now handled by the same pagy method:
Search (old) Replace with (new)
pagy_meilisearch(...) pagy(:meilisearch, ...)
Pagy.new_from_meilisearch(...) pagy(:meilisearch, ...)
meilisearch_search: ...
Pagy.options[:meilisearch_search] = ...
search_method: ...
Pagy.options[:search_method] = ...
  • Customization of the pagy_search method name has been discontinued:
    • Remove any existing :meilisearch_pagy_search variable from your code.
    • Replace your custom method name with the standard pagy_search method.
  • Active and passive modes are now handled by the same pagy method:
Search (old) Replace with (new)
pagy_searchkick(...) pagy(:searchkick, ...)
Pagy.new_from_searchkick(...) pagy(:searchkick, ...)
searchkick_search: ...
Pagy.options[:searchkick_search] = ...
search_method: ...
Pagy.options[:search_method] = ...
  • Customization of the pagy_search method name has been discontinued:
    • Remove any existing :searchkick_pagy_search variable from your code.
    • Replace your custom method name with the standard pagy_search method.
Search (old) Replace with (new)
pagy_headers(...) @pagy.headers_hash(...)
pagy_headers_merge response.headers.merge!(@pagy.headers_hash)
headers: ...
Pagy.options[:headers] = ...
headers_map: ...
Pagy.options[:headers_map] = ...
Search (old) Replace with (new)
pagy_jsonapi_links(@pagy, ...) @pagy.urls_hash(...)
  • Notice that the nil links are now removed as the JSON:API specifications require.
  • IMPORTANT: Enable the feature by explicitly setting the jsonapi: true option (in the initializer or pagy method).
Search (old) Replace with (new)
pagy_keyset(...) pagy(:keyset, ...)
pagy_keyset_first_url(@pagy, ...) @pagy.page_url(:first, ...)
pagy_keyset_next_url(@pagy, ...) @pagy.page_url(:next, ...)
filter_newest: ... override compose_predicate
  • Replace any existing :jsonify_keyset_attributes with :pre_serialize.
    • The lambda receives the same keyset_attributes argument, but it must modify the specific values directly. The lambda's return value is ignored. For example: ->(attrs) { attrs[:created_at] = attrs[:created_at].strftime('%F %T.%6N') }.
Search (old) Replace with (new)
limit_param: :... (symbol value) limit_key: '...' (string value)
limit_extra: ... delete
max_limit: ... delete
  • Enable the feature by setting client_max_limit: your_client_max_limit option (in the initializer or pagy method).
Search (old) Replace with (new)
pagy_metadata(@pagy, ...) @pagy.data_hash(...)
metadata: ...
Pagy.options[:metadata] = ...
data_keys: ...
Pagy.options[:data_keys] = ...
data_key -> :scaffold_url :url_template
  • The Pagy::OverflowError has been replaced by the Pagy::RangeError; however, it is no longer raised by default.
  • Pagy rescues the Pagy::RangeError and serves an empty page by default.
    • Now, Pagy behaves the same as it did before when requiring the overflow extra and using its default settings.
  • The legacy pagy.overflow? is now the pagy.in_range? method, which checks/returns the opposite state/boolean.
  • The overflow: :last_page behavior has been discontinued because it provides nearly no benefit:
    • Why there is little benefit in serving the last page?
      • The navigation bar for an out-of-range request is rendered identically to that of the last page.
      • The only difference is that there are no records/results to display.
      • The "previous page" button points to the last page, so if users truly want to see the last page results (which they have likely already seen), they can simply click the link.
  • Summary for keeping the same behavior:
    • The :overflow variable is not used anymore.
    • If you did not use the extra (i.e., Pagy raised errors), set raise_range_error: true.
    • If you used overflow: :empty_page or just required the overflow extra, simply remove it (this is now the default behavior).
    • If you used overflow: :last_page and still want this behavior despite the reasons above:
      • Set raise_range_error: true.
      • Use rescue Pagy::RangeError => e in your method.
      • Redirect to @pagy.page_url(:last).
  • Replace the :url variable with the :request Common option hash. For example:

    request: { base_url: 'http://www.example.com',
               path:     '/path',
               query:    { 'param1' => 1234 }, # The string-keyed hash query from the request 
               cookie:   'xyz' }               # The 'pagy' cookie, only for keynav  
  • If absolutely necessary, uncomment or add this line to your initializer: Pagy.translate_with_the_slower_i18n_gem!.
  • Due to extensive overwriting for minimal benefit, you can safely remove this feature from your app without noticeable impact. Remove all /gearbox/ related code.
  • Pagination bars similar to WillPaginate and Kaminari are not good for a lot of reasons. If still required, adapt the legacy file from a previous commit.
  • It was mostly useless and half-baked, causing many complications in both the Ruby and JavaScript code for no significant benefit.
  • Use an appropriate approach to address your requirement, such as utilizing URL rewriting at the HTTP server level.

# 3. Final steps

Search (old) Replace with (new)
include Pagy::Backend include Pagy::Method
include Pagy::Frontend remove (integrated)
Pagy.root. Pagy::ROOT.
page_param: :... (symbol value) page_key: '...' (string value)
pagy_info(@pagy, ...) @pagy.info_tag(...)
@pagy_locale = ... Pagy::I18n = ...
@pagy.pages @pagy.last
@pagy.vars @pagy.options
VariableError OptionError
<error>.variable <error>.option
count_args: ... remove (integrated)
anchor_string: ... remove (discontinued)
outset: ... remove (discontinued)
cycle: ... remove (discontinued)
  • Use the :querify option, which is a lambda that can modify the string-keyed query hash at will. It is a bit more verbose, but it's more powerful and low-level. It solves an incompatibility with the old high-level :params hash/lambda and improves performance. It is part of the Common URL Options group that gives you full and efficient control over the URL composition.
  • Example:
    # Old symbol-keyed, high-level hash variable
    params: { a: 1, b: 2 } 
    # New string-keyed, low-level, direct modification of the query hash
    querify: ->(q) { q.merge!('a' => 1, 'b' => 2) } 
    # It also allows to do things like:
    querify = ->(q) { q.except!('not_useful').merge!('custom' => 'useful') }
  • Use *previous* in all the options, accessors, methods, etc.

# 4. Finalize the upgrade

  • If your pagy-old.rb contains any JavaScript setup, it should still work, so you can move it to the pagy.rb file, however, for apps with builders, consider using the new Pagy.sync_javascript.
  • If your pagy-old.rb contains the Pagy::I18n setup, and the setup includes some custom dictionary file, then uncomment and set up the relevant Pagy::I18n lookup section in the pagy.rb file. (See the I18n docs for details)
  • Update your custom dictionary files (if any) to the new dictionary structure, or they won't work correctly.
  • Besides that, you don't need any line of the old setup, because all the locales are autoloaded when your app uses them.
  • Remove all the I18n code from the pagy-old.rb.
  • Overriding methods in controllers/helpers is not possible or discouraged.
  • The cleanest approach for local overriding is via Ruby refinements or the initializer for global override.
  • Check the How To Override Pagy Method.
  • If your pagy-old.rb contains overridden methods, copy the methods over to the pagy.rb initializer, however, consider that:
    • Internal Pagy protected methods have been extensively refactored, likely renamed, and occasionally removed.
    • You should reconcile internal overrides by reviewing the updated Pagy codebase.

You may want also to check these internal renaming:

Search (old) Replace with (new)
pagy_url_for(@pagy @pagy.page_url(
pagy_anchor(pagy pagy.a_lambda(
pagy_t I18n.translate
label_for page_label
label page_label
  • At this point, there should be no more code in the pagy-old.rb.