Reputation: 313
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
Upvotes: 2
Views: 1240
Reputation: 11
Soooo, I realize this is like 2 years later... but I had the same issue here as OP...
my solution was (as explained in the tutorial) to change the destroy
method in the SessionsController
from:
def destroy
log_out
redirect_to root_url
end
to:
def destroy
log_out if logged_in?
redirect_to root_url
end
In case anyone else is as late to the rails game as I am... hope this helps
Upvotes: 1
Reputation: 11275
Look at your log, you are trying to call .forget
on nil
object:
NoMethodError: undefined method `forget' for nil:NilClass
That means you passed nil
to SessionsHelper#forget
, so your current_user
is nil
.
Are you trying to logout an user currently not logged-in? If not, there should be something wrong with method current_user
because it return nil
while user already logged-in.
Upvotes: 1
Reputation: 17647
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.
Upvotes: 2