Rails Metal and ActiveRecord Sessions

I was playing around in Rails 2.3 and wanted to mess with Metal for a very simple API. The release notes say that it supports sessions — and that may be true for memcache and cookie stores, but I couldn’t get it working with ActiveRecord.

I sniffed around and noticed that abstract stores aren’t getting loaded as of this version (RC1).

# action_controller/session/abstract_store.rb
def get_session(env, sid)
  raise '#get_session needs to be implemented.'
end

def set_session(env, sid, session_data)
  raise '#set_session needs to be implemented.'
end

So, to work around that, I grabbed the actual get_session code from actioncontroller’s SqlBypass class and got it working within metal:

def self.call(env)
  if env["PATH_INFO"] =~ /^\/test/

    request = Rack::Request.new(env)
    session_options = env['rack.session.options']
    sid = request.cookies[session_options[:key]]
    session = ActiveRecord::SessionStore::Session.find_by_session_id(sid)
    session ||= ActiveRecord::SessionStore::Session.new(:session_id => sid, :data => {})

    [200, {"Content-Type" => "text/html"}, session.data.inspect]
  else
    [404, {"Content-Type" => "text/html"}, ["Not Found"]]
  end
end

This has worked so far as I can get the user_id necessary. I’m not sure there is a “just works” way of doing this — but I couldn’t find it and Google didn’t help much.

Hopefully someone out there can make use of this, I scratched me head for a while before just loading the session by hand.

Using Middleware

I just hacked this together and it seems to work (for my testing at least):

# lib/force_active_record_session.rb
module Rack
  class ForceActiveRecordSession
    def initialize(app, options = {})
      @app = app
    end
    def call(env)
      request = Rack::Request.new(env)
      sid = request.cookies[env['rack.session.options'][:key]]
      env['rack.session.record'] = \
        ActiveRecord::SessionStore::Session.find_by_session_id(sid) || \
          ActiveRecord::SessionStore::Session.new(:session_id => sid, :data => {})
      @app.call(env)
    end
  end
end

# environment.rb
require 'lib/force_active_record_session.rb'

# environment.rb -- inside Initializer block
config.middleware.insert 5, Rack::ForceActiveRecordSession

# in your metal action
session = env['rack.session.record'].data rescue {}
[200, {"Content-Type" => "text/html"}, session.inspect]
Just double check with rake middleware that the Rack::ForceActiveRecordSession is before Rails::Rack::Metal


Comments

No comments yet.

Add Yours

  • Author Avatar

    YOU


Comment Arrow



About Author

Rob Hurring

Ruby, Rails, PHP, bash... oh my!