Skip to content

Commit 6ec0615

Browse files
committed
Finish user edit, update, index, and destroy actions
1 parent 163163b commit 6ec0615

File tree

18 files changed

+321
-24
lines changed

18 files changed

+321
-24
lines changed

Gemfile

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,24 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
33

44
ruby "3.1.1"
55

6-
gem "rails", "7.0.2.3"
7-
gem "bcrypt", "3.1.16"
8-
gem "bootstrap-sass", "3.4.1"
9-
gem "sassc-rails", "2.1.2"
10-
gem "sprockets-rails", "3.4.2"
11-
gem "importmap-rails", "1.0.3"
12-
gem "turbo-rails", "1.0.1"
13-
gem "stimulus-rails", "1.0.4"
14-
gem "jbuilder", "2.11.5"
15-
gem "puma", "5.6.4"
16-
gem "bootsnap", "1.11.1", require: false
6+
gem "rails", "7.0.2.3"
7+
gem "bcrypt", "3.1.16"
8+
gem "faker", "2.20.0"
9+
gem "will_paginate", "3.3.1"
10+
gem "bootstrap-will_paginate", "1.0.0"
11+
gem "bootstrap-sass", "3.4.1"
12+
gem "sassc-rails", "2.1.2"
13+
gem "sprockets-rails", "3.4.2"
14+
gem "importmap-rails", "1.0.3"
15+
gem "turbo-rails", "1.0.1"
16+
gem "stimulus-rails", "1.0.4"
17+
gem "jbuilder", "2.11.5"
18+
gem "puma", "5.6.4"
19+
gem "bootsnap", "1.11.1", require: false
1720

1821
group :development, :test do
19-
gem "sqlite3", "1.4.2"
2022
gem "debug", "1.4.0", platforms: %i[ mri mingw x64_mingw ]
23+
gem "sqlite3", "1.4.2"
2124
end
2225

2326
group :development do
@@ -39,7 +42,7 @@ group :production do
3942
gem "pg", "1.3.3"
4043
end
4144

42-
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
45+
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem.
4346
# Uncomment the following line if you're running Rails
4447
# on a native Windows system:
45-
# gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
48+
# gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]

Gemfile.lock

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ GEM
7878
bootstrap-sass (3.4.1)
7979
autoprefixer-rails (>= 5.2.1)
8080
sassc (>= 2.0.0)
81+
bootstrap-will_paginate (1.0.0)
82+
will_paginate
8183
builder (3.2.4)
8284
capybara (3.36.0)
8385
addressable
@@ -98,6 +100,8 @@ GEM
98100
digest (3.1.0)
99101
erubi (1.10.0)
100102
execjs (2.8.1)
103+
faker (2.20.0)
104+
i18n (>= 1.8.11, < 2)
101105
ffi (1.15.5)
102106
formatador (1.1.0)
103107
globalid (1.0.0)
@@ -262,6 +266,7 @@ GEM
262266
websocket-driver (0.7.5)
263267
websocket-extensions (>= 0.1.0)
264268
websocket-extensions (0.1.5)
269+
will_paginate (3.3.1)
265270
xpath (3.2.0)
266271
nokogiri (~> 1.8)
267272
zeitwerk (2.5.4)
@@ -273,8 +278,10 @@ DEPENDENCIES
273278
bcrypt (= 3.1.16)
274279
bootsnap (= 1.11.1)
275280
bootstrap-sass (= 3.4.1)
281+
bootstrap-will_paginate (= 1.0.0)
276282
capybara (= 3.36.0)
277283
debug (= 1.4.0)
284+
faker (= 2.20.0)
278285
guard (= 2.18.0)
279286
guard-minitest (= 2.4.6)
280287
importmap-rails (= 1.0.3)
@@ -293,6 +300,7 @@ DEPENDENCIES
293300
turbo-rails (= 1.0.1)
294301
web-console (= 4.2.0)
295302
webdrivers (= 5.0.0)
303+
will_paginate (= 3.3.1)
296304

297305
RUBY VERSION
298306
ruby 3.1.1p18

