Rails ではセッションにモデルのインスタンスをぶち込んだりできるのですが,例えば複数のアプリケーションでログイン情報を共有するためにセッションを使ったりすると,モデルのクラス定義が存在しないとエラーになってしまいます.
アプリA -> User, Blog, Entry
アプリB -> User, Friend, Photo
in アプリA:
session[:new_entry] = Entry.new
to アプリB:
Session contains objects whose class definition isn't available.
Remember to require the classes for all objects kept in the session.
(Original exception: ERRORMESSAGE [Entry])\n
本来はこれは正しい動作なのですが,古いアプリなどで仕方なく連携する場合など困る場面もあります1.そこで仕方なく,例外を発生させずに空のクラスを定義することで回避します.
module ActionController
module Session
class AbstractStore
class SessionHash < Hash
private
def stale_session_check!
yield
rescue ArgumentError => argument_error
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
begin
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
eval(%Q{class #{$1}; def unused; true; end; end}, TOPLEVEL_BINDING)
#raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
end
retry
else
raise
end
end
end
end
end
end
ただこれだけだといろいろ問題になるので,この処理で定義した空クラスを削除します.app/controllers/application_controller.rb などで before_filter を定義して削除します.
def remove_unused_session
# セッション読み込み時に生成した class のデータを削除
session.to_hash.each{|k, d| session[k] = nil if session[k].respond_to?(:unused)}
end
あまり使わない方がいいノウハウですが,システム移行などでどうしてもと言う場合には使えるので,こういう手もあると覚えておくのはいいんじゃないでしょうか.
- もちろん Hash や Array など Ruby のクラスだけを使えうように書き換えれば問題ないのですが,そこまで工数割けない場合が多いと思います. [return]