読者です 読者をやめる 読者になる 読者になる

Rubyのブロックをメソッド間で受け渡す

ブロックとは

ブロックについてはこちらの記事が分かりやすかったです。

qiita.com

ブロックを引数にとるメソッドの定義

定義方法は、ブロックを引数にとることを明示する or しないの2通りがあります。
メソッドを呼び出したときの実行結果は同じです。

明示する

# 定義
def hoge_method(&block)
  block.call # ブロックの実行
end

# 呼び出し
hoge_method do
  puts 'hello world'
end
=> hello world

明示しない

# 定義
def fuga_method
  yield # ブロックの実行
end

# 呼び出し
fuga_method do
  puts 'hello world'
end
=> hello world

以下では明示する方法で記述していきます。

引数をとるブロックを渡す

ブロックを受け取るメソッドの中でいろいろ処理を行い、その結果をブロックで使用したい場合があると思います。
そういったときには、ブロックが引数をとるようにし、ブロックの実行時に処理結果を渡すようにします。

# 定義
def sample_method(&block)
  result = 'hello'   # 処理
  result << ' world' # 処理
  block.call(result) # ブロックに result が渡される
end

# 呼び出し
sample_method do |message|
  puts message # result の中身が message に入る 
end
=> hello world

ブロックを引数にとるメソッドを 別のメソッドで呼び出す

こういったことをしたい場合には以下のようにします。
少し複雑な気がしますが、ブロックを受け取って下へ下へと渡すだけです。

# 定義
def hoge_method(&block) # ブロックを引数にとるメソッドを呼び出すメソッド
  fuga_method(&block)   # ブロックを渡す
end

def fuga_method(&block) # ブロックを引数にとるメソッド
  result = 'hello'
  result << ' world'
  block.call(result)
end

# 呼び出し
hoge_method do |message|
  puts message
end
=> hello world

この場合には yield を使った方法では記述できないと思います。
( hoge_method が受け取ったブロックを fuga_method の呼び出し時に渡すことを明示する必要があるため。 )

間違ってたらごめんなさい。

ちなみに、 &block& がブロックを Proc オブジェクトに変換しているようなので、 fuga_method に Proc オブジェクトを渡すようにしても書けます。

Proc オブジェクトに変換されていることの確認

irb(main):001:0> def hoge_method(&block)
irb(main):002:1>   puts block.class
irb(main):003:1> end
=> :hoge_method
irb(main):004:0> hoge_method {}
Proc
=> nil

Proc オブジェクトを渡すようにする

# 定義
def hoge_method(&block) # Proc オブジェクトを引数にとるメソッドを呼び出すメソッド
  fuga_method(block)    # Proc オブジェクトを渡す
end

def fuga_method(block)  # Proc オブジェクトを引数にとるメソッド
  message = 'hello'
  message << ' world'
  block.call(message)
end

# 呼び出し
hoge_method do |message|
  puts message
end
=> hello world