From 022eae30292e9c2f20bbb09542924088858bcfdd Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sun, 5 Jan 2025 17:50:33 +0800 Subject: [PATCH] feat: add pagination to user index view - Integrate Kaminari for pagination of users - Update users_controller to paginate users - Add pagination views for first, last, next, and previous pages - Seed database with example users for testing This commit introduces pagination to the user index view, allowing for better navigation through large sets of users. The Kaminari gem is utilized to handle pagination, improving the user experience by reducing load times and enhancing usability. Additionally, the seeding script has been updated to create multiple users for better testing of the pagination feature. --- Gemfile | 3 +++ Gemfile.lock | 16 ++++++++++++++++ app/controllers/users_controller.rb | 4 +++- app/views/kaminari/_first_page.html.erb | 3 +++ app/views/kaminari/_gap.html.erb | 3 +++ app/views/kaminari/_last_page.html.erb | 3 +++ app/views/kaminari/_next_page.html.erb | 3 +++ app/views/kaminari/_page.html.erb | 9 +++++++++ app/views/kaminari/_paginator.html.erb | 15 +++++++++++++++ app/views/kaminari/_prev_page.html.erb | 3 +++ app/views/users/_user.html.erb | 4 ++++ app/views/users/index.html.erb | 19 ++++++++++++------- config/initializers/kaminari_config.rb | 15 +++++++++++++++ config/locales/en.yml | 2 +- db/seeds.rb | 15 +++++++++++++++ test/fixtures/users.yml | 9 ++++++++- test/integration/users_index_test.rb | 17 +++++++++++++++++ test/integration/users_login_test.rb | 1 + 18 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 app/views/kaminari/_first_page.html.erb create mode 100644 app/views/kaminari/_gap.html.erb create mode 100644 app/views/kaminari/_last_page.html.erb create mode 100644 app/views/kaminari/_next_page.html.erb create mode 100644 app/views/kaminari/_page.html.erb create mode 100644 app/views/kaminari/_paginator.html.erb create mode 100644 app/views/kaminari/_prev_page.html.erb create mode 100644 app/views/users/_user.html.erb create mode 100644 config/initializers/kaminari_config.rb create mode 100644 test/integration/users_index_test.rb diff --git a/Gemfile b/Gemfile index b0a18fc..18d2a8a 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,9 @@ gem "jbuilder" gem "bcrypt", "~> 3.1" +gem "faker", "~> 3.5" +gem "kaminari", "~> 1.2" + # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] # gem "bcrypt", "~> 3.1.7" diff --git a/Gemfile.lock b/Gemfile.lock index 5f8ef2d..0479d72 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -114,6 +114,8 @@ GEM erubi (1.13.1) et-orbi (1.2.11) tzinfo + faker (3.5.1) + i18n (>= 1.8.11, < 2) ffi (1.17.1-aarch64-linux-gnu) ffi (1.17.1-aarch64-linux-musl) ffi (1.17.1-arm-linux-gnu) @@ -166,6 +168,18 @@ GEM sshkit (>= 1.23.0, < 2.0) thor (~> 1.3) zeitwerk (>= 2.6.18, < 3.0) + 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) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) @@ -420,11 +434,13 @@ DEPENDENCIES capybara cssbundling-rails (~> 1.4) debug + faker (~> 3.5) guard (~> 2.19) importmap-rails jbuilder jsbundling-rails (~> 1.3) kamal + kaminari (~> 1.2) minitest-reporters propshaft puma (>= 5.0) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 283124a..0b54b14 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -4,7 +4,9 @@ class UsersController < ApplicationController before_action :correct_user, only: [ :edit, :update ] def index - @users = User.all + # @users = User.all + # @users = User.order(:name).page(params[:page]) + @users = User.page(params[:page]) end def show diff --git a/app/views/kaminari/_first_page.html.erb b/app/views/kaminari/_first_page.html.erb new file mode 100644 index 0000000..2c5985b --- /dev/null +++ b/app/views/kaminari/_first_page.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote %> +
  • diff --git a/app/views/kaminari/_gap.html.erb b/app/views/kaminari/_gap.html.erb new file mode 100644 index 0000000..6d3a149 --- /dev/null +++ b/app/views/kaminari/_gap.html.erb @@ -0,0 +1,3 @@ +
  • + <%= content_tag :a, raw(t 'views.pagination.truncate') %> +
  • diff --git a/app/views/kaminari/_last_page.html.erb b/app/views/kaminari/_last_page.html.erb new file mode 100644 index 0000000..aee2524 --- /dev/null +++ b/app/views/kaminari/_last_page.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, { remote: remote } %> +
  • diff --git a/app/views/kaminari/_next_page.html.erb b/app/views/kaminari/_next_page.html.erb new file mode 100644 index 0000000..8e9a10e --- /dev/null +++ b/app/views/kaminari/_next_page.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, rel: 'next', remote: remote %> +
  • diff --git a/app/views/kaminari/_page.html.erb b/app/views/kaminari/_page.html.erb new file mode 100644 index 0000000..47ea75d --- /dev/null +++ b/app/views/kaminari/_page.html.erb @@ -0,0 +1,9 @@ +<% if page.current? %> +
  • + <%= content_tag :a, page, data: { remote: remote }, rel: page.rel %> +
  • +<% else %> +
  • + <%= link_to page, url, remote: remote, rel: page.rel %> +
  • +<% end %> diff --git a/app/views/kaminari/_paginator.html.erb b/app/views/kaminari/_paginator.html.erb new file mode 100644 index 0000000..2c8757b --- /dev/null +++ b/app/views/kaminari/_paginator.html.erb @@ -0,0 +1,15 @@ +<%= paginator.render do -%> + +<% end -%> diff --git a/app/views/kaminari/_prev_page.html.erb b/app/views/kaminari/_prev_page.html.erb new file mode 100644 index 0000000..f16f6b4 --- /dev/null +++ b/app/views/kaminari/_prev_page.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote %> +
  • diff --git a/app/views/users/_user.html.erb b/app/views/users/_user.html.erb new file mode 100644 index 0000000..d54c0e3 --- /dev/null +++ b/app/views/users/_user.html.erb @@ -0,0 +1,4 @@ +
  • + <%= gravatar_for user, size: 50 %> + <%= link_to user.name, user %> +
  • \ No newline at end of file diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 26ad418..d0ba40a 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -2,11 +2,16 @@

    All users

    +<%= paginate @users %> + + \ No newline at end of file + <%= render @users %> + <%# auto loop by rails, like after %> + <%# @users.each do |user| %> + + <%#= gravatar_for user, size: 50 %> + <%#= link_to user.name, user %> + + <%# end %> + diff --git a/config/initializers/kaminari_config.rb b/config/initializers/kaminari_config.rb new file mode 100644 index 0000000..f476199 --- /dev/null +++ b/config/initializers/kaminari_config.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +Kaminari.configure do |config| + config.default_per_page = 5 + # config.default_per_page = 25 + # 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.max_pages = nil + # config.params_on_first_page = false +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 6c349ae..4e65d5c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -28,4 +28,4 @@ # enabled: "ON" en: - hello: "Hello world" + hello: "Hello world" \ No newline at end of file diff --git a/db/seeds.rb b/db/seeds.rb index 4fbd6ed..155583d 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -7,3 +7,18 @@ # ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| # MovieGenre.find_or_create_by!(name: genre_name) # end + +User.create!(name: "Example User", + email: "example@example.com", + password: "foobar", + password_confirmation: "foobar") + +99.times do |n| + name = Faker::Name.name + email = "example-#{n+1}@railstutorial.org" + password = "password" + User.create!(name: name, + email: email, + password: password, + password_confirmation: password) +end diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index bfcca93..2f60abe 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -12,4 +12,11 @@ michael: archer: name: Sterling Archer email: suchess@example.gov - password_digest: <%= User.digest('password') %> \ No newline at end of file + password_digest: <%= User.digest('password') %> + +<% 30.times do |n| %> +user_<%= n %>: + name: <%= "user #{n}" %> + email: <%= "user-#{n}@example.com" %> + password_digest: <%= User.digest('password') %> +<% end %> \ No newline at end of file diff --git a/test/integration/users_index_test.rb b/test/integration/users_index_test.rb new file mode 100644 index 0000000..a2064d1 --- /dev/null +++ b/test/integration/users_index_test.rb @@ -0,0 +1,17 @@ +require "test_helper" + +class UsersIndexTest < ActionDispatch::IntegrationTest + def setup + @user = users(:michael) + end + + test "index including pagination" do + log_in_as(@user) + get users_path + assert_template "users/index" + assert_select "ul.pagination" + User.page(1).each do |user| + assert_select "a[href=?]", user_path(user), text: user.name + end + end +end diff --git a/test/integration/users_login_test.rb b/test/integration/users_login_test.rb index fab2475..bddac37 100644 --- a/test/integration/users_login_test.rb +++ b/test/integration/users_login_test.rb @@ -39,6 +39,7 @@ class UsersLoginTest < ActionDispatch::IntegrationTest assert_template "users/show" assert_select "a[href=?]", login_path, count: 0 assert_select "a[href=?]", logout_path + assert_select "a[href=?]", users_path assert_select "a[href=?]", user_path(@user) delete logout_path assert_not is_logged_in?