Trace Trace - 5 months ago 15
Ruby Question

Blocks and yields in Ruby

I am trying to understand blocks and

and how they work in Ruby.

How is
used? Many of the Rails applications I've looked at use
in a weird way.

Can someone explain to me or show me where to go to understand them?


Yes, it is a bit puzzling at first.

In Ruby, methods may receive a code block in order to perform arbitrary segments of code.

When a method expects a block, it invokes it by calling the yield function.

This is very handy, for instance, to iterate over a list or to provide a custom algorithm.

Take the following example:

I'm going to define a Person class initialized with a name, and provide a do_with_name method that when invoked, would just pass the name attribute, to the block received.

class Person 
    def initialize( name ) 
         @name = name

    def do_with_name 
        yield( @name ) 

This would allow us to call that method and pass an arbitrary code block.

For instance, to print the name we would do:

person ="Oscar")

#invoking the method passing a block
person.do_with_name do |name|
    puts "Hey, his name is #{name}"

Would print:

Hey, his name is Oscar

Notice, the block receives, as a parameter, a variable called name (N.B. you can call this variable anything you like, but it makes sense to call it name). When the code invokes yield it fills this parameter with the value of @name.

yield( @name )

We could provide another block to perform a different action. For example, reverse the name:

#variable to hold the name reversed
reversed_name = ""

#invoke the method passing a different block
person.do_with_name do |name| 
    reversed_name = name.reverse

puts reversed_name

=> "racsO"

We used exactly the same method (do_with_name) - it is just a different block.

This example is trivial. More interesting usages are to filter all the elements in an array:

 days = ["monday", "tuesday", "wednesday", "thursday", "friday"]  

 # select those which start with 't' do | item |
     item.match /^t/

=> ["tuesday", "thursday"]

Or, we can also provide a custom sort algorithm, for instance based on the string size:

 days.sort do |x,y|
    x.size <=> y.size

=> ["monday", "friday", "tuesday", "thursday", "wednesday"]

I hope this helps you to understand it better.

BTW, if the block is optional you should call it like:

yield(value) if block_given?

If is not optional, just invoke it.