Ruby rails 教程第 12 章出现随机无关错误
Ruby on rails tutorial chapter 12 random unrelated errors created
这是我第一次涉足应用程序开发,我一直在逐字逐句地学习 Michael Hartl 的教程。一切顺利,直到第 12 章快结束时,我的测试开始失败。奇怪的是,失败的测试似乎与我在第 12 章中所做的更改无关。
失败3次,错误1次,在浏览器手动查看时,也是本地服务器复制的。基本上 logged_in_user 之前的操作似乎不起作用,但仅适用于某些模型操作,而不是全部。我试图用调试器跟踪它并试图恢复到第 12 章之前的提交(这一切都确实有效)但是我在数据库迁移和其他方面遇到了麻烦并且只会引入越来越多的错误。下面是错误日志、我的用户控制器、应用程序控制器和会话助手。我觉得错误必须在其中之一。
非常感谢您的帮助!
FAIL["test_should_redirect_update_when_not_logged_in", UsersControllerTest, 2016-03-06 05:58:45 -0800]
test_should_redirect_update_when_not_logged_in#UsersControllerTest
(1457272725.92s)
Expected true to be nil or false
test/controllers/users_controller_test.rb:27:in `block in '
ERROR["test_should_redirect_destroy_when_not_logged_in", UsersControllerTest, 2016-03-06 05:58:45 -0800]
test_should_redirect_destroy_when_not_logged_in#UsersControllerTest
(1457272725.96s) NoMethodError: NoMethodError: undefined
method admin?' for nil:NilClass
app/controllers/users_controller.rb:77:in
admin_user'
test/controllers/users_controller_test.rb:47:in block (2 levels) in <class:UsersControllerTest>'
test/controllers/users_controller_test.rb:46:in
block in '
app/controllers/users_controller.rb:77:in admin_user'
test/controllers/users_controller_test.rb:47:in
block (2 levels) in '
test/controllers/users_controller_test.rb:46:in `block in '
FAIL["test_should_redirect_edit_when_not_logged_in", UsersControllerTest, 2016-03-06 05:58:46 -0800]
test_should_redirect_edit_when_not_logged_in#UsersControllerTest
(1457272726.03s)
Expected true to be nil or false
test/controllers/users_controller_test.rb:21:in `block in '
FAIL["test_successful_edit_with_friendly_forwarding", UsersEditTest, 2016-03-06 05:58:46 -0800]
test_successful_edit_with_friendly_forwarding#UsersEditTest
(1457272726.07s)
Expected response to be a redirect to <[removed link]/users/633107804/edit> but was a redirect to <[removed
link]/users/633107804>.
Expected "[removed link]users/633107804/edit" to be === "[removed link]/users/633107804".
test/integration/users_edit_test.rb:22:in `block in '
class UsersController < ApplicationController
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to_users_url
end
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
end
def new
@user = User.new
end
def index
@users = User.paginate(page: params[:page])
end
def create
@user = User.new(user_params)
if @user.save
@user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
@user = User.find(params[:id])
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 following
@title = "Following"
@user = User.find(params[:id])
@users = @user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
@title = "Followers"
@user = User.find(params[:id])
@users = @user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
和应用程序控制器:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include SessionsHelper
private
#confirms a logged in user. Now placed in the application controller so both teh user controller and microposts controller can use it
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
以及会话助手:
module SessionsHelper
#logs in the given user
def log_in(user)
session[:user_id] = user.id
end
# returns the current logged-in user (if any)
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: session[:user_id])
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
# returns true if the user is logged in, and false otherwise
def logged_in?
!current_user.nil?
end
#Saves a user id and remember token digest to cookies
def remember(user)
user.remember
#Code to save cookies with 20 year expiry ("permanent") and "signed" so it can't be accessed by third party
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
# runs the forget method and removes remember tokens from cookies
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# logs out the given user
def log_out
forget(current_user)
session.delete(:user_id)
@current_user = nil
end
def current_user?(user)
user == current_user
end
# Redirects to stored location after trying to edit while logged out and then logging in
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.url if request.get?
end
end
用户控制器测试:
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def setup
@user = users(:Joe)
@other_user = users(:Jane)
end
test "should redirect to index when not logged in" do
get :index
assert_redirected_to login_url
end
test "should get new" do
get :new
assert_response :success
end
test "should redirect edit when not logged in" do
get :edit, id: @user
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when not logged in" do
patch :update, id: @user, user: { name: @user.name, email: @user.email }
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect edit when logged in as wrong user" do
log_in_as(@other_user)
get :edit, id: @user
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect update when logged in as the wrong user" do
log_in_as(@other_user)
patch :update, id: @user, user: { name: @user.name, email: @user.email }
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'User.count' do
delete :destroy, id: @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 :destroy, id: @user
end
assert_redirected_to root_url
end
end
将您的代码与我的教程代码进行比较后,尝试更改 UsersController
中 before_action
语句的顺序,看看是否可行:
之前:
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
之后:
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
所有 before_action
语句都是 运行 按照它们在控制器中定义的顺序排列的,因此对于 update
操作,您目前正在 运行 correct_user
先过滤,后者调用 current_user?(@user)
,后者调用 current_user
:
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: session[:user_id])
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
此时在你的测试中,由于你还没有登录用户,你没有 session[:user_id]
,因此 if
语句失败,你也没有没有 cookies.signed[:user_id]
,因此 elsif
语句失败,留下 nil
return 值和 @current_user
未设置。
在您的测试请求中的某个时刻,您似乎找到了通往 admin_user
方法的途径,该方法正在调用 current_user.admin?
,因为此时 current_user
是 nil
,您得到的是您所看到的 NoMethodError: undefined method admin?' for nil:NilClass
。
这是我第一次涉足应用程序开发,我一直在逐字逐句地学习 Michael Hartl 的教程。一切顺利,直到第 12 章快结束时,我的测试开始失败。奇怪的是,失败的测试似乎与我在第 12 章中所做的更改无关。
失败3次,错误1次,在浏览器手动查看时,也是本地服务器复制的。基本上 logged_in_user 之前的操作似乎不起作用,但仅适用于某些模型操作,而不是全部。我试图用调试器跟踪它并试图恢复到第 12 章之前的提交(这一切都确实有效)但是我在数据库迁移和其他方面遇到了麻烦并且只会引入越来越多的错误。下面是错误日志、我的用户控制器、应用程序控制器和会话助手。我觉得错误必须在其中之一。
非常感谢您的帮助!
FAIL["test_should_redirect_update_when_not_logged_in", UsersControllerTest, 2016-03-06 05:58:45 -0800] test_should_redirect_update_when_not_logged_in#UsersControllerTest (1457272725.92s) Expected true to be nil or false test/controllers/users_controller_test.rb:27:in `block in '
ERROR["test_should_redirect_destroy_when_not_logged_in", UsersControllerTest, 2016-03-06 05:58:45 -0800] test_should_redirect_destroy_when_not_logged_in#UsersControllerTest (1457272725.96s) NoMethodError: NoMethodError: undefined method
admin?' for nil:NilClass app/controllers/users_controller.rb:77:in
admin_user' test/controllers/users_controller_test.rb:47:inblock (2 levels) in <class:UsersControllerTest>' test/controllers/users_controller_test.rb:46:in
block in ' app/controllers/users_controller.rb:77:inadmin_user' test/controllers/users_controller_test.rb:47:in
block (2 levels) in ' test/controllers/users_controller_test.rb:46:in `block in 'FAIL["test_should_redirect_edit_when_not_logged_in", UsersControllerTest, 2016-03-06 05:58:46 -0800] test_should_redirect_edit_when_not_logged_in#UsersControllerTest (1457272726.03s) Expected true to be nil or false test/controllers/users_controller_test.rb:21:in `block in '
FAIL["test_successful_edit_with_friendly_forwarding", UsersEditTest, 2016-03-06 05:58:46 -0800] test_successful_edit_with_friendly_forwarding#UsersEditTest (1457272726.07s) Expected response to be a redirect to <[removed link]/users/633107804/edit> but was a redirect to <[removed link]/users/633107804>. Expected "[removed link]users/633107804/edit" to be === "[removed link]/users/633107804". test/integration/users_edit_test.rb:22:in `block in '
class UsersController < ApplicationController
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to_users_url
end
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
end
def new
@user = User.new
end
def index
@users = User.paginate(page: params[:page])
end
def create
@user = User.new(user_params)
if @user.save
@user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
@user = User.find(params[:id])
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 following
@title = "Following"
@user = User.find(params[:id])
@users = @user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
@title = "Followers"
@user = User.find(params[:id])
@users = @user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
和应用程序控制器:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include SessionsHelper
private
#confirms a logged in user. Now placed in the application controller so both teh user controller and microposts controller can use it
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
以及会话助手:
module SessionsHelper
#logs in the given user
def log_in(user)
session[:user_id] = user.id
end
# returns the current logged-in user (if any)
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: session[:user_id])
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
# returns true if the user is logged in, and false otherwise
def logged_in?
!current_user.nil?
end
#Saves a user id and remember token digest to cookies
def remember(user)
user.remember
#Code to save cookies with 20 year expiry ("permanent") and "signed" so it can't be accessed by third party
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
# runs the forget method and removes remember tokens from cookies
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# logs out the given user
def log_out
forget(current_user)
session.delete(:user_id)
@current_user = nil
end
def current_user?(user)
user == current_user
end
# Redirects to stored location after trying to edit while logged out and then logging in
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.url if request.get?
end
end
用户控制器测试:
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def setup
@user = users(:Joe)
@other_user = users(:Jane)
end
test "should redirect to index when not logged in" do
get :index
assert_redirected_to login_url
end
test "should get new" do
get :new
assert_response :success
end
test "should redirect edit when not logged in" do
get :edit, id: @user
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when not logged in" do
patch :update, id: @user, user: { name: @user.name, email: @user.email }
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect edit when logged in as wrong user" do
log_in_as(@other_user)
get :edit, id: @user
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect update when logged in as the wrong user" do
log_in_as(@other_user)
patch :update, id: @user, user: { name: @user.name, email: @user.email }
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'User.count' do
delete :destroy, id: @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 :destroy, id: @user
end
assert_redirected_to root_url
end
end
将您的代码与我的教程代码进行比较后,尝试更改 UsersController
中 before_action
语句的顺序,看看是否可行:
之前:
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
之后:
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
所有 before_action
语句都是 运行 按照它们在控制器中定义的顺序排列的,因此对于 update
操作,您目前正在 运行 correct_user
先过滤,后者调用 current_user?(@user)
,后者调用 current_user
:
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: session[:user_id])
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
此时在你的测试中,由于你还没有登录用户,你没有 session[:user_id]
,因此 if
语句失败,你也没有没有 cookies.signed[:user_id]
,因此 elsif
语句失败,留下 nil
return 值和 @current_user
未设置。
在您的测试请求中的某个时刻,您似乎找到了通往 admin_user
方法的途径,该方法正在调用 current_user.admin?
,因为此时 current_user
是 nil
,您得到的是您所看到的 NoMethodError: undefined method admin?' for nil:NilClass
。