Reputation: 139
Working through Rails Tutorial by M. Hartl Ch 8 Log in, Log out
When attempting to log out of my application, I receive the error message given in the title of my question.
I'm very new to this, but I think this means that the forget
instance method defined in models/user.rb
and called in SessionsHelper
is being called on user
which for some reason is nil. Here's the snippet I'm talking about in SessionsHelper
:
#Found in SessionHelper. Forgets persistent session
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_tolken)
end
And here's the definition of the forget
instance method:
#Found in class models/user.rb
def forget
update_attribute(:remember_digest, nil)
end
I don't know why user would be nil when called in SessionsHelper
. Here are the full files, including SessionsController
, SessionsHelper
, and User
.
I hope I was clear and used the right terminology. Let me know if I need to clarify.
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user # Log the user in and redirect to the user's showpage
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_to user # same as user_url(user)
else
# Show error message
flash.now[:danger] = 'Wrong email or password'
render 'new'
end
end
def destroy
log_out # if logged_in? # not logging out
redirect_to root_url
end
end
XXXXXXXXXX
module SessionsHelper
# Logs in given user
def log_in(user)
session[:user_id] = user.id
end
# Remembers a user in a persistant session
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
#Forgets persistent session
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_tolken)
end
# Returns the current logged-in user (if any)
def current_user
if (user_id = session[:user_id]) # CAREFUL! (not ==). Read if session of user_id exists
@current_user ||= User.find_by(id: user_id)
else (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
# Returns true if the user is logged-in, else false
def logged_in?
!current_user.nil?
end
# logs out current user
def log_out
session.delete(:user_id)
forget(current_user)
@current_user = nil
end
end
XXXXXXXXXX
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save {email.downcase!}
validates :name, presence: true, length: {maximum: 50}
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[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, length: {minimum: 6}
# Returns the hash digest of the given string. Used to make hashed password of user in fixture model for login integration testing
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns random token for remembering users via cookies
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persisten 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
Thank you
Upvotes: 2
Views: 1647
Reputation: 3282
Your log_out
method is deleting the session before forgetting the user. This causes an error because forget
takes current_user
as a parameter and current_user
uses the session to determine who the current user is:
if (user_id = session[:user_id]) # This will attempt to assign user_id based on the value of session[:user_id], but will fail since the session was deleted
You need to delete the session after forgetting the user.
Change your log out
method from this:
def log_out
session.delete(:user_id)
forget(current_user)
@current_user = nil
end
To this:
def log_out
forget(current_user)
session.delete(:user_id)
@current_user = nil
end
Upvotes: 1
Reputation: 13077
The current_user
method is returning nil, and that is throwing the error further down the line.
There is a typo in your current_user
method. Change the else
to elsif
and it should most likely fix the issue.
If that does not fix it, make sure the method is exactly the same as in Listing 8.57 of the rails tutorial ; the same is included below:
# Returns the user corresponding to the remember token cookie.
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
Upvotes: 1