脳汁portal

アメリカ在住(だった)新米エンジニアがその日学んだIT知識を書き綴るブログ

[Ruby] Cookieのvalueにhashを使う(StringをHashに変更する)方法

問題点

  • Cookieの:valueにHashをいれても、取り出すときにStringになってしまう

先日のブログで書いたとおり、セッション毎に別々のexpire timeを持たせるためにcookieを利用することにした.portaltan.hatenablog.com

・・・が、cookieの:valueにはHashが入らなかった(全部Stringに変換されてしまう)

response.set_cookie(:key, :value => {'first_value' => '1banme', 'second_value' => 2})

h = request.cookies['key']
logger.info h.class            # => String
logger.info h.size             # => 44
logger.info h.kind_of?(Hash)   # => nil
logger.info h                  # => {"first_value"=>"1banme", "second_value"=>2}

解決法

取り出すときにStringをHashに戻すメソッドを書いた

def revert_hash_from_string(str)
  if str[0] != '{'
    raise "Unexpected style"
  end

  str = str.chomp.gsub(/"|^{|}$/, '')
  str = str.split(/,[\s]*|=>/)
  str.each_with_index{|column, idx|
    str[idx] = column.to_i if column =~ /^\d+$/
  }

  Hash[*str]
end

手順としては

  1. 引数がHashスタイルの文字列じゃない場合はException
  2. まずは文字列にはいっている文字列を囲む「"」と先頭の「{」と末尾の「}」を削る
  3. 次にスペースで区切って配列にする
  4. valueが数字だけで構成されている場合はInt型に変換する
  5. 上記のArrayを展開してHashにして返す

確認

response.set_cookie(:key, :value => {'first_value' => '1banme', 'second_value' => 2})
h = revert_hash_from_string(request.cookies['key'])

logger.info h.class           # => Hash
logger.info h.size            # => 2
logger.info h.kind_of?(Hash)  # => true
logger.info h                 # => {"first_value"=>"1banme", "second_value"=>2}

logger.info h['first_value']        # => 1banme
logger.info h['first_value'].class  # => String
logger.info h['second_value']       # => 2
logger.info h['second_value'].class # => Fixnum

強引なやり方だがとりあえずこれで行こうと思う。
(前のエントリーも含めてもっといい方法があれば教えて欲しいです。。)