📑

ActionController::Parameters#to_h は permit した属性がないとエラーになる

に公開

知らなかったので覚書。
ActionController::Parameters#to_h は通常 permit された属性のみを含んだ Hash を返します。

require "action_controller"

params = ActionController::Parameters.new({
  name: "Senjougahara Hitagi",
  oddity: "Heavy stone crab"
})

# name のみを許可する
safe_params = params.permit(:name)

# to_h では許可された属性のみを Hash に変換する
p safe_params.to_h
# => {"name"=>"Senjougahara Hitagi"}

しかし、逆に permit された属性がない場合に #to_h を呼び出すとエラーになります。

require "action_controller"

params = ActionController::Parameters.new({
  name: "Senjougahara Hitagi",
  oddity: "Heavy stone crab"
})

# error: unable to convert unpermitted parameters to hash (ActionController::UnfilteredParameters)
p params.to_h

こんな感じで permit された属性がない場合は利用を禁止しているみたいですね。
コード上で #to_h を利用する場合は permit されているかどうかで挙動が変わりので注意が必要そうです。

おまけ #to_h の戻り値

#to_h メソッドの戻り値なんですが厳密にいうと Hash ではなくて ActiveSupport::HashWithIndifferentAccess を返します。

require "action_controller"

params = ActionController::Parameters.new({
  name: "Senjougahara Hitagi",
  oddity: "Heavy stone crab"
})

# name のみを許可する
safe_params = params.permit(:name)

# Hash ではなくて ActiveSupport::HashWithIndifferentAccess を返す
p safe_params.to_h.class
# => ActiveSupport::HashWithIndifferentAccess

# なので String でも Symbol でもアクセスできたりする
p safe_params.to_h["name"] # => "Senjougahara Hitagi"
p safe_params.to_h[:name]  # => "Senjougahara Hitagi"

Hash だと思っていると結構ぎょっとする挙動ですねー。
ちなみに #to_hash だと Hash が返ってきます。

require "action_controller"

params = ActionController::Parameters.new({
  name: "Senjougahara Hitagi",
  oddity: "Heavy stone crab"
})

# name のみを許可する
safe_params = params.permit(:name)

p safe_params.to_hash.class
# => Hash
GitHubで編集を提案

Discussion