Behind the Technology: Managing Sessions w/ “Ghost” objects
Here at Stackd we use Ruby on Rails and CouchDB to serve our main application. One problem we ran into was managing user sessions. Initially, we had session[:user] set equal to a Seat::Record (see seat-rb, our Ruby CouchDB driver) object as such…
https://gist.github.com/945914
This works pretty well, but keeping the session variable up to date can be a pain. By storing the entire object in the session, we have to update the session object every time we update the database record for the logged in user. Even worse, if saving the object fails, your session object could show values different than those in the database. You may also run into problems if your web application is not the only thing updating database records. This is especially important when using CouchDB. In order to update a document, we need to have the entire contents of the most recent revision of that document.
Our initial solution was setting session[:user] equal to the logged in user’s username, and on each page load fetch that user’s document from CouchDB. Then, we set an instance variable equal to that document (in a before_filter called “all”).
https://gist.github.com/956605
This solved our initial problems, but in many cases it wasn’t necessary to load the entire document. Many pages simply needed to check whether a user was logged in, or display the logged in user’s username. This information is available to us w/o the need to fetch anything from CouchDB.
So what is the best way to go about only fetching the document from the database only when it is needed? One solution is to not have a @user instance variable defined on all pages. Then, we could load the entire document on a per action basis when needed. This would work, but would clutter up our actions and is not very DRY.
Instead, we decided to make a minor modification to the previous example. Instead of setting @user equal to a Seat::Record, we set it equal to a “Ghost” class. This “Ghost” class acts like a Seat::Record object type, but does not fetch the entire document from CouchDB unless it is needed.
https://gist.github.com/956620
The UserGhost class allows us to call @user.username without fetching the entire document from the database. If we try to get @user.email or any attribute other than the username, our UserGhost class will fetch the entire document from the database and behave just as an instance of User would.