diff --git a/Gemfile b/Gemfile index db632566400..e8aacdc0d08 100644 --- a/Gemfile +++ b/Gemfile @@ -19,7 +19,7 @@ gem "good_job", "~> 3.99" gem "gravtastic", "~> 3.2" gem "honeybadger", "~> 5.5.1", require: false # see https://github.com/rubygems/rubygems.org/pull/4598 gem "http_accept_language", "~> 2.1" -gem "kaminari", "~> 1.2" +gem "pagy", "~> 9.0" gem "launchdarkly-server-sdk", "~> 8.7" gem "mail", "~> 2.8" gem "octokit", "~> 9.1" diff --git a/Gemfile.lock b/Gemfile.lock index 5130eaa33e8..fe0c4258a03 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -326,18 +326,6 @@ GEM faraday (~> 2.0) faraday-follow_redirects jwt (2.7.1) - kaminari (1.2.2) - activesupport (>= 4.1.0) - kaminari-actionview (= 1.2.2) - kaminari-activerecord (= 1.2.2) - kaminari-core (= 1.2.2) - kaminari-actionview (1.2.2) - actionview - kaminari-core (= 1.2.2) - kaminari-activerecord (1.2.2) - activerecord - kaminari-core (= 1.2.2) - kaminari-core (1.2.2) language_server-protocol (3.17.0.3) launchdarkly-server-sdk (8.7.0) concurrent-ruby (~> 1.1) @@ -836,7 +824,6 @@ DEPENDENCIES honeybadger (~> 5.5.1) http_accept_language (~> 2.1) importmap-rails (~> 2.0) - kaminari (~> 1.2) launchdarkly-server-sdk (~> 8.7) launchy (~> 3.0) letter_opener (~> 1.10) @@ -1035,10 +1022,6 @@ CHECKSUMS json (2.7.2) sha256=1898b5cbc81cd36c0fd4d0b7ad2682c39fb07c5ff682fc6265f678f550d4982c json-jwt (1.16.6) sha256=ab451f9cd8743cecc4137f4170806046c1d8a6d4ee6e8570e0b5c958409b266c jwt (2.7.1) sha256=07357cd2f180739b2f8184eda969e252d850ac996ed0a23f616e8ff0a90ae19b - kaminari (1.2.2) sha256=c4076ff9adccc6109408333f87b5c4abbda5e39dc464bd4c66d06d9f73442a3e - kaminari-actionview (1.2.2) sha256=1330f6fc8b59a4a4ef6a549ff8a224797289ebf7a3a503e8c1652535287cc909 - kaminari-activerecord (1.2.2) sha256=0dd3a67bab356a356f36b3b7236bcb81cef313095365befe8e98057dd2472430 - kaminari-core (1.2.2) sha256=3bd26fec7370645af40ca73b9426a448d09b8a8ba7afa9ba3c3e0d39cdbb83ff language_server-protocol (3.17.0.3) sha256=3d5c58c02f44a20d972957a9febe386d7e7468ab3900ce6bd2b563dd910c6b3f launchdarkly-server-sdk (8.7.0) sha256=062e7dc42c2884ed9deed42c49937917faa911128a659386e4bd45d1ad1506eb launchy (3.0.1) sha256=b7fa60bda0197cf57614e271a250a8ca1f6a34ab889a3c73f67ec5d57c8a7f2c diff --git a/app/assets/stylesheets/modules/nav/nav--paginated.css b/app/assets/stylesheets/modules/nav/nav--paginated.css index 54ae75fda4d..c7669fbc49d 100644 --- a/app/assets/stylesheets/modules/nav/nav--paginated.css +++ b/app/assets/stylesheets/modules/nav/nav--paginated.css @@ -46,32 +46,27 @@ margin-right: 8px; margin-left: 8px; } -.pagination { +nav.pagy { margin-top: 90px; position: relative; width: 100%; overflow: auto; text-align: center; } - .pagination a, .pagination em { + nav.pagy a, nav.pagy em { margin-right: 8px; margin-left: 8px; font-style: normal; } - .pagination a { + nav.pagy a { color: #9da2ab; transition-duration: 0.25s; transition-property: color; outline: none; } - .pagination a:hover, .pagination a:focus, .pagination a:active { + nav.pagy a:hover, nav.pagy a:focus, nav.pagy a:active { color: #141c22; } - .pagination .previous_page.disabled, .pagination .next_page.disabled { + nav.pagy a:first-child:not([href]), nav.pagy a:last-child:not([href]) { color: #9da2ab; } - .pagination .previous_page.disabled:hover, .pagination .previous_page.disabled:focus, .pagination .previous_page.disabled:active, .pagination .next_page.disabled:hover, .pagination .next_page.disabled:focus, .pagination .next_page.disabled:active { + nav.pagy a:first-child:not([href]):hover, nav.pagy a:first-child:not([href]):focus, nav.pagy a:first-child:not([href]):active, nav.pagy nav.pagy a:last-child:not([href]):hover, nav.pagy nav.pagy a:last-child:not([href]):focus, nav.pagy nav.pagy a:last-child:not([href]):active { color: #9da2ab; } - .pagination .previous_page { - position: absolute; - left: 0; } - .pagination .next_page { - position: absolute; - right: 0; } - .pagination .current { + nav.pagy .current { + color: #000; margin: 0 8px; } diff --git a/app/controllers/api/v1/timeframe_versions_controller.rb b/app/controllers/api/v1/timeframe_versions_controller.rb index 31007ea9a31..e8458e4a690 100644 --- a/app/controllers/api/v1/timeframe_versions_controller.rb +++ b/app/controllers/api/v1/timeframe_versions_controller.rb @@ -6,9 +6,7 @@ class InvalidTimeframeParameterError < StandardError; end MAXIMUM_TIMEFRAME_QUERY_IN_DAYS = 7 def index - render_rubygems( - Version.created_between(from_time, to_time).page(@page) - ) + render_rubygems(*pagy(Version.created_between(from_time, to_time))) end private @@ -35,12 +33,13 @@ def to_time raise InvalidTimeframeParameterError, 'the "to" parameter must be iso8601 formatted' end - def render_rubygems(versions) + def render_rubygems(pagy, versions) rubygems = versions.includes(:dependencies, :gem_download, rubygem: %i[linkset gem_download]).map do |version| payload = version.rubygem.payload(version) payload.merge(version.as_json) end + pagy_headers_merge(pagy) respond_to do |format| format.json { render json: rubygems } format.yaml { render yaml: rubygems } diff --git a/app/controllers/api_keys_controller.rb b/app/controllers/api_keys_controller.rb index 65663f0cff9..773d74d934c 100644 --- a/app/controllers/api_keys_controller.rb +++ b/app/controllers/api_keys_controller.rb @@ -8,8 +8,8 @@ class ApiKeysController < ApplicationController verify_session_before def index - @api_key = session.delete(:api_key) - @api_keys = current_user.api_keys.unexpired.not_oidc.preload(ownership: :rubygem).page(@page) + @api_key = session.delete(:api_key) + @api_keys_pagy, @api_keys = pagy(current_user.api_keys.unexpired.not_oidc.preload(ownership: :rubygem)) redirect_to new_profile_api_key_path if @api_keys.empty? end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d017ae5a988..2e3c0373344 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -3,6 +3,7 @@ class ApplicationController < ActionController::Base include Pundit::Authorization include ApplicationMultifactorMethods include TraceTagger + include Pagy::Backend helper ActiveSupport::NumberHelper diff --git a/app/controllers/news_controller.rb b/app/controllers/news_controller.rb index 6a5009ca455..f7f3861ee5a 100644 --- a/app/controllers/news_controller.rb +++ b/app/controllers/news_controller.rb @@ -2,19 +2,20 @@ class NewsController < ApplicationController before_action -> { set_page Gemcutter::NEWS_MAX_PAGES } def show - @rubygems = Rubygem.preload(:latest_version, :gem_download) - .news(Gemcutter::NEWS_DAYS_LIMIT) - .page(@page) - .per(Gemcutter::NEWS_PER_PAGE) + @rubygems_pagy, @rubygems = pagy( + Rubygem.preload(:latest_version, :gem_download) + .news(Gemcutter::NEWS_DAYS_LIMIT), + limit: Gemcutter::NEWS_MAX_PAGES + ) + limit_total_count end def popular @title = t(".title") - @rubygems = Rubygem.preload(:latest_version, :gem_download) - .popular(Gemcutter::POPULAR_DAYS_LIMIT) - .page(@page) - .per(Gemcutter::NEWS_PER_PAGE) + @rubygems_pagy, @rubygems = pagy(Rubygem.preload(:latest_version, :gem_download) + .popular(Gemcutter::POPULAR_DAYS_LIMIT), + limit: Gemcutter::NEWS_MAX_PAGES) limit_total_count render :show diff --git a/app/controllers/oidc/api_key_roles_controller.rb b/app/controllers/oidc/api_key_roles_controller.rb index 77c1facd77a..44b73f28132 100644 --- a/app/controllers/oidc/api_key_roles_controller.rb +++ b/app/controllers/oidc/api_key_roles_controller.rb @@ -11,15 +11,11 @@ class OIDC::ApiKeyRolesController < ApplicationController before_action :set_page, only: :index def index - @api_key_roles = current_user.oidc_api_key_roles.active.includes(:provider) - .page(@page) - .strict_loading + @api_key_roles_pagy, @api_key_roles = pagy(current_user.oidc_api_key_roles.active.includes(:provider).strict_loading) end def show - @id_tokens = @api_key_role.id_tokens.order(id: :desc).includes(:api_key) - .page(0).per(10) - .strict_loading + @id_tokens_pagy, @id_tokens = pagy(@api_key_role.id_tokens.order(id: :desc).includes(:api_key).strict_loading, limit: 10) respond_to do |format| format.json do render json: @api_key_role diff --git a/app/controllers/oidc/providers_controller.rb b/app/controllers/oidc/providers_controller.rb index 5bd58d3c759..996199ce01f 100644 --- a/app/controllers/oidc/providers_controller.rb +++ b/app/controllers/oidc/providers_controller.rb @@ -8,8 +8,8 @@ class OIDC::ProvidersController < ApplicationController before_action :set_page, only: :index def index - providers = OIDC::Provider.strict_loading.page(@page) - render OIDC::Providers::IndexView.new(providers:) + providers_pagy, providers = pagy(OIDC::Provider.strict_loading) + render OIDC::Providers::IndexView.new(providers_pagy:, providers:) end def show diff --git a/app/controllers/ownership_calls_controller.rb b/app/controllers/ownership_calls_controller.rb index 67fd36844cf..bff7ec02f04 100644 --- a/app/controllers/ownership_calls_controller.rb +++ b/app/controllers/ownership_calls_controller.rb @@ -13,9 +13,10 @@ class OwnershipCallsController < ApplicationController def index set_page - @ownership_calls = OwnershipCall.opened.includes(:user, rubygem: %i[latest_version gem_download]).order(created_at: :desc) - .page(@page) - .per(Gemcutter::OWNERSHIP_CALLS_PER_PAGE) + @ownership_calls_pagy, @ownership_calls = pagy( + OwnershipCall.opened.includes(:user, rubygem: %i[latest_version gem_download]).order(created_at: :desc), + limit: Gemcutter::OWNERSHIP_CALLS_PER_PAGE + ) end def create diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 1adbf233817..dd79909ca95 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -53,8 +53,8 @@ def adoptions end def security_events - @security_events = current_user.events.order(id: :desc).page(params[:page]).per(50) - render Profiles::SecurityEventsView.new(security_events: @security_events) + @security_events_pagy, @security_events = pagy(current_user.events.order(id: :desc), limit: 50) + render Profiles::SecurityEventsView.new(security_events_pagy: @security_events_pagy, security_events: @security_events) end private diff --git a/app/controllers/reverse_dependencies_controller.rb b/app/controllers/reverse_dependencies_controller.rb index 706668aeff9..2a4eabd5481 100644 --- a/app/controllers/reverse_dependencies_controller.rb +++ b/app/controllers/reverse_dependencies_controller.rb @@ -11,6 +11,6 @@ def index .preload(:gem_download, :latest_version) @reverse_dependencies = @reverse_dependencies.legacy_search(params[:rdeps_query]) if params[:rdeps_query].is_a?(String) - @reverse_dependencies = @reverse_dependencies.page(@page).without_count + @reverse_dependencies_pagy, @reverse_dependencies = pagy_countless(@reverse_dependencies) end end diff --git a/app/controllers/rubygems_controller.rb b/app/controllers/rubygems_controller.rb index e5a9ef07d30..5a467ea215a 100644 --- a/app/controllers/rubygems_controller.rb +++ b/app/controllers/rubygems_controller.rb @@ -11,7 +11,7 @@ def index respond_to do |format| format.html do @letter = Rubygem.letterize(gem_params[:letter]) - @gems = Rubygem.letter(@letter).includes(:latest_version, :gem_download).page(@page) + @gems_pagy, @gems = pagy(Rubygem.letter(@letter).includes(:latest_version, :gem_download), limit: 2) end format.atom do @versions = Version.published.limit(Gemcutter::DEFAULT_PAGINATION) @@ -32,8 +32,8 @@ def show def security_events authorize @rubygem, :show_events? - @security_events = @rubygem.events.order(id: :desc).page(params[:page]).per(50) - render Rubygems::SecurityEventsView.new(rubygem: @rubygem, security_events: @security_events) + @security_events_pagy, @security_events = pagy(@rubygem.events.order(id: :desc), limit: 50) + render Rubygems::SecurityEventsView.new(rubygem: @rubygem, security_events_pagy: @security_events_pagy, security_events: @security_events) end private diff --git a/app/controllers/searches_controller.rb b/app/controllers/searches_controller.rb index 6cf5b058b32..bae489ee5ad 100644 --- a/app/controllers/searches_controller.rb +++ b/app/controllers/searches_controller.rb @@ -6,7 +6,8 @@ def show @error_msg, @gems = ElasticSearcher.new(params[:query], page: @page).search return unless @gems - set_total_pages if @gems.total_count > Gemcutter::SEARCH_MAX_PAGES * Rubygem.default_per_page + @gems_pagy = Pagy.new_from_searchkick(@gems) + set_total_pages if @gems.total_count > 10_000 exact_match = Rubygem.name_is(params[:query]).first @yanked_gem = exact_match unless exact_match&.indexed_versions? @yanked_filter = true if params[:yanked] == "true" diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 8b76902f9fd..b1957c7833f 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -5,7 +5,8 @@ def index @number_of_gems = Rubygem.total_count @number_of_users = User.count @number_of_downloads = GemDownload.total_count - @most_downloaded = Rubygem.by_downloads.includes(:gem_download).page(@page).per(Gemcutter::STATS_PER_PAGE) + @most_downloaded_pagy, + @most_downloaded = pagy(Rubygem.by_downloads.includes(:gem_download), limit: Gemcutter::STATS_PER_PAGE) @most_downloaded_count = GemDownload.most_downloaded_gem_count limit_total_count end diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index a2ce84c89f1..d4d9c196e86 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -3,7 +3,7 @@ class VersionsController < ApplicationController def index set_page - @versions = @rubygem.versions.by_position.page(@page).per(Gemcutter::VERSIONS_PER_PAGE) + @versions_pagy, @versions = pagy(@rubygem.versions.by_position, limit: Gemcutter::VERSIONS_PER_PAGE) end def show diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 62447d8a786..36de3576ecf 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,5 +1,6 @@ module ApplicationHelper include BetterHtml::Helpers + include Pagy::Frontend def page_title combo = "#{t :title} | #{t :subtitle}" @@ -59,12 +60,6 @@ def active?(path) "is-active" if request.path_info == path end - # replacement for Kaminari::ActionViewExtension#paginate - # only shows `next` and `prev` links and not page numbers, saving a COUNT(DISTINCT ..) query - def plain_paginate(items) - render "layouts/plain_paginate", items: items - end - def content_for_title(title, title_url) return title unless title_url link_to title, title_url, class: "t-link--black" diff --git a/app/views/api_keys/index.html.erb b/app/views/api_keys/index.html.erb index c1816ee1a9c..a7e843334f3 100644 --- a/app/views/api_keys/index.html.erb +++ b/app/views/api_keys/index.html.erb @@ -11,7 +11,7 @@

