Object#yield_selfが導入されます。tapと違ってyieldの戻り値がそのままyield_selfの戻り値となっています。

class Object
  def yield_self(*args)
    yield(self, *args)
  end
end

引用:https://bugs.ruby-lang.org/issues/6721

class Kernel
  def tap
    yield self
    self
  end
end

引用:Rubinius

これを見たときになんとなくパイプライン演算子ぽいなと思いました。Elixirでやるとこんな感じです。

IO.puts "I love Elixir"
        |> String.upcase
        |> String.reverse

# RIXILE EVOL I と表示されます

もし、Ruby2.5になったらこんな感じにかけます。

class Object
  def yield_self(*args)
    yield(self, *args)
  end
end

puts "I love Ruby"
     .yield_self(&:upcase)
     .yield_self(&:reverse)

# YBUR EVOL I と表示されます。

Elixirはファンクションの第一引数に次々に値をセットしている一方で、Rubyはyieldの戻り値をチェーンしています。プログラミングの幅が広がればよいですが、いまのところベストプラクティスは思いつきません:sweat_smile:と思っていたのですが、頂いたコメントからヒントを得ることができました:tada:

tapではbreakを利用して戻り値に無理やりしていたことを自然に書くことが出来ます。文字列や整数の結果をチェーンするのを思いついたのですが、ステップごとに異なるクラスのインスタンスに変化していくときに活躍しそうです!(Elixirでの例とおんなじですね:smile:

require 'net/http'

class Object
  def yield_self(*args)
    yield(self, *args)
  end
end

uri = 'https://www.libertyfish.co.jp/'
puts uri.yield_self(&URI.method(:parse))
        .yield_self(&Net::HTTP.method(:get_response))
        .code
# ステータスの200が表示される

蛇足ですが、tapと似たようなものだと思ってyield_selfに渡すブロックで破壊的メソッドを使った場合は思ってたのと違う!というのが起こりそうですね。

class Object
  def yield_self(*args)
    yield(self, *args)
  end
end

puts "HELLO"
     .yield_self{|greet| greet.upcase!}

# すでに大文字であれば、`upcase!`の戻り値は`nil`

puts "HELLO"
     .yield_self{|greet| greet.upcase}

# すでに大文字であっても、"HELLO"

puts "HELLO"
     .tap{|greet| greet.upcase!}

# すでに大文字であっても、"HELLO"

puts "HELLO"
     .tap{|greet| greet.upcase}

# すでに大文字であっても、"HELLO"
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.