Ruby blocks in a few minutes - Josh Brody Ruby blocks in a few minutes | Josh Brody
Back

Ruby blocks in a few minutes

Here’s the thing about blocks in Ruby: they’re just chunks of code you pass around. The yield keyword is how you run them. That’s it. Everything else is details.

Let’s start with a standard method definition:

123456
def hello_world!
  puts "hello world!!!!!"
  "🌎"
end

hello_world!
loads ~35MB Ruby environment
Error: invalid byte sequence in US-ASCII

This looks like this under the hood:

1234
define_singleton_method :hello_world! do
  puts "hello world!!!!!"
  "🌎"
end
loads ~35MB Ruby environment
Error: invalid byte sequence in US-ASCII

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:

12345
def our_defined_method(name, &block)
  define_singleton_method name do
    block.call(name)
  end
end
loads ~35MB Ruby environment

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?

123456
def blink
  yield
  1
end

blink { puts "I blinked." }
loads ~35MB Ruby environment
I blinked.

What about twice?

1234567
def blink_twice
  yield
  yield
  2
end

blink_twice { puts "I blinked" }
loads ~35MB Ruby environment
I blinked
I blinked

or,

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

blink_times(3) { puts "I blinked" }
loads ~35MB Ruby environment
I blinked
I blinked
I blinked

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

12345
def vanilla_ice
  yield "stop"
end

vanilla_ice { |msg| puts "#{msg} collaborate and listen" }
loads ~35MB Ruby environment
stop collaborate and listen

What if we want to capture it?

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

favorite_string_method { :to_s }
loads ~35MB Ruby environment
your favorite map method is to_s

what if we want to apply our favorite string method?

123456
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 }
loads ~35MB Ruby environment

or maybe even return our original yield into another yield:

1234567
def changed_my_mind
  @favorite = yield
  puts "my favorite map method is #{@favorite}"
  yield @favorite
end

changed_my_mind { "thing" }.then { |fav| puts "awe yeah it's #{fav}" }
loads ~35MB Ruby environment
my favorite map method is thing
awe yeah it's thing

Stay in the loop

Occasional essays on design, tools, and the craft of building things. No spam, unsubscribe anytime.

Ambient weather

The background of this site reflects the current weather and time of day in Saint Paul. The orbs shift in color and behavior based on what's happening outside my window.

Learn more about how this works