- <%= page_entries_info @api_keys, entry_name: 'API keys' %> + <%== pagy_info @api_keys_pagy, item_name: 'API keys' %>

@@ -121,7 +121,7 @@ - <%= paginate @api_keys %> + <%== pagy_nav @api_keys_pagy if @api_keys_pagy.pages > 1 %>

<%= button_to t(".new_key"), new_profile_api_key_path, method: "get", class: "form__submit" %>

<% if current_user.oidc_api_key_roles.any? %> diff --git a/app/views/components/events/table_component.rb b/app/views/components/events/table_component.rb index e6955d33857..53e26d6fe39 100644 --- a/app/views/components/events/table_component.rb +++ b/app/views/components/events/table_component.rb @@ -9,16 +9,17 @@ class Events::TableComponent < ApplicationComponent extend Phlex::Rails::HelperMacros register_value_helper :current_user - register_value_helper :page_entries_info - register_value_helper :paginate + register_value_helper :pagy_info + register_value_helper :pagy_nav extend Dry::Initializer + option :security_events_pagy option :security_events def view_template header(class: "gems__header push--s") do - p(class: "gems__meter l-mb-0") { plain page_entries_info(security_events) } + p(class: "gems__meter l-mb-0") { plain pagy_info(security_events_pagy, item_name: "security events") } end if security_events.any? @@ -39,7 +40,7 @@ def view_template end end - plain paginate(security_events) + plain pagy_nav(security_events_pagy) if security_events_pagy.pages > 1 end private diff --git a/app/views/layouts/_plain_paginate.html.erb b/app/views/layouts/_plain_paginate.html.erb index dd0a76bed34..decbf206a4b 100644 --- a/app/views/layouts/_plain_paginate.html.erb +++ b/app/views/layouts/_plain_paginate.html.erb @@ -1,4 +1,4 @@ diff --git a/app/views/news/show.html.erb b/app/views/news/show.html.erb index 62815a3dec3..7959c0973a7 100644 --- a/app/views/news/show.html.erb +++ b/app/views/news/show.html.erb @@ -6,8 +6,8 @@
-

