The dreaded :false
March 14, 2008 – 8:36 amIf you use the restful_authentication plugin, you’ve probably run up against :false. If not, let me explain:
Restful_authentication provides a method called current_user. If someone’s logged in, the method returns his or her User object. If no one is logged in, though, current_user returns :false. This is (for me, at least) a violation of the PoLS - I’d expect it to return nil, or at worst the Boolean false.
The rationale for returning :false is that is allows RA to memoize the results of the current_user method, which is defined thusly:
def current_user
@current_user ||= (login_from_session || login_from_basic_auth \
|| login_from_cookie || :false)
end
So if it’s the first time you call current_user, the system will try to authenticate the current user in a variety of ways; if each fails, it returns :false, which prevents subsequent calls from hitting those methods again. It’s a good idea, but that :false still irks me.
Since Rick Olson’s plugins are now all on GitHub, though, I have some recourse. I forked restful_authentication and made the following single change:
def current_user
@current_user ||= login_from_session || login_from_basic_auth || \
login_from_cookie unless @current_user == false
end
This has the same memoizing effect as using :false, but sets current_user to Boolean false instead of to the symbol. The key is the explicit unless @current_user == false - which fails if @current_user is undefined or nil (unlike unless @current_user, which passes in those cases).
It’s a simple change, and one that I think makes a lot of sense.
UPDATE OK, I’m a moron - there are two other minor changes in this version of RA. here’s the entire changed section:
def logged_in?
current_user
end
# Accesses the current user from the session. Set it to :false if login fails
# so that future calls do not hit the database.
def current_user
@current_user ||= login_from_session || login_from_basic_auth || \
login_from_cookie unless @current_user == false
end
# Store the given user id in the session.
def current_user=(new_user)
session[:user_id] = !new_user ? nil : new_user.id
@current_user = new_user
end


5 Responses to “The dreaded :false”
Shouldn’t you use a conditional with `logged_in?` rather than testing the result of current_user?
By Jeremy McAnally on Mar 14, 2008
In general, I do - but I’ve run into a couple of cases where that isn’t possible. Generally, this is when a method accepts a user object and is sometimes called with current_user, and others with a known other user. Within such a method, you can’t use logged_in?, so you have to test the passed-in user directly.
Also, I just realized that I skipped over two other changes to support this - I’ll update the post.
By Ben on Mar 14, 2008
You can use a method that redefines itself when you want to cache the result but also allow nil or false as a return value. See e.g. http://redhanded.hobix.com/inspect/methodsThatSelfDestruct.html
Also, Rick should know about this technique because he’s abstracted it to expiring_attr_reader
http://svn.techno-weenie.net/projects/plugins/expiring_attr_reader/
By Sven Fuchs on Mar 14, 2008
That is ridiculously helpful - thanks!
By Ben on Mar 14, 2008
The :false thing irked me too when I first looked at restful_authentication - if a symbol was needed why not something a bit more meaningful like :guest ?
In the project I’m working on at the moment I got tired of checking if a user was logged in or not so I changed current_user to return an instance of a NilUser class when a user isn’t logged in, where NilUser is based on the Null object pattern: http://en.wikipedia.org/wiki/Null_Object_pattern
By Rob Anderton on Mar 17, 2008