Search code examples
rubyfunctionblockyield

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!


Solution

  • 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?