<%= page_entries_info @rubygems %>

+

<%== pagy_info @rubygems_pagy, item_name: 'gems' %>

<%= render partial: "news/rubygem", collection: @rubygems %> -<%= paginate @rubygems %> +<%== pagy_nav @rubygems_pagy if @rubygems_pagy.pages > 1 %> diff --git a/app/views/oidc/api_key_roles/index.html.erb b/app/views/oidc/api_key_roles/index.html.erb index a2ea9b7bea1..6fbb41a4950 100644 --- a/app/views/oidc/api_key_roles/index.html.erb +++ b/app/views/oidc/api_key_roles/index.html.erb @@ -4,7 +4,7 @@ <%= button_to(t(".new_role"), new_profile_oidc_api_key_role_path, method: "get", class: "form__submit") %>

-

<%= page_entries_info(@api_key_roles) %>

+

<%== pagy_info(@api_key_roles_pagy) %>

@@ -36,5 +36,5 @@ <% end %>
- <%= paginate @api_key_roles %> + <%== pagy_nav @api_key_roles_pagy if @api_key_roles_pagy.pages > 1 %> diff --git a/app/views/oidc/api_key_roles/show.html.erb b/app/views/oidc/api_key_roles/show.html.erb index aa7f645f094..5786c2821e2 100644 --- a/app/views/oidc/api_key_roles/show.html.erb +++ b/app/views/oidc/api_key_roles/show.html.erb @@ -32,7 +32,7 @@

