The difference between tap and yield_self is in what is returned by each of the two methods.
Object#tap yields self to the block and then returns self. Kernel#yield_self yields self to the block and then returns the result of the block.
Here are some examples of where each can be useful:
tap
Replacing the need for a result line at the end of a method:
def my_method
result = get_some_result
call_some_method_with result
result
end
can be written as:
def my_method
get_some_result.tap do |result|
call_some_method_with result
end
end
Another example is initialisation of some object that takes several steps:
some_object = SomeClass.new.tap do |obj|
obj.field1 = some_value
obj.field2 = other_value
end
yield_self and yield(self)
If used inside one of your own methods yield_self would have the same effect as yield(self). However, by having it as a method in its own right this promotes method chaining as an alternative to nested function calls.
This blog post by Michał Łomnicki has some helpful examples. e.g. this code:
CSV.parse(File.read(File.expand_path("data.csv"), __dir__))
.map { |row| row[1].to_i }
.sum
can be rewritten as:
"data.csv"
.yield_self { |name| File.expand_path(name, __dir__) }
.yield_self { |path| File.read(path) }
.yield_self { |body| CSV.parse(body) }
.map { |row| row[1].to_i }
.sum
This can aid with clarity where nested calls are being used for a series of transformations on some data. Similar features exist in other programming languages. The pipe operator in Elixir is a good one to take a look at.
then
#yield_self might feel a bit technical and wordy. That’s why Ruby 2.6 introduced an alias for #yield_self, #then.
"something"
.then {|str| str.chars.map {|x| x.ord + 1 }}
.then {|ords| ords.map {|x| x.chr }}
.then {|chars| chars.join }
.then {|str| str + "!" }
.then {|str| str + "!" }
# tpnfuijoh!!