I am learning from rubytutorial and am having this error. I checked this answer and this but still does not work. I attached session_helper.rb and user.rb here. could someone show me where I miss?
1) Error:
UsersLoginTest#test_login_with_valid_information_followed_by_logout:
NoMethodError: undefined method `forget' for nil:NilClass
app/helpers/sessions_helper.rb:25:in `forget'
app/helpers/sessions_helper.rb:32:in `log_out'
app/controllers/sessions_controller.rb:19:in `destroy'
test/integration/users_login_test.rb:33:in `block in <class:UsersLoginTest>'
22 runs, 50 assertions, 0 failures, 1 errors, 0 skips
session_helper.rb
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(cookies[:remember_token])
log_in user
@current_user = user
end
end
end
def logged_in?
!current_user.nil?
end
# Forgets a persistent session.
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# Logs out the current user.
def log_out
forget(current_user)
session.delete(:user_id)
@current_user = nil
end
# Remembers a user in a persistent session.
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
end
user.rb
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
end
Your error message indicates that you have an:
undefined method forget' for nil:NilClass
This means that you attempted to call a method (forget
) on an object that did not have that method (nil
). Looking at the part of your code where the error is coming from...
def forget(user)
user.forget
...you can see that you are calling forget on the variable user
. This is being called from the log_out method:
def log_out
forget(current_user)
So, you are passing current_user
to the forget
method, and then calling forget
on it. Only, it is not an instance of the User class (like you expected), which does have a forget
method, but rather is nil
. nil
does not have a forget method.
This is probably nil because you are not authenticated and there is no current_user
. One way to remedy this would be to check if the current user actually exists before forgetting it, like this:
def log_out
current_user && forget(current_user)
A more sophisticated way to handle this issue would be with the Null Object Pattern, where current_user
would return an instance of User with minimal authorizations, instead of nil.