<%= OIDC::ApiKeyRole.human_attribute_name(:id_tokens) %>

-

<%= page_entries_info(@id_tokens) %>

+

<%== pagy_info(@id_tokens_pagy, item_name: "ID Tokens") %>

<% if @id_tokens.present? %> <%= render OIDC::IdToken::TableComponent.new(id_tokens: @id_tokens) %> diff --git a/app/views/oidc/providers/index_view.rb b/app/views/oidc/providers/index_view.rb index 1c40139c29c..ecc6a040d2b 100644 --- a/app/views/oidc/providers/index_view.rb +++ b/app/views/oidc/providers/index_view.rb @@ -3,9 +3,10 @@ class OIDC::Providers::IndexView < ApplicationView include Phlex::Rails::Helpers::LinkTo - attr_reader :providers + attr_reader :providers_pagy, :providers - def initialize(providers:) + def initialize(providers_pagy:, providers:) + @providers_pagy = providers_pagy @providers = providers super() end @@ -19,14 +20,14 @@ def view_template end hr header(class: "gems__header push--s") do - p(class: "gems__meter l-mb-0") { plain helpers.page_entries_info(providers) } + p(class: "gems__meter l-mb-0") { plain helpers.pagy_info(providers_pagy) } end ul do providers.each do |provider| li { link_to provider.issuer, profile_oidc_provider_path(provider) } end end - plain helpers.paginate(providers) + plain helpers.pagy_nav(providers_pagy) if providers_pagy.pages > 1 end end end diff --git a/app/views/oidc/providers/show_view.rb b/app/views/oidc/providers/show_view.rb index 86d376e0409..315da25e4fc 100644 --- a/app/views/oidc/providers/show_view.rb +++ b/app/views/oidc/providers/show_view.rb @@ -30,9 +30,9 @@ def view_template h3(class: "t-list__heading") { "Roles" } div(class: "") do - api_key_roles = helpers.current_user.oidc_api_key_roles.where(provider:).page(0).per(10) + api_key_roles_pagy, api_key_roles = Pagy::Backend.pagy(current_user.oidc_api_key_roles.where(provider:), page: 0, items: 10) header(class: "gems__header push--s") do - p(class: "gems__meter l-mb-0") { plain helpers.page_entries_info(api_key_roles) } + p(class: "gems__meter l-mb-0") { plain helpers.pagy_info(api_key_roles_pagy) } end render OIDC::ApiKeyRole::TableComponent.new(api_key_roles:) if api_key_roles.present? end diff --git a/app/views/ownership_calls/index.html.erb b/app/views/ownership_calls/index.html.erb index e659c844940..75d52099b03 100644 --- a/app/views/ownership_calls/index.html.erb +++ b/app/views/ownership_calls/index.html.erb @@ -7,7 +7,7 @@

