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?
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.
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.
Blocks are used for passing blocks of code to methods, and procs and lambda's allow storing blocks of code in variables.
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.
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
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 =)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With