Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blocks and yields in Ruby

Tags:

ruby

block

I am trying to understand blocks and yield and how they work in Ruby.

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

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

like image 575
Matt Elhotiby Avatar asked Jun 18 '10 01:06

Matt Elhotiby


People also ask

What is yield in Ruby?

To accept the parameters we placed a variable(i) between two vertical lines (||). Yield with Return value: It is possible to get the return value of a block by simply assigning the return value of a yield to a variable. if we use yield with an argument, it will pass that argument to the block.

What are blocks in Ruby?

A block is the same thing as a method, but it does not belong to an object. Blocks are called closures in other programming languages. There are some important points about Blocks in Ruby: Block can accept arguments and returns a value. Block does not have their own name.

What are blocks and procs in Ruby?

Blocks are used for passing blocks of code to methods, and procs and lambda's allow storing blocks of code in variables.

Why are blocks useful Ruby?

If you have used each before, then you have used blocks! A Ruby block is useful because it allows you to save a bit of logic (code) & use it later. This could be something like writing data to a file, comparing if one element is equal to another, or even printing an error message.


2 Answers

Yes, it is a bit puzzling at first.

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

When a method expects a block, you can invoke it by calling the yield function.

Example:

Take Person, a class with a name attribute and a do_with_name method. When the method is invoked it will pass the name attribute to the block.

class Person      def initialize( name )           @name = name     end      def do_with_name   # expects a block         yield( @name ) # invoke the block and pass the `@name` attribute     end end 

Now you can invoke this method and pass an arbitrary code block.

person = Person.new("Oscar")  # Invoking the method passing a block to print the value person.do_with_name do |value|     puts "Got: #{value}" end 

Would print:

Got: Oscar 

Notice the block receives as a parameter a variable called value. When the code invokes yield it passes as argument the value of @name.

yield( @name ) 

The same method can be invoked with a different block.

For instance to reverse the name:

reversed_name = ""  # Invoke the method passing a different block person.do_with_name do |value|      reversed_name = value.reverse end  puts reversed_name  => "racsO" 

Other more interesting real life examples:

Filter elements in an array:

 days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]     # Select those which start with 'T'   days.select do | item |      item.match /^T/  end  => ["Tuesday", "Thursday"] 

Or sort by name length:

 days.sort do |x,y|     x.size <=> y.size  end  => ["Monday", "Friday", "Tuesday", "Thursday", "Wednesday"] 

If the block is optional you can use:

yield(value) if block_given? 

If is not optional, just invoke it.

You can try these examples on your computer with irb (Interactive Ruby Shell)

Here are all the examples in a copy/paste ready form:

class Person      def initialize( name )           @name = name     end      def do_with_name   # expects a block         yield( @name ) # invoke the block and pass the `@name` attribute     end end   person = Person.new("Oscar")  # Invoking the method passing a block to print the value person.do_with_name do |value|     puts "Got: #{value}" end   reversed_name = ""  # Invoke the method passing a different block person.do_with_name do |value|      reversed_name = value.reverse end  puts reversed_name    # Filter elements in an array:     days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]    # Select those which start with 'T'  days.select do | item |     item.match /^T/ end    # Sort by name length:      days.sort do |x,y|    x.size <=> y.size end 
like image 93
OscarRyz Avatar answered Oct 20 '22 19:10

OscarRyz


In Ruby, methods can check to see if they were called in such a way that a block was provided in addition to the normal arguments. Typically this is done using the block_given? method but you can also refer to the block as an explicit Proc by prefixing an ampersand (&) before the final argument name.

If a method is invoked with a block then the method can yield control to the block (call the block) with some arguments, if needed. Consider this example method that demonstrates:

def foo(x)   puts "OK: called as foo(#{x.inspect})"   yield("A gift from foo!") if block_given? end  foo(10) # OK: called as foo(10) foo(123) {|y| puts "BLOCK: #{y} How nice =)"} # OK: called as foo(123) # BLOCK: A gift from foo! How nice =) 

Or, using the special block argument syntax:

def bar(x, &block)   puts "OK: called as bar(#{x.inspect})"   block.call("A gift from bar!") if block end  bar(10) # OK: called as bar(10) bar(123) {|y| puts "BLOCK: #{y} How nice =)"} # OK: called as bar(123) # BLOCK: A gift from bar! How nice =) 
like image 21
maerics Avatar answered Oct 20 '22 17:10

maerics