Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you use Ruby blocks to conditionally execute something?

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!

like image 734
Steve Johnson Avatar asked May 02 '11 05:05

Steve Johnson


People also ask

How do you pass a block to a function in Ruby?

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.

What is a block argument in Ruby?

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.

What is do in Ruby?

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.


3 Answers

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?

like image 128
Amadan Avatar answered Nov 15 '22 06:11

Amadan


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".

like image 42
Vlad Khomich Avatar answered Nov 15 '22 06:11

Vlad Khomich


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?

like image 41
RyanScottLewis Avatar answered Nov 15 '22 07:11

RyanScottLewis