- <%= page_entries_info @ownership_calls, entry_name: 'ownership calls' %> + <%== pagy_info @ownership_calls_pagy, item_name: 'ownership calls' %>

@@ -15,4 +15,4 @@ <%= render partial: "apply", layout: "ownership_call", collection: @ownership_calls, as: :ownership_call %>
-<%= paginate @ownership_calls %> +<%== pagy_nav @ownership_calls_pagy if @ownership_calls_pagy.size > 1 %> diff --git a/app/views/profiles/security_events_view.rb b/app/views/profiles/security_events_view.rb index 31e4f133609..e01d59f5dda 100644 --- a/app/views/profiles/security_events_view.rb +++ b/app/views/profiles/security_events_view.rb @@ -8,6 +8,7 @@ class Profiles::SecurityEventsView < ApplicationView include Phlex::Rails::Helpers::LinkTo extend Dry::Initializer + option :security_events_pagy option :security_events def view_template @@ -18,7 +19,7 @@ def view_template t(".description_html") end - render Events::TableComponent.new(security_events: security_events) + render Events::TableComponent.new(security_events_pagy:, security_events:) end end diff --git a/app/views/reverse_dependencies/index.html.erb b/app/views/reverse_dependencies/index.html.erb index 93a6533b12e..d54444a1422 100644 --- a/app/views/reverse_dependencies/index.html.erb +++ b/app/views/reverse_dependencies/index.html.erb @@ -15,7 +15,7 @@
<%= render partial: "rubygems/rubygem", collection: @reverse_dependencies %>
- <%= plain_paginate @reverse_dependencies %> + <%== pagy_nav @reverse_dependencies_pagy if @reverse_dependencies_pagy.pages > 1 %> <% else %>
diff --git a/app/views/rubygems/index.html.erb b/app/views/rubygems/index.html.erb index d8cf166c2f6..cb6ba78ee36 100644 --- a/app/views/rubygems/index.html.erb +++ b/app/views/rubygems/index.html.erb @@ -6,4 +6,4 @@ <%= render @gems %> -<%= paginate @gems %> +<%== pagy_nav(@gems_pagy) if @gems_pagy.pages > 1 %> diff --git a/app/views/rubygems/security_events_view.rb b/app/views/rubygems/security_events_view.rb index 79c2e01bd71..58a70fcaaff 100644 --- a/app/views/rubygems/security_events_view.rb +++ b/app/views/rubygems/security_events_view.rb @@ -7,6 +7,7 @@ class Rubygems::SecurityEventsView < ApplicationView extend Dry::Initializer option :rubygem + option :security_events_pagy option :security_events def view_template @@ -17,7 +18,7 @@ def view_template t(".description_html", gem: helpers.link_to(rubygem.name, rubygem_path(rubygem.slug))) end - render Events::TableComponent.new(security_events: security_events) + render Events::TableComponent.new(security_events_pagy:, security_events:) end end diff --git a/app/views/searches/show.html.erb b/app/views/searches/show.html.erb index 2075bfb7830..b71b768e2af 100644 --- a/app/views/searches/show.html.erb +++ b/app/views/searches/show.html.erb @@ -25,7 +25,7 @@ <% @subtitle = t('.subtitle_html', :query => params[:query]) %>
-

<%= page_entries_info(@gems, :entry_name => 'gem') %>

+

