Ruby 2.5 で Object#yield_self
というメソッドが追加されます。
https://github.com/ruby/ruby/blob/v2_5_0_preview1/NEWS
https://bugs.ruby-lang.org/issues/6721
定義は簡単で、受け取ったブロックの引数に自身を渡して、そのブロックを評価した結果を返します。
class Object
def yield_self
yield(self)
end
end
簡単な例
"foo".yield_self{|s| s + s}
# => "foofoo"
tap
に似ていますが、tap
は自身を返すのに対し、yield_self
はブロックを評価した結果を返します。
"foo".tap{|s| puts s + s}
foofoo
# => "foo"
今まで yield_self
と同じことをするには tap
の中で break
する必要がありました。
"foo".tap{|s| break s + s}
# => "foofoo"
もっと実際的なケース
https://mlomnicki.com/yield-self-in-ruby-25/ を参考にしました。
GitHub の API にアクセスして、Rails リポジトリのスターの数を調べます。
require 'uri'
require 'net/http'
require 'json'
今までだったら、メソッドを入れ子にして書いたり
JSON.parse(Net::HTTP.get(URI.parse("https://api.github.com/repos/rails/rails"))).fetch("stargazers_count")
中間変数に入れたりしていたケースを
url = URI.parse("https://api.github.com/repos/rails/rails")
res = Net::HTTP.get(url)
data = JSON.parse(res)
data.fetch("stargazers_count")
yield_self
を使うとメソッドチェーンで繋いで書いていくことができます。
"https://api.github.com/repos/rails/rails"
.yield_self { |s| URI.parse(s) }
.yield_self { |url| Net::HTTP.get(url) }
.yield_self { |res| JSON.parse(res) }
.fetch("stargazers_count")
ブロックなら中間変数と違って名前空間を汚すこともありません。
&記法でも書くことができますが、これは反ってわかりにくいかなと思います。
"https://api.github.com/repos/rails/rails"
.yield_self(&URI.method(:parse))
.yield_self(&Net::HTTP.method(:get))
.yield_self(&JSON.method(:parse))
.fetch("stargazers_count")
名前について
yield_self
は長いなと思いました...。
元のissue を見ると、
みたいに言われたりしていて、他にも
apply, transform, alter, mutate, map, morph, cast
などの名前が候補として挙がっていましたが、結局は最初に提案された yield_self
になったみたいですね。issue の議論は途中で途切れていて、最終的に決まった経緯はわかりませんでした。
簡単ですが、以上です!