Railsのバージョンを6.1から7.0に上げた時にMarshal.dump(params)
みたいなコードがTypeError (can't dump IO)
というエラーが出て一部使えなくなっていました。
paramsの中身はActionController::Parametersによるparamsです。
原因
一体なぜなのかよくわからなかったのですが、調べてみるとこんな結果が。以下一部引用させてもらいます
paramsハッシュに「logging_context」ハッシュを追加し、その中に「:request」オブジェクトがあり、Marshalはそれを捨てたくないのです。
logging_contextはActionController::Parametersのinitialize時に代入される変数の一つのようです。以下引用
def initialize(parameters = {}, logging_context = {})
# 〜〜〜〜〜〜〜〜〜〜〜<中略>〜〜〜〜〜〜〜〜〜〜〜〜〜〜
@parameters = parameters.with_indifferent_access
@logging_context = logging_context <= これ
@permitted = self.class.permit_all_parameters
end
logging_contextの中身を見てみる
デバッグ機能で見れるらしいという噂もありますが、方法がよくわからないのでモンキーパッチを当てて確認します。
def initializeの中身はコピペしただけのものを用意します。
module ActionController
class Parameters
def initialize(parameters = {}, logging_context = {})
parameters.each_key do |key|
unless key.is_a?(String) || key.is_a?(Symbol)
raise InvalidParameterKey, "all keys must be Strings or Symbols, got: #{key.class}"
end
end
@parameters = parameters.with_indifferent_access
@logging_context = logging_context
@permitted = self.class.permit_all_parameters
pp @logging_context # 見るだけなのでこれで
end
end
end
結果はこんな感じ
{:controller=>"HomesController",
:action=>"index",
:request=>#<ActionDispatch::Request GET "http://localhost:3000/homes" for ::1>,
:params=>{"controller"=>"homes", "action"=>"index"}}
全てのlogging_contextがこんな感じかはわからないのですが、とりあえず自分の環境ではこのような結果になりました。
つまり、この
:request=>#<ActionDispatch::Request GET "http://localhost:3000/homes" for ::1>
の部分をrails的にはdumpしたくないということでしょうか。
例えば普通に画面から送られてきているparamsをdumpしようとすると失敗します。
paramsの中身
<ActionController::Parameters {"age"=>"25"} permitted: false>
dumpしてみる
Marshal.dump(params)
結果 エラーになる
TypeError (can't dump IO)
logging_contextから:requestを除けばdumpができるようになるのですが、流石にそれはダメそうです。
対策
対策その1 permitするとdumpできるようになる
普通にこれが手堅いような気がします
Marshal.dump(params.permit(age: 25))
対策その2 hashに変えてしまう
私の場合はparamsだと不都合が出る場合があったのでhashに変換することでdumpできるようになりました。
parameters以外の情報が必要ないという場合はこれでもいいかもしれません。
Marshal.dump(params.to_unsafe_h)
対策その3 新しくActionController::Parametersを作る
要素の同じActionController::Parametersであっても、その場で生成したActionController::Parametersにはrequest情報がないためdumpが可能です。
ただあまり使い所はないかもしれません。
その場でActionController::Parametersを生成
Marshal.dump(ActionController::Parameters.new(params.to_unsafe_h))
結果 dumpが成功する
"\x04\bo:!ActionController::Parameters\b:\x10@parametersC:-ActiveSupport::HashWithIndifferentAccess{\x06I\"\bage\x06:\x06EFi\x1E:\x15@logging_context{\x00:\x0F@permittedF"