app/assets/stylesheets/custom.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,16 @@ input {
212212

213213
.dropdown-menu.active {
214214
display: block;
215+
}
216+
217+
/* Users index */
218+
219+
.users {
220+
list-style: none;
221+
margin: 0;
222+
li {
223+
overflow: auto;
224+
padding: 10px 0;
225+
border-bottom: 1px solid $gray-lighter;
226+
}
215227
}

app/controllers/sessions_controller.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ def new
66
def create
77
user = User.find_by(email: params[:session][:email].downcase)
88
if user && user.authenticate(params[:session][:password])
9+
forwarding_url = session[:forwarding_url]
910
reset_session
1011
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
1112
log_in user
12-
redirect_to user
13+
redirect_to forwarding_url || user
1314
else
1415
flash.now[:danger] = 'Invalid email/password combination'
1516
render 'new', status: :unprocessable_entity

app/controllers/users_controller.rb

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
class UsersController < ApplicationController
2+
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
3+
before_action :correct_user, only: [:edit, :update]
4+
before_action :admin_user, only: :destroy
5+
6+
def index
7+
@users = User.paginate(page: params[:page])
8+
end
29

310
def show
411
@user = User.find(params[:id])
@@ -20,10 +27,52 @@ def create
2027
end
2128
end
2229

30+
def edit
31+
@user = User.find(params[:id])
32+
end
33+
34+
def update
35+
@user = User.find(params[:id])
36+
if @user.update(user_params)
37+
flash[:success] = "Profile updated"
38+
redirect_to @user
39+
else
40+
render 'edit', status: :unprocessable_entity
41+
end
42+
end
43+
44+
def destroy
45+
User.find(params[:id]).destroy
46+
flash[:success] = "User deleted"
47+
redirect_to users_url, status: :see_other
48+
end
49+
2350
private
2451

2552
def user_params
2653
params.require(:user).permit(:name, :email, :password,
2754
:password_confirmation)
2855
end
29-
end
56+
57+
# Before filters
58+
59+
# Confirms a logged-in user.
60+
def logged_in_user
61+
unless logged_in?
62+
store_location
63+
flash[:danger] = "Please log in."
64+
redirect_to login_url
65+
end
66+
end
67+
68+
# Confirms the correct user.
69+
def correct_user
70+
@user = User.find(params[:id])
71+
redirect_to(root_url) unless @user == current_user
72+
end
73+
74+
# Confirms an admin user.
75+
def admin_user
76+
redirect_to(root_url) unless current_user.admin?
77+
end
78+
end

app/helpers/sessions_helper.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ module SessionsHelper
33
# Logs in the given user.
44
def log_in(user)
55
session[:user_id] = user.id
6+
# Guard against session replay attacks.
7+
# See https://bit.ly/33UvK0w for more.
8+
session[:session_token] = user.session_token
69
end
710

811
# Remembers a user in a persistent session.
@@ -15,7 +18,10 @@ def remember(user)
1518
# Returns the user corresponding to the remember token cookie.
1619
def current_user
1720
if (user_id = session[:user_id])
18-
@current_user ||= User.find_by(id: user_id)
21+
user = User.find_by(id: user_id)
22+
if user && session[:session_token] == user.session_token
23+
@current_user = user
24+
end
1925
elsif (user_id = cookies.encrypted[:user_id])
2026
user = User.find_by(id: user_id)
2127
if user && user.authenticated?(cookies[:remember_token])
@@ -25,6 +31,11 @@ def current_user
2531
end
2632
end
2733

34+
# Returns true if the given user is the current user.
35+
def current_user?(user)
36+
user && user == current_user
37+
end
38+
2839
# Returns true if the user is logged in, false otherwise.
2940
def logged_in?
3041
!current_user.nil?
@@ -43,4 +54,9 @@ def log_out
4354
reset_session
4455
@current_user = nil
4556
end
57+
58+
# Stores the URL trying to be accessed.
59+
def store_location
60+
session[:forwarding_url] = request.original_url if request.get?
61+
end
4662
end

app/helpers/users_helper.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
module UsersHelper
22

33
# Returns the Gravatar for the given user.
4-
def gravatar_for(user)
4+
def gravatar_for(user, options = { size: 80 })
5+
size = options[:size]
56
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
6-
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
7+
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
78
image_tag(gravatar_url, alt: user.name, class: "gravatar")
89
end
910
end

app/models/user.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class User < ApplicationRecord
77
format: { with: VALID_EMAIL_REGEX },
88
uniqueness: true
99
has_secure_password
10-
validates :password, presence: true, length: { minimum: 6 }
10+
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
1111

1212
# Returns the hash digest of the given string.
1313
def User.digest(string)
@@ -25,6 +25,13 @@ def User.new_token
2525
def remember
2626
self.remember_token = User.new_token
2727
update_attribute(:remember_digest, User.digest(remember_token))
28+
remember_digest
29+
end
30+
31+
# Returns a session token to prevent session hijacking.
32+
# We reuse the remember digest for convenience.
33+
def session_token
34+
remember_digest || remember
2835
end
2936

3037
# Returns true if the given token matches the digest.

app/views/layouts/_header.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
<li><%= link_to "Home", root_path %></li>
1616
<li><%= link_to "Help", help_path %></li>
1717
<% if logged_in? %>
18-
<li><%= link_to "Users", '#' %></li>
18+
<li><%= link_to "Users", users_path %></li>
1919
<li class="dropdown">
2020
<a href="#" id="account" class="dropdown-toggle">
2121
Account <b class="caret"></b>
2222
</a>
2323
<ul id="dropdown-menu" class="dropdown-menu">
2424
<li><%= link_to "Profile", current_user %></li>
25-
<li><%= link_to "Settings", '#' %></li>
25+
<li><%= link_to "Settings", edit_user_path(current_user) %></li>
2626
<li class="divider"></li>
2727
<li>
2828
<%= link_to "Log out", logout_path,

app/views/users/_user.html.erb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<li>
2+
<%= gravatar_for user, size: 50 %>
3+
<%= link_to user.name, user %>
4+
<% if current_user.admin? && !current_user?(user) %>
5+
| <%= link_to "delete", user, data: { "turbo-method": :delete,
6+
turbo_confirm: "You sure?" } %>
7+
<% end %>
8+
</li>

0 commit comments

Comments
 (0)