LoginSignup
1

More than 3 years have passed since last update.

posted at

Rubyのキーワード引数にto_hashを実装するオブジェクトを渡すとキーワード引数として展開される

TL;DR

Rubyにおいて、キーワード引数を取るメソッドには、to_hashメソッドを実装しているオブジェクトを渡すことができて、キーワード引数として展開して解釈される。

キーワード引数とは

def log(msg, level: "ERROR", time: Time.now)
  puts "#{ time.ctime } [#{ level }] #{ msg }"
end

log("Hello!", level: "INFO")  #=> Mon Feb 18 01:46:22 2013 [INFO] Hello!

(https://magazine.rubyist.net/articles/0041/0041-200Special-kwarg.html より引用)

このlogメソッドのleveltimeみたいな感じで、引数にデフォルト値を指定することができて、また名前を指定して引数を渡せるという便利機能です。

Hashはキーワード引数に展開される

キーワード引数にはHashを渡すことができて、その場合はHashの中身が展開されます

# 普通の使い方
log('aaa', level: 'AAA') # => Wed Nov  6 09:16:36 2019 [AAA] aaa
# Hashも渡せる
log('aaa', {level: 'AAA'}) # => Wed Nov  6 09:16:44 2019 [AAA] aaa

このようなHash => keyword引数の変換はRuby 2.7ではdeprecatedになったので注意してください 1

Hash以外もキーワード引数にわたす

そして、キーワード引数に渡して展開されるのはHashだけではありません。「to_hashを実装しており、かつその返り値のkeyがsymbolであるオブジェクト」ならなんでも展開されます。

# to_hashを実装しているクラス
class HogeWithToHash
  def to_hash
    { level: 'AAA' }
  end
end

# キーワード引数として展開される
log('aaa', HogeWithToHash.new) # => Wed Nov  6 09:27:25 2019 [AAA] aaa

# to_hashを実装しているが返り値のHashのkeyが文字列であるクラス
class HogeWithToHashStringKey
  def to_hash
    { 'level' => 'AAA' }
  end
end

# キーワード引数として正しく展開されない
log('aaa', HogeWithToHashStringKey.new) # => ArgumentError (wrong number of arguments (given 2, expected 1))

キーワード引数に展開されるオブジェクトの例: Rake::TaskArguments

このような条件を満たすオブジェクトとして、Rakeタスクを定義するときに使われる Task::Arguments が挙げられます。したがって、以下のRakefileは正しく動きます:

def greet(first_name:, last_name:)
  puts "Hello, #{first_name} #{last_name}"
end

task :greet, [:first_name, :last_name] do |t, args|
  greet(args)
end
$ rake "greet[Ichiro,Tanaka]"
Hello, Ichiro Tanaka

以上

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
What you can do with signing up
1