<%== pagy_info(@gems_pagy, :item_name => 'gem') %>

<%= render partial: 'aggregations', locals: { gems: @gems } %> @@ -44,7 +44,7 @@ <% if @gems.present? %> <%= render partial: 'rubygems/rubygem', collection: @gems %> - <%= paginate @gems %> + <%== pagy_nav @gems_pagy if @gems_pagy.pages > 1 %> <% end %> <% end %> <% end %> diff --git a/app/views/stats/index.html.erb b/app/views/stats/index.html.erb index 69555fc487f..0808b774ec6 100644 --- a/app/views/stats/index.html.erb +++ b/app/views/stats/index.html.erb @@ -20,7 +20,7 @@
-

<%= page_entries_info @most_downloaded, entry_name: 'gem' %>

+

<%== pagy_info @most_downloaded_pagy, item_name: 'gem' %>

<% @most_downloaded.each do |gem| %> @@ -33,4 +33,4 @@
<% end %> -<%= paginate @most_downloaded %> +<%== pagy_nav @most_downloaded_pagy if @most_downloaded_pagy.pages > 1 %> diff --git a/app/views/versions/index.html.erb b/app/views/versions/index.html.erb index 94620b1f37d..b4303a24f1e 100644 --- a/app/views/versions/index.html.erb +++ b/app/views/versions/index.html.erb @@ -4,12 +4,12 @@

<%= t('.not_hosted_notice') %>

<% else %> -

<%= t('.versions_since', :count => @versions.total_count, :since => nice_date_for(@versions.map(&:authored_at).min)) %>:

+

<%= t('.versions_since', :count => @versions_pagy.count, :since => nice_date_for(@versions.map(&:authored_at).min)) %>:

