I recently purchased the book Seven Languages in Seven Weeks and have been reading through the chapter on Ruby. In the section which introduces blocks (page 40), a code sample is given which illustrates the use of blocks for the purpose of conditionally executing something:
in_case_of_emergency do
use_credit_card
panic
end
def in_case_of_emergency
yield if emergency?
end
This code doesn't make much sense to me, and the book doesn't provide much of an explanation. I was wondering if one of you Ruby gurus would mind helping me get my head around this one.
How can you have both a block and a function with the same name? How would you define "emergency?" I can't even create the block in IRB without it complaining:
NoMethodError: undefined method `in_case_of_emergency' for main:Object
from (irb):1
from :0
And how would you invoke this code to demonstrate how it works? Thanks!
Blocks are used extensively in Ruby for passing bits of code to functions. By using the yield keyword, a block can be implicitly passed without having to convert it to a proc. When using parameters prefixed with ampersands, passing a block to a method results in a proc in the method's context.
A ruby block is one or more lines of code that you put inside the do and end keywords (or { and } for inline blocks). It allows you to group code into a standalone unit that you can use as a method argument.
There are two ways of defining a block in Ruby: The first is using the do.. end keyword, the other is using a pair of curly braces. Do.. end block is mainly used when defining a block of code that spans multiple lines, while curly braces {} are used when defining a block of code that spans a single line.
First off: the two are in the wrong order. You need to define in_case_of_emergency
first.
Second: You don't name blocks; therefore, it is incorrect that there are two things named in_case_of_emergency
. One is a function definition, while the second is the function invocation of that same function.
So, step-by-step:
def emergency?
return rand(2) == 0
end
Let's say you have this function that returns true
half the time, and false
half the time, randomly. (Boy, that's a lot of emergencies!) Then:
def in_case_of_emergency
yield if emergency?
end
This defines a function named in_case_of_emergency
. When called, it executes yield if emergency?
, which is a statement yield
modified by the conditional if
. This is syntactic sugar for
if emergency?()
yield
end
Note that Ruby does not require brackets for function invocation, thus we can drop them; and if there is only one statement inside an if
, you can write it on the same line as above (obviating the need for an end
).
Next, we have a function invocation:
in_case_of_emergency do
use_credit_card
panic
end
This calls the function we just defined, in_case_of_emergency
, passing it a block to execute. These are the two statements (use_credit_card
, panic
) that would be executed by yield
- but only if the emergency?
evaluates to true
.
Does this make more sense now?
that's simple, that is the method:
def in_case_of_emergency
yield if emergency?
end
and that is the call to your method:
in_case_of_emergency do
use_credit_card
panic
end
wheres
do
use_credit_card
panic
end
is an argument. Ruby methods can implicitly take blocks as arguments. What happens is that
yield
will execute the block you provided. In your case yield if emergency?
means "execute provided block if conditions are met".
Adding to other answers, it may be easier to understand blocks if you drop the yield
keyword and treat blocks in methods like the Procs they actually are.
# for testing purposes, let's always have `emergency?` return `true`
def emergency?
true
end
def in_case_of_emergency(&block)
block.call if emergency?
end
in_case_of_emergency do
puts "AHH! Emergency! Help!", "Seriously, I'm freaking out!"
end
See how much easier it is to realize that the block is actually an argument?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With