LoginSignup
0
0

More than 1 year has passed since last update.

Rails7でActionController::ParametersをMarshal.dumpしようとすると TypeError (can't dump IO) となる

Last updated at Posted at 2023-03-22

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時に代入される変数の一つのようです。以下引用

actionpack/lib/action_controller/metal/strong_parameters.rb
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の中身はコピペしただけのものを用意します。

lib/monkey_patches/strong_parameters.rb
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しようとすると失敗します。

tekitous_controller.rb
paramsの中身
<ActionController::Parameters {"age"=>"25"} permitted: false>

dumpしてみる
Marshal.dump(params)

結果 エラーになる
TypeError (can't dump IO)

logging_contextから:requestを除けばdumpができるようになるのですが、流石にそれはダメそうです。

対策

対策その1 permitするとdumpできるようになる

普通にこれが手堅いような気がします

tekitous_controller.rb
Marshal.dump(params.permit(age: 25))

対策その2 hashに変えてしまう

私の場合はparamsだと不都合が出る場合があったのでhashに変換することでdumpできるようになりました。
parameters以外の情報が必要ないという場合はこれでもいいかもしれません。

tekitous_controller.rb
Marshal.dump(params.to_unsafe_h)

対策その3 新しくActionController::Parametersを作る

要素の同じActionController::Parametersであっても、その場で生成したActionController::Parametersにはrequest情報がないためdumpが可能です。
ただあまり使い所はないかもしれません。

tekitous_controller.rb
その場で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"
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0