- <%= paginate @versions %> + <%== pagy_nav @versions_pagy if @versions_pagy.pages > 1 %>
<% end %> diff --git a/config/initializers/kaminari_config.rb b/config/initializers/kaminari_config.rb deleted file mode 100644 index 013e5b3cdd7..00000000000 --- a/config/initializers/kaminari_config.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -Kaminari.configure do |config| - config.default_per_page = 30 - # config.max_per_page = nil - # config.window = 4 - # config.outer_window = 0 - # config.left = 0 - # config.right = 0 - # config.page_method_name = :page - # config.param_name = :page - # config.params_on_first_page = false -end diff --git a/config/initializers/pagy.rb b/config/initializers/pagy.rb new file mode 100644 index 00000000000..f7ea6125d5b --- /dev/null +++ b/config/initializers/pagy.rb @@ -0,0 +1,212 @@ +# frozen_string_literal: true + +# Pagy initializer file (9.0.2) +# Customize only what you really need and notice that the core Pagy works also without any of the following lines. +# Should you just cherry pick part of this file, please maintain the require-order of the extras + +# Pagy Variables +# See https://ddnexus.github.io/pagy/docs/api/pagy#variables +# You can set any pagy variable as a Pagy::DEFAULT. They can also be overridden per instance by just passing them to +# Pagy.new|Pagy::Countless.new|Pagy::Calendar::*.new or any of the #pagy* controller methods +# Here are the few that make more sense as DEFAULTs: +Pagy::DEFAULT[:limit] = 30 # default 20 +Pagy::DEFAULT[:size] = 11 # default 7 +# Pagy::DEFAULT[:ends] = true # default +# Pagy::DEFAULT[:page_param] = :page # default +# Pagy::DEFAULT[:count_args] = [] # example for non AR ORMs +# Pagy::DEFAULT[:max_pages] = 3000 # example + +# Extras +# See https://ddnexus.github.io/pagy/categories/extra + +# Legacy Compatibility Extras + +# Size extra: Enable the Array type for the `:size` variable (e.g. `size: [1,4,4,1]`) +# See https://ddnexus.github.io/pagy/docs/extras/size +# require 'pagy/extras/size' # must be required before the other extras + +# Backend Extras + +# Arel extra: For better performance utilizing grouped ActiveRecord collections: +# See: https://ddnexus.github.io/pagy/docs/extras/arel +# require 'pagy/extras/arel' + +# Array extra: Paginate arrays efficiently, avoiding expensive array-wrapping and without overriding +# See https://ddnexus.github.io/pagy/docs/extras/array +# require 'pagy/extras/array' + +# Calendar extra: Add pagination filtering by calendar time unit (year, quarter, month, week, day) +# See https://ddnexus.github.io/pagy/docs/extras/calendar +# require 'pagy/extras/calendar' +# Default for each calendar unit class in IRB: +# >> Pagy::Calendar::Year::DEFAULT +# >> Pagy::Calendar::Quarter::DEFAULT +# >> Pagy::Calendar::Month::DEFAULT +# >> Pagy::Calendar::Week::DEFAULT +# >> Pagy::Calendar::Day::DEFAULT +# +# Uncomment the following lines, if you need calendar localization without using the I18n extra +# module LocalizePagyCalendar +# def localize(time, opts) +# ::I18n.l(time, **opts) +# end +# end +# Pagy::Calendar.prepend LocalizePagyCalendar + +# Countless extra: Paginate without any count, saving one query per rendering +# See https://ddnexus.github.io/pagy/docs/extras/countless +# require 'pagy/extras/countless' +# Pagy::DEFAULT[:countless_minimal] = false # default (eager loading) + +# Elasticsearch Rails extra: Paginate `ElasticsearchRails::Results` objects +# See https://ddnexus.github.io/pagy/docs/extras/elasticsearch_rails +# Default :pagy_search method: change only if you use also +# the searchkick or meilisearch extra that defines the same +# Pagy::DEFAULT[:elasticsearch_rails_pagy_search] = :pagy_search +# Default original :search method called internally to do the actual search +# Pagy::DEFAULT[:elasticsearch_rails_search] = :search +# require 'pagy/extras/elasticsearch_rails' + +# Headers extra: http response headers (and other helpers) useful for API pagination +# See http://ddnexus.github.io/pagy/extras/headers +require 'pagy/extras/headers' +# Pagy::DEFAULT[:headers] = { page: 'Current-Page', +# limit: 'Page-Items', +# count: 'Total-Count', +# pages: 'Total-Pages' } # default + +# Keyset extra: Paginate with the Pagy keyset pagination technique +# See http://ddnexus.github.io/pagy/extras/keyset +# require 'pagy/extras/keyset' + +# Meilisearch extra: Paginate `Meilisearch` result objects +# See https://ddnexus.github.io/pagy/docs/extras/meilisearch +# Default :pagy_search method: change only if you use also +# the elasticsearch_rails or searchkick extra that define the same method +# Pagy::DEFAULT[:meilisearch_pagy_search] = :pagy_search +# Default original :search method called internally to do the actual search +# Pagy::DEFAULT[:meilisearch_search] = :ms_search +# require 'pagy/extras/meilisearch' + +# Metadata extra: Provides the pagination metadata to Javascript frameworks like Vue.js, react.js, etc. +# See https://ddnexus.github.io/pagy/docs/extras/metadata +# you must require the JS Tools internal extra (BEFORE the metadata extra) ONLY if you need also the :sequels +# require 'pagy/extras/js_tools' +# require 'pagy/extras/metadata' +# For performance reasons, you should explicitly set ONLY the metadata you use in the frontend +# Pagy::DEFAULT[:metadata] = %i[scaffold_url page prev next last] # example + +# Searchkick extra: Paginate `Searchkick::Results` objects +# See https://ddnexus.github.io/pagy/docs/extras/searchkick +# Default :pagy_search method: change only if you use also +# the elasticsearch_rails or meilisearch extra that defines the same +# DEFAULT[:searchkick_pagy_search] = :pagy_search +# Default original :search method called internally to do the actual search +# Pagy::DEFAULT[:searchkick_search] = :search +require 'pagy/extras/searchkick' +# uncomment if you are going to use Searchkick.pagy_search +# Searchkick.extend Pagy::Searchkick + +# Frontend Extras + +# Bootstrap extra: Add nav, nav_js and combo_nav_js helpers and templates for Bootstrap pagination +# See https://ddnexus.github.io/pagy/docs/extras/bootstrap +# require 'pagy/extras/bootstrap' + +# Bulma extra: Add nav, nav_js and combo_nav_js helpers and templates for Bulma pagination +# See https://ddnexus.github.io/pagy/docs/extras/bulma +# require 'pagy/extras/bulma' + +# Pagy extra: Add the pagy styled versions of the javascript-powered navs +# and a few other components to the Pagy::Frontend module. +# See https://ddnexus.github.io/pagy/docs/extras/pagy +# require 'pagy/extras/pagy' + +# Multi size var used by the *_nav_js helpers +# See https://ddnexus.github.io/pagy/docs/extras/pagy#steps +# Pagy::DEFAULT[:steps] = { 0 => 5, 540 => 7, 720 => 9 } # example + +# Feature Extras + +# Gearbox extra: Automatically change the limit per page depending on the page number +# See https://ddnexus.github.io/pagy/docs/extras/gearbox +# require 'pagy/extras/gearbox' +# set to false only if you want to make :gearbox_extra an opt-in variable +# Pagy::DEFAULT[:gearbox_extra] = false # default true +# Pagy::DEFAULT[:gearbox_limit] = [15, 30, 60, 100] # default + +# Limit extra: Allow the client to request a custom limit per page with an optional selector UI +# See https://ddnexus.github.io/pagy/docs/extras/limit +# require 'pagy/extras/limit' +# set to false only if you want to make :limit_extra an opt-in variable +# Pagy::DEFAULT[:limit_extra] = false # default true +# Pagy::DEFAULT[:limit_param] = :limit # default +# Pagy::DEFAULT[:limit_max] = 100 # default + +# Overflow extra: Allow for easy handling of overflowing pages +# See https://ddnexus.github.io/pagy/docs/extras/overflow +require 'pagy/extras/overflow' +Pagy::DEFAULT[:overflow] = :empty_page # default (other options: :last_page and :exception) + +# Trim extra: Remove the page=1 param from links +# See https://ddnexus.github.io/pagy/docs/extras/trim +require 'pagy/extras/trim' +# set to false only if you want to make :trim_extra an opt-in variable +# Pagy::DEFAULT[:trim_extra] = false # default true + +# Standalone extra: Use pagy in non Rack environment/gem +# See https://ddnexus.github.io/pagy/docs/extras/standalone +# require 'pagy/extras/standalone' +# Pagy::DEFAULT[:url] = 'http://www.example.com/subdir' # optional default + +# Jsonapi extra: Implements JSON:API specifications +# See https://ddnexus.github.io/pagy/docs/extras/jsonapi +# require 'pagy/extras/jsonapi' # must be required after the other extras +# set to false only if you want to make :jsonapi an opt-in variable +# Pagy::DEFAULT[:jsonapi] = false # default true + +# Rails +# Enable the .js file required by the helpers that use javascript +# (pagy*_nav_js, pagy*_combo_nav_js, and pagy_limit_selector_js) +# See https://ddnexus.github.io/pagy/docs/api/javascript + +# With the asset pipeline +# Sprockets need to look into the pagy javascripts dir, so add it to the assets paths +# Rails.application.config.assets.paths << Pagy.root.join('javascripts') + +# I18n + +# Pagy internal I18n: ~18x faster using ~10x less memory than the i18n gem +# See https://ddnexus.github.io/pagy/docs/api/i18n +# Notice: No need to configure anything in this section if your app uses only "en" +# or if you use the i18n extra below +# +# Examples: +# load the "de" built-in locale: +# Pagy::I18n.load(locale: 'de') +# +# load the "de" locale defined in the custom file at :filepath: +# Pagy::I18n.load(locale: 'de', filepath: 'path/to/pagy-de.yml') +# +# load the "de", "en" and "es" built-in locales: +# (the first passed :locale will be used also as the default_locale) +# Pagy::I18n.load({ locale: 'de' }, +# { locale: 'en' }, +# { locale: 'es' }) +# +# load the "en" built-in locale, a custom "es" locale, +# and a totally custom locale complete with a custom :pluralize proc: +# (the first passed :locale will be used also as the default_locale) +# Pagy::I18n.load({ locale: 'en' }, +# { locale: 'es', filepath: 'path/to/pagy-es.yml' }, +# { locale: 'xyz', # not built-in +# filepath: 'path/to/pagy-xyz.yml', +# pluralize: lambda{ |count| ... } ) + +# I18n extra: uses the standard i18n gem which is ~18x slower using ~10x more memory +# than the default pagy internal i18n (see above) +# See https://ddnexus.github.io/pagy/docs/extras/i18n +# require 'pagy/extras/i18n' + +# When you are done setting your own default freeze it, so it will not get changed accidentally +Pagy::DEFAULT.freeze diff --git a/lib/elastic_searcher.rb b/lib/elastic_searcher.rb index 8436e740ccb..4aefd83ad1b 100644 --- a/lib/elastic_searcher.rb +++ b/lib/elastic_searcher.rb @@ -19,7 +19,7 @@ def search result = Rubygem.searchkick_search( body: search_definition.to_hash, page: @page, - per_page: Kaminari.config.default_per_page, + per_page: Pagy::DEFAULT[:limit], load: false ) result.response # ES query is triggered here to allow fallback. avoids lazy loading done in the view @@ -29,7 +29,7 @@ def search end def api_search - result = Rubygem.searchkick_search(body: search_definition(for_api: true).to_hash, page: @page, per_page: Kaminari.config.default_per_page, + result = Rubygem.searchkick_search(body: search_definition(for_api: true).to_hash, page: @page, per_page: Pagy::DEFAULT[:limit], load: false) result.response["hits"]["hits"].pluck("_source") rescue Searchkick::InvalidQueryError => e @@ -39,7 +39,7 @@ def api_search end def suggestions - result = Rubygem.searchkick_search(body: suggestions_definition.to_hash, page: @page, per_page: Kaminari.config.default_per_page, load: false) + result = Rubygem.searchkick_search(body: suggestions_definition.to_hash, page: @page, per_page: Pagy::DEFAULT[:limit], load: false) result = result.response["suggest"]["completion_suggestion"][0]["options"] result.map { |gem| gem["_source"]["name"] } rescue *CONNECTION_ERRORS => e diff --git a/test/functional/rubygems_controller_test.rb b/test/functional/rubygems_controller_test.rb index 0bf2ff07e61..f501f045770 100644 --- a/test/functional/rubygems_controller_test.rb +++ b/test/functional/rubygems_controller_test.rb @@ -107,7 +107,7 @@ class RubygemsControllerTest < ActionController::TestCase should respond_with :success should "render links" do @gems.each do |g| - assert page.has_content?(g.name) + page.assert_content(g.name) page.assert_selector("a[href='#{rubygem_path(g.slug)}']") end end @@ -173,7 +173,7 @@ class RubygemsControllerTest < ActionController::TestCase should respond_with :success should "render links" do @gems.each do |g| - assert page.has_content?(g.name) + page.assert_content(g.name) assert page.has_selector?("a[href='#{rubygem_path(g.slug)}']") end end diff --git a/test/views/events/table_component_test.rb b/test/views/events/table_component_test.rb index 911677c2243..48e21313dfe 100644 --- a/test/views/events/table_component_test.rb +++ b/test/views/events/table_component_test.rb @@ -41,7 +41,7 @@ def render(...) end def page(array, page: 0, per: 10) - Kaminari.paginate_array(array).page(page).per(per) + Pagy::Backend.pagy(array, page: page, limit: per) end test "renders an empty view" do