devise rails current_user vs user_signed_in? - ruby-on-rails

I am using Devise on Rails 4.1
My question is regarding the helpers and how they relate to sessions.
current_user : tells you if there is a user session available for the user.
user_signed_in: tells you if the user is authenticated.
I cannot understand how a there can be a current_user if the user_signed_in? is false?
What is the difference between the two methods, and how does it relate to sessions.
THanks.
Richard Madson

user_signed_in? is provided as a convenience. You are correct in your assertion that if user_signed_in? is false, there will never be a current_user.
In the devise source code, we can see:
def #{mapping}_signed_in?
!!current_#{mapping}
end
(where user takes the place of #{mapping})
user_signed_in? simply returns the truthiness of current_user, ie, false if current_user is nil.

current_user method returns current signed-in user, while user_signed_in? method is used to verify if any user is signed in and returns true or false. if user_signed_in? is false then current_user method will return nil.
https://github.com/plataformatec/devise#controller-filters-and-helpers

Related

Defining a method on current_user without it throwing a no method error on non logged in users

I made a moderator method thats in the user model
def mod_of_game?(guide_id)
game_mods_relationships.exists?(game_category_id: guide_id)
end
Problem is that whenever the user isn't logged in it just throws a no method error on the page.
I'll be making more user methods in the future and i can only assume i'll come across this problem every time.
I haven't tried it but i guess i could put an if else statement in the method
def mod_of_game?(guide_id)
if current_user.nil?
#empty method
else
game_mods_relationships.exists?(game_category_id: guide_id)
end
But I feel there is a more efficient way that i'm not aware of. I'm Building an app to learn rails better so i guess this is one of the things I just dont know.
The problem is that if no user is logged in, current_user will be nil, not an instance of the User class. So, there is no way to fix this inside the User model, as current_user is not a User if it is nil. Also, current_user is generally not available in the model, just in the controller and view.
What I would recommend is to add a filter in the controller, to make sure that if no user is logged in, the visitor will be redirected to the log in page. This can be done with a before_action filter in the controller, something like:
class YourController < ApplicationController
before_filter :authenticate_user!
...
end
Otherwise you can always check if current_user is nil before calling .mod_of_game?, like so:
current_user.mod_of_game?(#guide) unless current_user.nil?
Try following:
# It will return `nil` if user is not logged in
def mod_of_game?(guide_id)
game_mods_relationships.exists?(game_category_id: guide_id) if current_user
end
Your pattern is wrong.
Calling mod_of_game? is an instance method, which means it's got to be called on an instance of User.
By the nature of current_user, you wouldn't be able to call this method unless the user was logged in, or at least invoked.
You'll have to use all the conditions on the front-end to determine firstly whether current_user exists, and then to call mod_of_game? on it...
<% if user_signed_in? && current_user.mod_of_game?(#guide) %>
--
A much better way would be to either create your own helper method, or to use the .try method:
#app/helpers/application_helper.rb
class ApplicationHelper
def mod? guide
return false unless current_user
current_user.mod_of_game? guide
end
end
This would allow you to call:
<% if mod? #guide %>
... which will return false if the user is not signed in, or the user is not a mod.
The reason the pattern is bad is because you're having to base logic on two conditions: user signed in? AND are they a mod?
What you want is a single point of logic, which will return true or false:
<% if current_user.try(:mod_of_game?, #guide) %>

Session Helper Methods in Michael Hartl Tutorial Chapter 8

I'm going through Michael Hartl tutorial on Ruby on Rails and I'm having trouble understanding some logic. Note that the logic works, it's just not resonating with me on what's actually happening.
In this chapter we're logging in users and creating a session. Here the helper methods:
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
#current_user ||= User.find_by(id: session[:user_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
end
Depending on whether the user's logged on or not we change the navigation with this conditional:
<% if logged_in? %>
do something....
<% else %>
do something else...
<% end %>
As I understand it, the code checks to see if the user is logged in by calling the logged_in? method. The logged_in? method calls the current_user method to see if it's nil. If it is nil it returns false, if it's not nil it returns true. As a test I tried to change the logged_in? method to the following:
def logged_in?
!#current_user.nil?
end
When I run this method for some reason, after I log in with credentials that are authenticated, #current_user returns nil. Why is this? Note this works if I change it back to the original logged_in? method where I'm just calling the method current_user.
This is not a direct answer to your question since you have that figured it out while I was trying to answer. But I want to clarify on some points.
In rails, in fact most of the web application, we track a user's log in state in server's session. the log_in method in your code does that.
Then when the new request comes in to a controller that requires authentication, we check the session if there is a stored user. If it exists then the request is authenticated, else it's unauthenticated. So, logged_in? method's actual responsibility is to check the session.
However, it is quite common that we want to access the authenticated user's attributes in the controller and/or views. So we set an #current_user variable on the controller so that you can access the User object of authenticated user. Again, using an instance variable directly is not a good practice. So we wrapped it in the current_user method.
Then you might ask, why don't we store the whole user object in session? Because it is bad to store much in session(see here). So, we just store the id and use it to get the user from db.
Here is where the ||= part comes in. ||= caches the result of db. Otherwise, we would be hitting db every time we call current_user method.
Hope this clarifies a bit on what's actually happening.
As I was formulating my question I figured this out. In the later case
def logged_in?
!#current_user.nil?
end
#current_user isn't set yet because the current_user method was never called. To test, I changed the method to the following:
def logged_in?
current_user
!#current_user.nil?
end
where the method was called first and then #current_user was evaluated. It worked without issue. The original method works because current_user returns #current_user to the logged_in? method as either a user object or nil (#current_user is set as the last line in the method -- it's the only line, so it's retuned implicitly to the logged_in? method).

What is the difference between current_user and #current_user (I am using devise) ?

I know #current_user is instance variable but I am still unclear about the difference between current_user and #current_user ? when should i use current_user and when #current_user ?
In my personal experience with devise I never use #current_user, current_user always suffices wherever and whenever I want.
The official documentation only mentions current_user too.
Devise Controllers filters and helpers
current_user is the helper method provided by devise to access the current user. There is nothing like #current_user from devise unless you have set it in your code.

current_user returns nil even when user_signed_in? returns true

I have devise 2.1.2 and cancan 1.6.8 installed.
The current_user helper from devise works fine in my controllers, but is not working the view templates. I'm confused why this is happening. I've been able to use current_user in my view files before.
I tried adding the before_filter authenticate_user! and that didn't help.
Worse comes to worse, I'll add a before filter to the application_controller that says #current_user = current_user but that seems silly.
It's weird that current_user returns nil while user_signed_in? returns true.
Any thoughts on how to get this resolved?
In the meantime,warden.authenticate(:scope => :user) seems to return the current_user so I'm just going to add that to my application helper.

Why am I getting error Undefined method `name' for nil:NilClass with Ruby on Rails?

I thought methods such as name and email were default in rails?
In my static pages view, in profile.html.erb I have:
<% if logged_in? %>
<% provide(:title, #user.name) %>
<% else %>
<% provide(:title, 'Profile')%>
<% end %>
I put in my static_page_controller
def profile
#user = User.find_by_remember_token(:remember_token)
end
When I go to the console User.find_by_remember_token("actualtoken").name returns me the appropriate users name, but :remember_token does not. How do I make :remember_token = the logged in users remember token?
In my sessions_helper I have
def log_in(user)
cookies.permanent[:remember_token] = user.remember_token
current_user = user
end
def logged_in?
!current_user.nil?
end
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= user_from_remember_token
end
def log_out
current_user = nil
cookies.delete(:remember_token)
end
private
def user_from_remember_token
remember_token = cookies[:remember_token]
User.find_by_remember_token(remember_token) unless remember_token.nil?
end
end
copying it to my static_pages_helper didn't accomplish anything.
Quick things you should be aware of the rails framework and the ruby language:
A function defined in any of your helpers will be available to all helpers and views (so there is no reason to copy and paste the same functions through different helpers);
You're probably using an authentication gem and I guess it is the Devise gem. If this is right, then you should not be overriding their helpers unless you have a reason to do this;
User.anything will call the static function anything from the User class;
user = User.find_by_anything(the_thing) is a class static helper provided by ActiveModel that will query the database looking for a user that has *anything = the_thing*; this user or nil will be returned;
user.an_attribute will call a function that returns the user specified attribute (which is the same as the column name of this attribute by default);
user.try(:anything) will try to call the function anything from the user and return its value. If user is nil, the returned value will also be nil.
That said, I guess you just wanted to retrieve the current user remember token, which can be accomplished with the following:
user = current_user.try(:remember_token)
EDITED: The question is a bit messy, but I also think the following code will work with your controller:
def profile
#user = User.find_by_remember_token(params[:remember_token])
end
You must access the request's parameters through the params hash.
EDIT: completely replaces my first answer with one hopefully not as stupid :-)
(While there are several ways to implement and manage sessions in Rails, the default uses a cookie in the browser to reference a key stored in memory. Sessions are created by a request from a browser, so while it's certainly possible to use the console to get at an existing session, it's probably not what you want.)
So your method, user_from_remember_token will either return a user or nil. What I don't see in your code is where you're setting the remember_token on the User model. I'll assume it's there, but you may want to have code that tells the user to log in if you don't find them. A common pattern would be
def current_user
#current_user ||= user_from remember_token
unless #current_user
flash[:notice] = "Yo! Log in first."
redirect_to login_path and return
end
end
There's no problem calling a model finder from a separate controller. But why call User.find_by_remember_token(:remember_token) -- you don't have the remember_token yet (right?). Don't you just want to call the current_user method in your sessions helper?
If the method is not visible, you may want to include or require the session helper in your application_controller.rb

Resources