Getting DataMapper session storage working with Rails 3.0

I was setting up a new Rails 3 project last night and out of instinct I went straight to config/initializers/session_store.rb to change from storing user sessions in cookies to storing them in the database. I wanted to use DataMapper for this project, but the only example shown was for using ActiveRecord session storage:

config/initializers/session_store.rb

# Cookie storage:
MyApp::Application.config.session_store :cookie_store, :key => '_my_app_session'

# ActiveRecord storage:
MyApp::Application.config.session_store :active_record_store

I assumed that it would be as simple as changing it to session_store :data_mapper_store. After spending some time trying to figure this out on my own, I worked with Dan Kubb (dkubb) in #datamapper and arrived at the following solution:

require 'dm-rails/session_store'

ActionDispatch::Session::DataMapperStore = Rails::DataMapper::SessionStore
MyApp::Application.config.session_store :data_mapper_store

I’m going to provide more detail on this (as well as work with the DataMapper guys to make this simpler for the future) but just wanted to post this up in case anybody was struggling with it. I doubt I was the first person to try and use DataMapper session storage, but who knows?

UPDATE

After working through this issue with Gibheer and knowtheory in #datamapper, we were able to find the relevant code here:

railties/lib/rails/application/configuration.rb

def session_store(*args)
  if args.empty?
    case @session_store
    when :disabled
      nil
    when :active_record_store
      ActiveRecord::SessionStore
    when Symbol
      ActionDispatch::Session.const_get(@session_store.to_s.camelize)
    else
      @session_store
    end
  else
    @session_store = args.shift
    @session_options = args.shift || {}
  end
end

So naturally, ActiveRecord’s own session storage is hard-coded in. The interesting thing to note here is that if you pass a Symbol in as the first argument, that symbol is cached instead of the class it resolves to. Example: if you pass in :data_mapper_store, every time this method is called it will perform the string conversions on @sessionstore and do a constget lookup, rather than just caching @session_store = ActionDispatc::Session::DataMapperSession.

Because of this inefficiency, the DataMapper crew will probably not be renaming the Rails::DataMapper::SessionStore class to fit the convention expected when passing a Symbol for the session_store. Instead, do the following:

require 'dm-rails/session_store'

MyApp::Application.config.session_store Rails::DataMapper::SessionStore