Commit f98aa10a by phuctmZigexn

Finish user edit, update, index, and destroy actions

parent 483bb03f
...@@ -67,3 +67,6 @@ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] ...@@ -67,3 +67,6 @@ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem 'bootstrap-sass', '~> 3.3', '>= 3.3.7' gem 'bootstrap-sass', '~> 3.3', '>= 3.3.7'
gem 'bcrypt', '~> 3.1', '>= 3.1.12' gem 'bcrypt', '~> 3.1', '>= 3.1.12'
gem 'jquery-rails', '~> 4.3', '>= 4.3.3' gem 'jquery-rails', '~> 4.3', '>= 4.3.3'
gem 'faker', '~> 1.9', '>= 1.9.1'
gem 'will_paginate', '~> 3.1', '>= 3.1.6'
gem 'bootstrap-will_paginate', '~> 1.0'
\ No newline at end of file
...@@ -56,6 +56,8 @@ GEM ...@@ -56,6 +56,8 @@ GEM
bootstrap-sass (3.3.7) bootstrap-sass (3.3.7)
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4) sass (>= 3.3.4)
bootstrap-will_paginate (1.0.0)
will_paginate
builder (3.2.3) builder (3.2.3)
byebug (10.0.2) byebug (10.0.2)
capybara (3.4.2) capybara (3.4.2)
...@@ -81,6 +83,8 @@ GEM ...@@ -81,6 +83,8 @@ GEM
crass (1.0.4) crass (1.0.4)
erubi (1.7.1) erubi (1.7.1)
execjs (2.7.0) execjs (2.7.0)
faker (1.9.1)
i18n (>= 0.7)
ffi (1.9.25) ffi (1.9.25)
globalid (0.4.1) globalid (0.4.1)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
...@@ -200,6 +204,7 @@ GEM ...@@ -200,6 +204,7 @@ GEM
websocket-driver (0.7.0) websocket-driver (0.7.0)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3) websocket-extensions (0.1.3)
will_paginate (3.1.6)
xpath (3.1.0) xpath (3.1.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
...@@ -210,10 +215,12 @@ DEPENDENCIES ...@@ -210,10 +215,12 @@ DEPENDENCIES
bcrypt (~> 3.1, >= 3.1.12) bcrypt (~> 3.1, >= 3.1.12)
bootsnap (>= 1.1.0) bootsnap (>= 1.1.0)
bootstrap-sass (~> 3.3, >= 3.3.7) bootstrap-sass (~> 3.3, >= 3.3.7)
bootstrap-will_paginate (~> 1.0)
byebug byebug
capybara (>= 2.15, < 4.0) capybara (>= 2.15, < 4.0)
chromedriver-helper chromedriver-helper
coffee-rails (~> 4.2) coffee-rails (~> 4.2)
faker (~> 1.9, >= 1.9.1)
jbuilder (~> 2.5) jbuilder (~> 2.5)
jquery-rails (~> 4.3, >= 4.3.3) jquery-rails (~> 4.3, >= 4.3.3)
listen (>= 3.0.5, < 3.2) listen (>= 3.0.5, < 3.2)
...@@ -230,6 +237,7 @@ DEPENDENCIES ...@@ -230,6 +237,7 @@ DEPENDENCIES
tzinfo-data tzinfo-data
uglifier (>= 1.3.0) uglifier (>= 1.3.0)
web-console (>= 3.3.0) web-console (>= 3.3.0)
will_paginate (~> 3.1, >= 3.1.6)
RUBY VERSION RUBY VERSION
ruby 2.5.1p57 ruby 2.5.1p57
......
...@@ -185,3 +185,14 @@ input { ...@@ -185,3 +185,14 @@ input {
width: auto; width: auto;
margin-left: 0; margin-left: 0;
} }
/* User index */
.users {
list-style: none;
margin: 0;
li {
overflow: auto;
padding: 10px 0;
border-bottom: 1px solid $gray-lighter;
}
}
\ No newline at end of file
...@@ -7,7 +7,7 @@ class SessionsController < ApplicationController ...@@ -7,7 +7,7 @@ class SessionsController < ApplicationController
if user && user.authenticate(params[:session][:password]) if user && user.authenticate(params[:session][:password])
log_in user log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user) params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_to user redirect_back_or user
else else
flash.now[:danger] = "Invalid email/password combination" flash.now[:danger] = "Invalid email/password combination"
render 'new' render 'new'
......
class UsersController < ApplicationController class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: [:destroy]
def index
@users = User.paginate(page: params[:page])
end
def show def show
@user = User.find(params[:id]) @user = User.find(params[:id])
end end
...@@ -22,8 +30,45 @@ class UsersController < ApplicationController ...@@ -22,8 +30,45 @@ class UsersController < ApplicationController
@user = User.find params[:id] @user = User.find params[:id]
end end
def update
@user = User.find params[:id]
if @user.update_attributes user_params
flash[:success] = "Profile updated"
redirect_to @user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private private
def user_params def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation) params.require(:user).permit(:name, :email, :password, :password_confirmation)
end end
#confirm a logged-in user
# if user don't login and they're in GET request, it store this request to session[:forwarding_url]
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in"
redirect_to login_url
end
end
# confirm the correct user
def correct_user
@user = User.find params[:id]
redirect_to root_url unless current_user? @user
end
# confirm an admin user
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end end
...@@ -10,6 +10,9 @@ module SessionsHelper ...@@ -10,6 +10,9 @@ module SessionsHelper
cookies.permanent[:remember_token] = user.remember_token cookies.permanent[:remember_token] = user.remember_token
end end
def current_user?(user)
user == current_user
end
# return the user corresponding to the remember token cookie # return the user corresponding to the remember token cookie
def current_user def current_user
return @current_user if @current_user return @current_user if @current_user
...@@ -42,4 +45,18 @@ module SessionsHelper ...@@ -42,4 +45,18 @@ module SessionsHelper
session.delete(:user_id) session.delete(:user_id)
@current_user = nil @current_user = nil
end end
# friendly forwarding - stored location of expected page and redirect to it after login
# evaluate to session[:forwarding_url] unless it's nil
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete :forwarding_url
end
# stores the URL trying to be accessed.
# only use for GET to prevent storing submiting a form when not log in
# original_url and get? is contained Request object of Rails
def store_location
session[:forwarding_url] = request.original_url if request.get?
end
end end
module UsersHelper module UsersHelper
def gravatar_for(user, size: 80) def gravatar_for(user, options = { size: 80 })
gravatar_id = Digest::MD5::hexdigest(user.email.downcase) gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
size = options[:size]
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
image_tag(gravatar_url, alt: user.name, class: "gravatar") image_tag(gravatar_url, alt: user.name, class: "gravatar")
end end
......
...@@ -10,7 +10,7 @@ class User < ApplicationRecord ...@@ -10,7 +10,7 @@ class User < ApplicationRecord
format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
has_secure_password has_secure_password
validates :password, presence: true, length: { minimum: 6 } validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
def User.digest(string) def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
......
...@@ -6,14 +6,14 @@ ...@@ -6,14 +6,14 @@
<li><%= link_to 'Home', root_path %></li> <li><%= link_to 'Home', root_path %></li>
<li><%= link_to 'Help', help_path %></li> <li><%= link_to 'Help', help_path %></li>
<% if logged_in? %> <% if logged_in? %>
<li><%= link_to "Users", '#' %></li> <li><%= link_to "Users", users_path %></li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">
Account <b class="caret"></b> Account <b class="caret"></b>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li> <li><%= link_to "Profile", current_user %></li>
<li><%= link_to "Settings", "#" %></li> <li><%= link_to "Settings", edit_user_path(current_user) %></li>
<li class="divider"></li> <li class="divider"></li>
<li> <li>
<%= link_to "Log out", logout_path, method: :delete %> <%= link_to "Log out", logout_path, method: :delete %>
......
<% if @user.errors.any? %> <% if user.errors.any? %>
<div id="error_explanation"> <div id="error_explanation">
<div class="alert alert-danger"> <div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>. The form contains <%= pluralize(user.errors.count, "error") %>.
</div> </div>
<ul> <ul>
<% @user.errors.full_messages.each do |msg| %> <% user.errors.full_messages.each do |msg| %>
<li><%= msg %></li> <li><%= msg %></li>
<% end %> <% end %>
</ul> </ul>
......
<%= form_for @user do |f| %>
<%= render 'shared/error_messages', user: @user %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>
\ No newline at end of file
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
<%= link_to 'delete', user, method: :delete, data: { confirm: "You sure?" } %>
<% end %>
</li>
\ No newline at end of file
<% provide(:title, "Edit user") %>
<% provide(:button_text, 'Save changes') %>
<h1>Update your profile</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= render 'form' %>
<div class="gravatar_edit">
<%= gravatar_for @user %>
<a href="http://gravatar.com/emails" target="_blank">change</a>
</div>
</div>
</div>
\ No newline at end of file
<% provide :title, 'All users' %>
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<%= render @users %>
</ul>
<%= will_paginate %>
\ No newline at end of file
<% provide(:title, 'Sign up') %> <% provide(:title, 'Sign up') %>
<% provide(:button_text, 'Create my account') %>
<h1>Sign up</h1> <h1>Sign up</h1>
<div class="row"> <div class="row">
<div class="col-md-6 col-md-offet-3"> <div class="col-md-6 col-md-offet-3">
<%= form_for @user, url: signup_path do |f| %> <%= render 'form' %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div> </div>
</div> </div>
\ No newline at end of file
...@@ -13,3 +13,5 @@ Rails.application.routes.draw do ...@@ -13,3 +13,5 @@ Rails.application.routes.draw do
resources :users resources :users
end end
class AddAdminToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :admin, :boolean, default: false
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2018_08_06_093756) do ActiveRecord::Schema.define(version: 2018_08_09_071603) do
create_table "users", force: :cascade do |t| create_table "users", force: :cascade do |t|
t.string "name" t.string "name"
...@@ -19,6 +19,7 @@ ActiveRecord::Schema.define(version: 2018_08_06_093756) do ...@@ -19,6 +19,7 @@ ActiveRecord::Schema.define(version: 2018_08_06_093756) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.string "password_digest" t.string "password_digest"
t.string "remember_digest" t.string "remember_digest"
t.boolean "admin", default: false
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
end end
......
# This file should contain all the record creation needed to seed the database with its default values. User.create! name: "Example User", email: "example@railstutorial.org",
# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). password: "foobar", password_confirmation: "foobar", admin: true
#
# Examples: 99.times do |n|
# name = Faker::Name.name
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) email = "example-#{n+1}@railstutorial.org"
# Character.create(name: 'Luke', movie: movies.first) password = 'password'
User.create! name: name, email: email,
password: password, password_confirmation: password
end
\ No newline at end of file
require 'test_helper' require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest class UsersControllerTest < ActionDispatch::IntegrationTest
test "should get new" do def setup
get signup_path @user = users :michael
assert_response :success @other_user = users :archer
end end
test "should redirect edit when logged in as wrong user" do
log_in_as @other_user
get edit_user_path @user
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect update when logged in as wrong user" do
log_in_as @other_user
patch user_path @user, params: { user: { name: @user.name,
email: @user.email } }
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect index when not logged in" do
get users_path
assert_redirected_to login_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'User.count' do
delete user_path(@user)
end
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 end
...@@ -2,3 +2,27 @@ michael: ...@@ -2,3 +2,27 @@ michael:
name: Michael Example name: Michael Example
email: michael@example.com email: michael@example.com
password_digest: <%= User.digest('password') %> password_digest: <%= User.digest('password') %>
admin: true
archer:
name: Sterling Archer
email: duchess@example.gov
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 %>
\ No newline at end of file
require 'test_helper'
class UsersEditTest < ActionDispatch::IntegrationTest
def setup
@user = users :michael
end
test "unsuccessful edit" do
log_in_as @user
get edit_user_path @user
assert_template 'users/edit'
patch user_path(@user), params: { user: { name: "",
email: "foo@invalid",
password: "foo",
password_confirmation: "bar" } }
assert_template 'users/edit'
end
test "successful edit" do
log_in_as @user
get edit_user_path @user
assert_template 'users/edit'
name = "Foo Bar"
email = "foo@bar.com"
patch user_path(@user), params: { user: { name: name,
email: email,
password: "",
password_confirmation: "" } }
assert_not flash.empty?
assert_redirected_to @user
@user.reload
assert_equal name, @user.name
assert_equal email, @user.email
end
# redirecting to edit page when non-logged-in user visit edit page,
# have already logged in instead of show page
test 'successful edit with friendly forwarding' do
get edit_user_path @user
log_in_as @user
assert_redirected_to edit_user_url @user
name = "Foo Bar"
email = "foo@bar.com"
patch user_path(@user), params: { user: { name: name,
email: email,
password: "",
password_confirmation: "" } }
assert_not flash.empty?
assert_redirected_to @user
@user.reload
assert_equal name, @user.name
assert_equal email, @user.email
end
end
require 'test_helper'
class UsersIndexTest < ActionDispatch::IntegrationTest
def setup
@admin = users :michael
@non_admin = users :archer
end
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'
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_select 'a', text: 'delete', count: 0
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment