2

I found this example of a code block in Coppers book "Beginning Ruby". This should be an example of a custom method for handling code blocks?

  def each_vowel(&code_block)
    %w{a e i o u}.each { |vowel| code_block.call(vowel) }
  end

  each_vowel { |vowel| puts vowel }

I just can't see how this works. Is he sending a code block into another code block?

Something about it just doesn't feel right. I get that each gets the specific items, one at a time, from the array and put it into the vowel variable, but what happens next?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Tomislav Mikulin
  • 5,306
  • 4
  • 23
  • 36

2 Answers2

2

%w{a e i o u} is the ruby syntax of an array of words. It is equivalent to ['a', 'e', 'i', 'o', 'u'].

So the code above can be written as:

  def each_vowel(&code_block)
    ['a', 'e', 'i', 'o', 'u'].each { |vowel| code_block.call(vowel) }
  end

  each_vowel { |vowel| puts vowel }

So what this code does is for every element in the array (.each) it calls the block (code_block.call) with the element as the parameter.

.each itself accepts a block (in this case it is { |vowel| code_block.call(vowel) }) and calls it for each element in the array.

The line each_vowel { |vowel| puts vowel } calls the method defined earlier with the block { |vowel| puts vowel } as the input parameter. It might be more familiar with parentheses:

  each_vowel() { |vowel| puts vowel }

But in ruby parentheses are optional, especially when a method does not expect parameters (a block is not counted as a parameter).

Community
  • 1
  • 1
Uri Agassi
  • 36,848
  • 14
  • 76
  • 93
  • I get that, but I don't see whats going on in this section { |vowel| code_block.call(vowel) } – Tomislav Mikulin Nov 26 '14 at 18:55
  • @user3867776 - I've added some more explanations. If you find something still not clear, please point me to where it all breaks down for you. – Uri Agassi Nov 26 '14 at 19:00
  • `each { |vowel| ... }` is very similar to JavaScript with `forEach(function(vowel) { ... })`. Just think of it as an inline function with a slightly different way of defining it. – tadman Nov 26 '14 at 19:25
  • 1
    OP, imagine that you are `code_block` with the nickname, "Cap", because the only thing you know how to do is capitalize a string. People *call* out things like, "Hey, Cap, what do I get with 'e'?". You do your thing and yell back, "That would be 'E'". In the background you hear someone else call out, "Yo, Sizey, what do I get for 'artifact'?", and the reply, "Eight!". – Cary Swoveland Nov 26 '14 at 21:18
2

Yes, you are right that a block is being executed inside another block. The &code_block is a special way of turning a block into an executable proc object. Inside the method definition, code_block is now referencing a proc object which when executed with the call method, essentially runs the code in the block associated with the method call (in this case { |vowel| puts vowel }.

This isn't the only way to execute the associated block though. Another very common way is with the yield keyword. Here the block is executed as soon as the yield keyword is reached.

def each_vowel
  %w{a e i o u}.each { |vowel| yield(vowel) }
end

each_vowel { |vowel| puts vowel }

Notice that in this case, it is unnecessary to use &code_block in the method signature. Yield always has access to the associated block. If you want access to the block in the form of a proc object however, you will need to specify something like &code_block at the end of the parameter list.

Eric Mathison
  • 1,210
  • 10
  • 16
  • So in the { |vowel| yield(vowel) } we pass vowel as a parameter to code block. So it will puts a,e,i,o,u – Tomislav Mikulin Nov 27 '14 at 11:05
  • 1
    Yes, that's exactly right. Whatever we pass into yield will be passed as the argument for the `{ |vowel| puts vowel }` block. Since `yield(vowel)` is called five times (once for each vowel), `{ |vowel puts vowel}` is called five times as well, each time passing in another letter to the `vowel` parameter. In your example, `code_block.call(vowel)` does exactly the same thing. – Eric Mathison Nov 29 '14 at 00:33
  • No problem. You had a fun example. If one of the posted answers satisfies your question, it'd be awesome if you could accept it. – Eric Mathison Nov 29 '14 at 16:45
  • Sorry, forgot about that,I just clicked it :) – Tomislav Mikulin Nov 30 '14 at 07:54