The dreaded :false

March 14, 2008 – 8:36 am

If 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

  1. 5 Responses to “The dreaded :false”

  2. Shouldn’t you use a conditional with `logged_in?` rather than testing the result of current_user?

    By Jeremy McAnally on Mar 14, 2008

  3. 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

  4. 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

  5. That is ridiculously helpful - thanks!

    By Ben on Mar 14, 2008

  6. 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

Post a Comment