diff --git a/.idea/simple_app.iml b/.idea/simple_app.iml
index aa7dd65..c5b220e 100644
--- a/.idea/simple_app.iml
+++ b/.idea/simple_app.iml
@@ -49,6 +49,7 @@
+
@@ -62,13 +63,14 @@
+
-
+
@@ -124,7 +126,7 @@
-
+
@@ -152,6 +154,7 @@
+
diff --git a/Gemfile b/Gemfile
index 8811d77..a4a99c0 100644
--- a/Gemfile
+++ b/Gemfile
@@ -16,10 +16,12 @@ gem "turbo-rails"
gem "stimulus-rails"
# Build JSON APIs with ease [https://github.com/rails/jbuilder]
gem "jbuilder"
-gem 'ostruct'
-gem 'bcrypt', '~> 3.1.7'
-gem 'pg'
-
+gem "ostruct"
+gem "bcrypt", "~> 3.1.7"
+gem "pg"
+gem "faker"
+gem "will_paginate", "3.3.1"
+gem "bootstrap-will_paginate", "1.0.0"
gem "sassc-rails"
gem "rails-controller-testing"
gem "bootstrap-sass", "~> 3.4.1"
@@ -63,7 +65,6 @@ group :test do
gem "guard-minitest"
end
-
group :development do
# Use console on exceptions pages [https://github.com/rails/web-console]
gem "web-console"
diff --git a/Gemfile.lock b/Gemfile.lock
index a83b8ef..3ee2fdb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -86,6 +86,8 @@ GEM
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1)
sassc (>= 2.0.0)
+ bootstrap-will_paginate (1.0.0)
+ will_paginate
brakeman (6.2.1)
racc
builder (3.3.0)
@@ -109,6 +111,8 @@ GEM
drb (2.2.1)
erubi (1.13.0)
execjs (2.9.1)
+ faker (3.4.2)
+ i18n (>= 1.8.11, < 2)
ffi (1.17.0-aarch64-linux-gnu)
ffi (1.17.0-aarch64-linux-musl)
ffi (1.17.0-arm-linux-gnu)
@@ -135,7 +139,7 @@ GEM
guard-minitest (2.4.6)
guard-compat (~> 1.2)
minitest (>= 3.0)
- i18n (1.14.5)
+ i18n (1.14.6)
concurrent-ruby (~> 1.0)
importmap-rails (2.0.1)
actionpack (>= 6.0.0)
@@ -282,7 +286,7 @@ GEM
rubocop-minitest (0.36.0)
rubocop (>= 1.61, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
- rubocop-performance (1.21.1)
+ rubocop-performance (1.22.1)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rails (2.26.1)
@@ -356,6 +360,7 @@ GEM
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
+ will_paginate (3.3.1)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.18)
@@ -379,9 +384,11 @@ DEPENDENCIES
bcrypt (~> 3.1.7)
bootsnap
bootstrap-sass (~> 3.4.1)
+ bootstrap-will_paginate (= 1.0.0)
brakeman
capybara
debug
+ faker
guard
guard-minitest
importmap-rails
@@ -403,6 +410,7 @@ DEPENDENCIES
tzinfo-data
web-console
webdrivers
+ will_paginate (= 3.3.1)
BUNDLED WITH
2.5.18
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 288b9ab..42af950 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -1,3 +1,4 @@
+@import "bootstrap";
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index f9a80c4..9dc06d4 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,7 +1,7 @@
class UsersController < ApplicationController
- before_action :logged_in_user, only: [:index, :edit, :update]
- before_action :correct_user, only: [:edit, :update]
-
+ before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
+ before_action :correct_user, only: [:edit, :update]
+ before_action :admin_user, only: :destroy
def show
@user = User.find(params[:id])
@@ -23,11 +23,21 @@ def create
end
end
+ def destroy
+ user = User.find(params[:id])
+ if user.destroy
+ flash[:success] = "User deleted"
+ else
+ flash[:danger] = "User could not be deleted"
+ end
+ redirect_to users_url, status: :see_other
+ end
+
def edit
end
def index
- @users = User.all
+ @users = User.paginate(page: params[:page], per_page: 10)
end
def update
@@ -62,4 +72,10 @@ def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
+ def admin_user
+ unless current_user.admin?
+ flash[:danger] = "Access Denied."
+ redirect_to root_url
+ end
+ end
end
diff --git a/app/views/users/_user.html.erb b/app/views/users/_user.html.erb
new file mode 100644
index 0000000..2d7fa3d
--- /dev/null
+++ b/app/views/users/_user.html.erb
@@ -0,0 +1,12 @@
+
+ <% @users.each do |user| %>
+ -
+ <%= link_to gravatar_for( user, size: 50), user %>
+ <%= link_to user.name, user %>
+ <% if current_user.admin? && !current_user?(user) %>
+ | <%= link_to "delete", user, data: { "turbo-method": :delete,
+ turbo_confirm: "You sure?" } %>
+ <% end %>
+
+ <% end %>
+
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
index c5b5679..e21e9ef 100644
--- a/app/views/users/index.html.erb
+++ b/app/views/users/index.html.erb
@@ -1,11 +1,7 @@
<% provide(:title, 'All users') %>
All users
+<%= will_paginate %>
-
- <% @users.each do |user| %>
- -
- <%= gravatar_for user, size: 50 %>
- <%= link_to user.name, user %>
-
- <% end %>
-
+<%= render "users/user" %>
+
+<%= will_paginate %>
diff --git a/db/migrate/20240918050726_add_admin_to_users.rb b/db/migrate/20240918050726_add_admin_to_users.rb
new file mode 100644
index 0000000..60fee8f
--- /dev/null
+++ b/db/migrate/20240918050726_add_admin_to_users.rb
@@ -0,0 +1,5 @@
+class AddAdminToUsers < ActiveRecord::Migration[7.2]
+ def change
+ add_column :users, :admin, :boolean, default: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a8c9b02..6799a62 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.2].define(version: 2024_09_15_202915) do
+ActiveRecord::Schema[7.2].define(version: 2024_09_18_050726) do
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
@@ -18,5 +18,6 @@
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
+ t.boolean "admin", default: false
end
end
diff --git a/db/seeds.rb b/db/seeds.rb
index 4fbd6ed..1f6bf80 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -1,9 +1,10 @@
-# This file should ensure the existence of records required to run the application in every environment (production,
-# development, test). The code here should be idempotent so that it can be executed at any point in every environment.
-# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
-#
-# Example:
-#
-# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
-# MovieGenre.find_or_create_by!(name: genre_name)
-# end
+99.times do |n|
+ name = Faker::Name.name
+ email = "example-#{n + 1}@railstutorial.org"
+
+ User.create(name: name,
+ email: email,
+ password: "123456",
+ password_confirmation: "123456",
+ admin: true)
+end
diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb
index e842123..0c67faa 100644
--- a/test/controllers/users_controller_test.rb
+++ b/test/controllers/users_controller_test.rb
@@ -42,4 +42,30 @@ def setup
get users_path
assert_redirected_to login_url
end
+
+ test "should not allow the admin attribute to be edited via the web" do
+ log_in_as(@other_user)
+ assert_not @other_user.admin?
+ patch user_path(@other_user), params: {
+ user: { password: "password",
+ password_confirmation: "password",
+ admin: true } }
+ assert_not @other_user.admin?
+ end
+
+ test "should redirect destroy when not logged in" do
+ assert_no_difference "User.count" do
+ delete user_path(@user)
+ end
+ assert_response :see_other
+ assert_redirected_to login_url
+ end
+ test "should redirect destroy when logged in as a non-admin" do
+ log_in_as(@other_user)
+ assert_no_difference "User.count" do
+ delete user_path(@user)
+ end
+ assert_redirected_to root_url
+ end
+
end
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index 4fa41a6..bffbf3b 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -3,9 +3,27 @@
amr:
name: Amr Hossam
email: amr@gmail.com
+ admin: true
password_digest: <%= User.digest("password") %>
amr2:
name: Amr2
email: foo@gmail.com
password_digest: <%= User.digest("password") %>
+
+lana:
+ name: Lana Kane
+ email: hands@example.gov
+ password_digest: <%= User.digest('password') %>
+
+malory:
+ name: Malory Archer
+ email: boss@example.gov
+ 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 %>
diff --git a/test/integration/users_index_test.rb b/test/integration/users_index_test.rb
new file mode 100644
index 0000000..9977ced
--- /dev/null
+++ b/test/integration/users_index_test.rb
@@ -0,0 +1,36 @@
+require "test_helper"
+
+class UsersIndexTest < ActionDispatch::IntegrationTest
+
+ def setup
+ @admin = users(:amr)
+ @non_admin = users(:amr2)
+ end
+
+ # TODO check that
+
+ # test "index as admin including pagination and delete links" do
+ # log_in_as(@admin)
+ # get users_path
+ # assert_template "users/index"
+ # assert_select "div.pagination"
+ # first_page_of_users = User.paginate(page: 1)
+ # first_page_of_users.each do |user|
+ # assert_select "a[href=?]", user_path(user), text: user.name
+ # unless user == @admin
+ # assert_select "a[href=?]", user_path(user), text: "delete", method: :delete
+ # end
+ # end
+ # assert_difference "User.count", -1 do
+ # delete user_path(@non_admin)
+ # end
+ # end
+
+ test "index as non-admin" do
+ log_in_as(@non_admin)
+ get users_path
+ assert_template "users/index"
+ assert_select "a", text: "delete", count: 0
+
+ end
+end