Commit 48d60581 by Quang Vinh Nguyen

Finish user edit, update, index, and destroy actions

parent 7844c61c
...@@ -58,6 +58,9 @@ source 'https://rubygems.org' ...@@ -58,6 +58,9 @@ source 'https://rubygems.org'
gem 'rails', '5.1.4' gem 'rails', '5.1.4'
gem 'bcrypt', '3.1.12' gem 'bcrypt', '3.1.12'
gem 'faker', '1.7.3'
gem 'will_paginate', '3.1.6'
gem 'bootstrap-will_paginate', '1.0.0'
gem 'bootstrap-sass', '3.3.7' gem 'bootstrap-sass', '3.3.7'
gem 'puma', '3.9.1' gem 'puma', '3.9.1'
gem 'sass-rails', '5.0.6' gem 'sass-rails', '5.0.6'
......
...@@ -47,6 +47,8 @@ GEM ...@@ -47,6 +47,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 (9.0.6) byebug (9.0.6)
coderay (1.1.2) coderay (1.1.2)
...@@ -61,6 +63,8 @@ GEM ...@@ -61,6 +63,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.7.3)
i18n (~> 0.5)
ffi (1.9.23) ffi (1.9.23)
formatador (0.2.5) formatador (0.2.5)
globalid (0.4.1) globalid (0.4.1)
...@@ -198,6 +202,7 @@ GEM ...@@ -198,6 +202,7 @@ GEM
websocket-driver (0.6.5) websocket-driver (0.6.5)
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)
PLATFORMS PLATFORMS
ruby ruby
...@@ -205,8 +210,10 @@ PLATFORMS ...@@ -205,8 +210,10 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
bcrypt (= 3.1.12) bcrypt (= 3.1.12)
bootstrap-sass (= 3.3.7) bootstrap-sass (= 3.3.7)
bootstrap-will_paginate (= 1.0.0)
byebug (= 9.0.6) byebug (= 9.0.6)
coffee-rails (= 4.2.2) coffee-rails (= 4.2.2)
faker (= 1.7.3)
guard (= 2.13.0) guard (= 2.13.0)
guard-minitest (= 2.4.4) guard-minitest (= 2.4.4)
jbuilder (= 2.7.0) jbuilder (= 2.7.0)
...@@ -226,6 +233,7 @@ DEPENDENCIES ...@@ -226,6 +233,7 @@ DEPENDENCIES
tzinfo-data tzinfo-data
uglifier (= 3.2.0) uglifier (= 3.2.0)
web-console (= 3.5.1) web-console (= 3.5.1)
will_paginate (= 3.1.6)
BUNDLED WITH BUNDLED WITH
1.16.2 1.16.2
...@@ -204,3 +204,15 @@ input { ...@@ -204,3 +204,15 @@ input {
width: auto; width: auto;
margin-left: 0; margin-left: 0;
} }
/* Users 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
# comment
class SessionsController < ApplicationController class SessionsController < ApplicationController
def new def new
# debugger
end end
# def create # def create
...@@ -16,7 +18,6 @@ class SessionsController < ApplicationController ...@@ -16,7 +18,6 @@ class SessionsController < ApplicationController
# render 'new' # render 'new'
# end # end
# end # end
def create def create
@user = User.find_by(email: params[:session][:email].downcase) @user = User.find_by(email: params[:session][:email].downcase)
if @user && @user.authenticate(params[:session][:password]) if @user && @user.authenticate(params[:session][:password])
...@@ -24,7 +25,8 @@ class SessionsController < ApplicationController ...@@ -24,7 +25,8 @@ class SessionsController < ApplicationController
log_in @user log_in @user
# remember user # update: remember_digest attribute # remember user # update: remember_digest attribute
params[:session][:remember_me] == '1' ? remember(@user) : forget(@user) params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
redirect_to @user # redirect_to @user
redirect_back_or @user
else else
# Create an error message. # Create an error message.
flash.now[:danger] = 'Invalid email/password combination' flash.now[:danger] = 'Invalid email/password combination'
......
# This is user controller # This is user controller
class UsersController < ApplicationController class UsersController < ApplicationController
before_action :logged_in_user, only: %i[edit update index destroy]
before_action :correct_user, only: %i[edit update]
before_action :admin_user, only: :destroy
def show def show
@user = User.find(params[:id]) @user = User.find(params[:id])
end end
...@@ -13,16 +16,63 @@ class UsersController < ApplicationController ...@@ -13,16 +16,63 @@ class UsersController < ApplicationController
if @user.save if @user.save
log_in @user log_in @user
flash[:success] = 'Welcome to the Sample App!' flash[:success] = 'Welcome to the Sample App!'
redirect_to @user redirect_to @user # redirect_to user_url(@user)
# redirect_to user_url(@user)
else else
render 'new' render 'new'
end end
end end
end
def edit
@user = User.find(params[:id])
end
private 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 index
# @users = User.all
@users = User.paginate(page: params[:page])
end
def destroy
User.find(params[:id]).destroy
flash[:success] = 'User deleted'
redirect_to users_url
end
private
# comment
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
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = 'Please log in.'
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
...@@ -12,6 +12,11 @@ module SessionsHelper ...@@ -12,6 +12,11 @@ module SessionsHelper
cookies.permanent[:remember_token] = user.remember_token cookies.permanent[:remember_token] = user.remember_token
end end
# Return true if the given user is the current user.
def current_user?(user)
user == current_user
end
# Return the current logged-in user (if any). # Return the current logged-in user (if any).
def current_user def current_user
# @current_user ||= User.find_by(id: session[:user_id]) # @current_user ||= User.find_by(id: session[:user_id])
...@@ -44,4 +49,15 @@ module SessionsHelper ...@@ -44,4 +49,15 @@ module SessionsHelper
session.delete(:user_id) session.delete(:user_id)
@current_user = nil @current_user = nil
end end
# Redirects to stored location (or to the default).
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# Stores the URL trying to be accessed.
def store_location
session[:forwarding_url] = request.original_url if request.get?
end
end end
# comment # comment
class User < ApplicationRecord class User < ApplicationRecord
attr_accessor :remember_token # we don't wan't to save this attribute to database attr_accessor :remember_token # we don't wan't to save this attribute to database
before_save { self.email.downcase! } before_save { email.downcase! }
validates :name, presence: true, length: { maximum: 50 } validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX }, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false } uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
has_secure_password has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
# Returns the hash digest of the given string. # Returns the hash digest of the given string.
def self.digest(string) def self.digest(string)
......
...@@ -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 %>
......
<%= form_for(@user) do |f| %>
<%#= form_for(@user, url: signup_path) do |f| %>
<%= render 'shared/error_messages', object: @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 %>
<%= 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' rel='noopener'>Change</a>
</div>
</div>
</div>
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>
<ul class='users'>
<%# @users.each do |user| %>
<%#= render user %>
<%# end %>
<%= 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-offset-3"> <div class="col-md-6 col-md-offset-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 %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div> </div>
</div> </div>
class AddAdminToUsers < ActiveRecord::Migration[5.1]
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: 20180531022956) do ActiveRecord::Schema.define(version: 20180601060938) 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: 20180531022956) do ...@@ -19,6 +19,7 @@ ActiveRecord::Schema.define(version: 20180531022956) 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: 'foo',
# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). email: 'foo@bar.com',
# password: 'foobar',
# Examples: password_confirmation: 'foobar',
# admin: true)
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
# Character.create(name: 'Luke', movie: movies.first) User.create!(name: 'Example User',
email: 'example@railstutorial.org',
password: 'foobar',
password_confirmation: 'foobar',
admin: true)
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
require 'test_helper' require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest class UsersControllerTest < ActionDispatch::IntegrationTest
def setup
@user = users(:foo)
@other_user = users(:archer)
end
test 'should redirect index when not logged in' do
get users_path
assert_redirected_to login_url
end
test 'should get new' do test 'should get new' do
get signup_path get signup_path
assert_response :success assert_response :success
end end
test 'should redirect edit when not logged in' do
get edit_user_path(@user)
assert_not flash.empty?
assert_redirected_to login_url
end
test 'should redirect update when not logged in' do
patch user_path(@user), params: { user: { name: @user.name,
email: @user.email } }
assert_not flash.empty?
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: 'foobar',
password_confirmation: 'foobar',
admin: true } }
assert_not @other_user.reload.admin?
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
...@@ -3,7 +3,29 @@ foo: ...@@ -3,7 +3,29 @@ foo:
name: foo name: foo
email: foo@bar.com email: foo@bar.com
password_digest: <%= User.digest('password') %> password_digest: <%= User.digest('password') %>
admin: true
michael: 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' require 'test_helper'
class SiteLayoutTest < ActionDispatch::IntegrationTest class SiteLayoutTest < ActionDispatch::IntegrationTest
# test "the truth" do def setup
# assert true @user = users(:foo)
# end end
test 'layout links' do test 'layout links' do
get root_path get root_path
...@@ -19,5 +19,13 @@ class SiteLayoutTest < ActionDispatch::IntegrationTest ...@@ -19,5 +19,13 @@ class SiteLayoutTest < ActionDispatch::IntegrationTest
get signup_path get signup_path
assert_select 'title', full_title('Sign up') assert_select 'title', full_title('Sign up')
get login_path
log_in_as(@user)
get root_path
assert_select 'a[href=?]', users_path
assert_select 'a[href=?]', user_path(@user)
assert_select 'a[href=?]', edit_user_path(@user)
assert_select 'a[href=?]', logout_path
end end
end end
require 'test_helper'
class UsersEditTest < ActionDispatch::IntegrationTest
def setup
@user = users(:foo)
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'
# assert_select(selector, equality?, message?)
# assert_select(element, selector, equality?, message?)
assert_select 'div.alert', count: 1, text: 'The form contains 4 errors.'
end
test 'successful edit with friendly forwarding' do
get edit_user_path(@user)
assert_equal session[:forwarding_url], 'http://www.example.com' + edit_user_path(@user)
log_in_as(@user)
assert_redirected_to edit_user_url(@user)
assert_nil session[:forwarding_url]
log_in_as(@user)
get edit_user_path(@user)
assert_template 'users/edit'
name = 'Foo Bar'
email = 'dude@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(:foo)
@non_admin = users(:archer)
end
test 'index 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
require 'test_helper' require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest class UsersLoginTest < ActionDispatch::IntegrationTest
include ApplicationHelper
# comment # comment
def setup def setup
@user = users(:michael) @user = users(:michael)
......
...@@ -3,7 +3,8 @@ require 'test_helper' ...@@ -3,7 +3,8 @@ require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest class UsersSignupTest < ActionDispatch::IntegrationTest
test 'invalid signup information' do test 'invalid signup information' do
get signup_path get signup_path
assert_select 'form[action="/signup"]' # assert_select 'form[action="/signup"]'
# assert_select 'form[action="/users"]'
assert_no_difference 'User.count' do assert_no_difference 'User.count' do
post signup_path, params: { user: { name: '', post signup_path, params: { user: { name: '',
email: 'user@invalid', email: 'user@invalid',
......
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