DEV Community

Kaziu
Kaziu

Posted on

Ruby のブロックと yield を一番簡単な例で

Ruby を書いていると必ず出てくる「ブロック」と「yield」。
難しく見えますが、要は 「処理を渡す」「渡された処理を実行する」 だけです。

ブロックとは

メソッドに渡す「処理のかたまり(コードの切れ端)」 のこと。
{ }do...end で囲みます。

[1, 2, 3].each { |n| puts n }
#              └──────────┘ これがブロック
Enter fullscreen mode Exit fullscreen mode

.each に「各要素で puts n してね」という指示書を渡しています。

1
2
3
Enter fullscreen mode Exit fullscreen mode

{ }do...end は同じもの。長いときは do...end を使うだけです。

[1, 2, 3].each do |n|
  puts n
end
Enter fullscreen mode Exit fullscreen mode

yield とは

メソッドの中の「yield と書いた場所」で、渡されたブロックを実行する命令

def あいさつ
  puts "こんにちは"
  yield              # ← ここで渡されたブロックを実行
  puts "さようなら"
end

あいさつ { puts "元気ですか?" }
Enter fullscreen mode Exit fullscreen mode
こんにちは
元気ですか?    ← yield の場所にブロックが差し込まれる
さようなら
Enter fullscreen mode Exit fullscreen mode

yield は好きな場所に置け、何回でも呼べ、値も渡せます

def 数える
  yield 1
  yield 2
end

数える { |n| puts "#{n}番目" }
# 1番目
# 2番目
Enter fullscreen mode Exit fullscreen mode

2つの関係

  • ブロック = 渡す「やることメモ」(呼ぶ側が書く)
  • yield = そのメモを実行する命令(メソッド側が書く)

差し込む場所を決めるのがメソッド、差し込む中身を決めるのが呼ぶ側。


実践編:yield で処理を「挟み込む」

yield の本当に便利な使い方が、共通の前処理と後始末で、渡された処理を挟むパターンです。

def with_debug
  old = Rails.logger.level
  Rails.logger.level = :debug   # 前処理:ログを debug に
  yield                         # 呼び出し側の処理を実行
ensure # エラーが起きても起きなくても、最後に必ず実行される(=finally)
  Rails.logger.level = old      # 後始末:必ず元に戻す
end
Enter fullscreen mode Exit fullscreen mode

使い方:

with_debug do
  # この中だけログが debug になる
  user.save!
end
# ここを抜けると自動で元のログレベルに戻る
Enter fullscreen mode Exit fullscreen mode

なぜ便利か

  • 共通部分(ログ切替と復元)を1か所にまとめられる。使う側は「何をしたいか」だけ書けばよい。
  • ensure で後始末が保証される。ブロックの中でエラーが起きても、ログレベルは必ず元に戻る。

もし yield を使わなければ、使う場所ごとに「切替 → 処理 → 復元」を毎回コピペする羽目になります。yield なら枠(前後処理)はメソッド側、中身は呼ぶ側、ときれいに分けられます。

もう一つの例:処理時間を測る

同じパターンで「実行時間の計測」も書けます。

def measure(label)
  start = Time.now
  yield
ensure
  puts "#{label}: #{Time.now - start}秒"
end

measure("重い処理") do
  heavy_task
end
# => 重い処理: 1.23秒
Enter fullscreen mode Exit fullscreen mode

「共通の前後処理でブロックを挟む」 ——これは Ruby で頻出の設計パターンです。
File.open { |f| ... }(開いて必ず閉じる)なども、この仲間です。

Top comments (0)