yo_waka's blog

418 I'm a teapot

RailsのセッションをJSONで

Railsのセッション情報はこちらの記事にあるようにRubyのMarshalでシリアライズされ、Base64エンコードされたものがクッキーなどに保存される。

ただ、Ruby以外では復元が難しいため、Node.jsなど別言語で書かれたサーバーアプリケーションとセッションを共有しづらい。
なので、こちらの記事を参考にJSONで保存できるようにしてみた(というかほとんどそのまま)。

セッション情報の保存先にはredis-storeライブラリを使ってRedisに保存する。
redis-storeは、RubyGemsにあがっているものだとRails4で動かない(Rails3でもredisライブラリのバージョンが新しいと動かない)。
GitHubを見てみたら、forkされたものが最近のRackのバージョンアップ含めてRails4対応していたので、それを使う。

Gemfile

gem 'redis'
gem 'redis-store', github: 'bricker/redis-store'
gem 'redis-rails', github: 'bricker/redis-store'

config/initializer/session_store.rb

require 'action_dispatch/middleware/session/redis_store'

#
# デフォルトだとRubyのMarshalでセッションオブジェクトを保存するため、Node.js側で扱えない
# JSONで保存するためにモンキーパッチをあてる
#
module ActionDispatch
  module Session
    class RedisStore
      # override
      def get_session(env, sid)
        with_lock(env, [nil, {}]) do
          unless sid and session = @pool.get(sid)
            sid, session = generate_sid, {}
            unless /^OK/ =~ @pool.set(sid, JSON.generate(session))
              raise "Session collision on '#{sid.inspect}'"
            end
          end
          session = JSON.parse(session) unless session == {}
          if session.has_key?('flash')
            session['flash'] = ActionDispatch::Flash::FlashHash.new.update(Hash[*session['flash']])
          end
          [sid, session]
        end
      end

      # override
      def set_session(env, session_id, new_session, options)
        with_lock(env, false) do
          jsonize_session = JSON.generate(new_session)
          @pool.set session_id, jsonize_session, options
          session_id
        end
      end
    end
  end
end

# セッションの生存期間は1週間
Sample::Application.config.session_store :redis_store, servers: {
  host: "localhost",
  port: 6379,
  namespace: "_sessions",
  db: 10,
  expire_in: 60 * 60 * 24 * 7
}

redis-store、もう1年くらい更新されてないしissuesもpull reqも溜まってるけど作者の人やる気もうないのかな。。。