I’m not using any authentication gem or plug in, I’m rolling my own. I’m actually using OpenID, so rolling my own is not hard: no passwords to encrypt, resend and send, no email address to verify. I think this is the future. But I’m also doing something that I call ghost users.
When you use one of my web apps you can start using most of it right away. At first you are a totally anonymous user but as soon as you do something that requires you to have an account on the database I create one for you. In the case of Sano, the moment you add a weight you’ll have an account.
At that moment your account is accessible thanks to the session of your browser. The moment you close the browser that account is no longer accessible. If you use the app and like it and decide to stick around, all you have to do is log in with your OpenID. For using the web app there’s no barrier of entry.
From the programming point of view this creates some challenges:
- Maintenance of the users and related data. Over time the user database will have users that are no longer accessible with weights and other related data. That needs cleaning up. For the moment I’m going to ignore that problem because it has no impact on the system and it’s not likely to present a problem any time soon.
- Getting a user object is no longer a query to the database. Any piece of code should be able to get a user object no matter what’s the status of the session. Sometimes the user object is only needed in memory and sometime it’s needed in the database. For example: if you create a new weight, I need a user object that’s been saved on the database; to list your weights, I need a user, I don’t care if it’s in the database or not (if it’s not, it’s not going to have any weights, which is where you start).
- Merging users happens regularly. Look at this scenario: you go to the app, you log in, you enter your weight, you leave; you go back to the app, you don’t log in, you enter a new weight. Now you have one account with one weight and another session only account with another weight. You realize you need to log in and you do it. At that moment I have to merge the two users.
To make it as simple as possible I’ve created a method called user in my application controller that is also declared as a helper method so it’s available for all controllers and all views. This method is guaranteed to return a user, and if you call with options[:create]=true then it’s guaranteed to return a user that is also on the database. What is not guaranteed is that the user object represents a permanent account. It may be what I call a ghost user.
This is the method:
class ApplicationController < ActionController::Base helper_method :user helper_method :user! protected # Helper method to get the current user. It will always return a user but the # user may not be in the database. If options[:create] is true, then the user # will be in the database (although it may be a ghost user). def user(options = {:create => false}) # If we already have a user object, return that. return @user if @user != nil # Try to find the user in the database if session[:user_id] is defined. @user = User.find(session[:user_id]) if session[:user_id] != nil return @user if @user != nil # Create a new user object. @user = User.new() if options[:create] # Save the user in the database and set the session user_id for latter. @user.save session[:user_id] = @user.id end return @user end # Helper method to get a user that for sure exists on the database. def user! return user(:create => true) end end
Leave a Reply