Blog

Load a session by its ID on a Padrino stack

The problem

Lately I’ve worked on the porting to Ruby of an PHP web application we developed internally, with a view to open source it.

One peculiarity of the application is that, from a certain point of the flow on, the session ID is stored as part of the URL. The user can later on resume the process from a different browser using that URL.

Loading a session knowing the session ID is trivial in PHP:

session_id($sid);session_start();

However, searching the Web and reading the Ruby forums, I couldn’t find a similar functionality in Ruby. Had to roll my sleeves up and find my way to an instance of a session store, somewhere in the code.

Looking for a solution

The web application is based on Padrino, which extends Sinatra, which itself is build on top of Rack: finding the right access point to the session store was challenging. The first step was to remove Padrino’s standard, cookie-based session initialization and use a memory-based session pool

  • enable :sessions
  • use Rack::Session::Pool

Still, this proved not to be helpful, as I couldn’t still find the entry point to the session store.

My fellow developer @kennylovrin suggested to get completely rid of the session ID in the URL, and use instead a short-lived, self-generated token. While @kennylovrin’s solution definitely would have worked, I refused to believe that there wasn’t a cleaner fix out there.

Google’ing around to find a solution, I eventually stumbled upon Moneta, an interface to several key/value stores (among which the memory based one that I needed), which nicely integrates with Rack to provide a session storage middleware. That was the solution I was looking for.

How to load the session so?

The first step is to remove Rack::Session::Pool and replace it with

require 'moneta'require 'rack/session/moneta'use Rack::Session::Moneta, store: Moneta.new(:Memory, expires: true)

Through Moneta, it’s now possible to access the session store. I defined this Padrino helper for this purpose:

def getSessionStore	@env['rack.session.options'][:store]end

With this implementation, getting the session is as easy as

session = sessionStore.load(sessionId)

That does the job!

Conclusion

Despite the described workaround being effective, I’m still convinced there must be an easier, add-on free solution that relies completely on Rack. Feel free to share a better solution if you know one!