💡

Ruby で Hash#merge の変わりに { **attributes } を利用する

2024/01/04に公開

例えば以下のようにキーワード引数のデフォルト値を指定するときに Hash#merge を利用したい場合があります。

def user_create(**attributes)
  # attributes を優先しつつ、active, country のデフォルト値を指定する
  User.create({
    active: true,
    country: "日本"
  }.merge(attributes))
end

# こっちはデフォルト値が利用される
user_create(name: "homu")

# こっちは指定した値を優先して利用される
user_create(name: "mami", country: "アメリカ")

こういうときに Hash の ** を利用すると #merge を使わずにすっきり書けます。

def user_create(**attributes)
  # **attributes すると {} の中に直接 **attributes の中身が展開される
  User.create({
    active: true,
    country: "日本",
    **attributes
  })
end

JavaScript のスプレッド構文みたいな書き方ですね。
ただし、上記の場合は問題ないんですが attributesnil の場合だと ** はうまく動かないので注意する必要があります。

# 何かしらの理由で attributes が nil になっている場合
attributes = nil

# **nil するとエラーになってしまう
# error: no implicit conversion of nil into Hash (TypeError)
{
  active: true,
  country: "日本",
  **attributes
}

上記の挙動は以下のチケットによって将来的に改善されるかもしれません(既に matz の同意は得ており、あとはどう実装するのかだけ。

GitHubで編集を提案

Discussion