Understanding Ruby blocks and the yield keyword

Letโ€™s start by looking at a standard method definition:

def hello_world!
  puts "hello world!!!!!"
  "๐ŸŒŽ"
end

This looks like this under the hood:

define_singleton_method :hello_world! do
  puts "hello world!!!!!"
  "๐ŸŒŽ"
end

Letโ€™s write our own fancy define_method method. Note that because weโ€™re in IRB, we need to define on main, so we use define_singleton_method:

def our_defined_method(name, &block)
  define_singleton_method name do
    block.call(name)
  end
end

Where we yield our explicit block which is passed as a Proc (&).

our_defined_method(:hello_universe) do
  puts "hello unverise!"
  "๐ŸŒŒ"
end
> hello_universe
# hello unverise!"
  "๐ŸŒŒ"

We can convert our method to a Proc:

its_really_something = method(:hello_universe)
its_really_something.class # Method
its_really_something.call

# hello universe!
# => ๐ŸŒŒ

Now that weโ€™ve seen blocks, what do we do with them?

def blink
  yield
  1
end

blink { puts "I blinked." }

# I blinked
# => 1

blink do
  puts "I blinked."
end

What about twice?

def blink_twice
  yield
  yield
  2
end

blink { puts "I blinked" }

or,

def blink_times(num)
  num.times do
    yield
  end
  num
end

What if we want to send something to our block? We can yield it.

def vanilla_ice
  yield "stop"
end

vanilla_ice { |msg| puts "#{msg} collaborate and listen "}

What if we want to capture it?

def favorite_string_method
  fav = yield
  puts "your favorite map method is #{fav}"
end

favorite_string_method { :to_s }

what if we want to apply our favorite string method?

def apply_favorite_method_to_array(array)
  favorite_method = yield
  array.map(&favorite_method)
end

apply_favorite_method_to_array([1,2,3]) { :to_s } # => ["1", "2", "3"]

or maybe even return our original yield into another yield:

def changed_my_mind
  @favorite = yield
  puts "My @favorite map method is #{@favorite}"
  return yield @favorite
end

changed_my_mind { "thing" }.then { |fav| puts "Awe yeah it's #{fav} "}