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?
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?
It's quite possible that someone will provide a truly detailed answer here, but I've always found this post from Robert Sosinski to be a great explanation of the subtleties between blocks, procs & lambdas.
I should add that I believe the post I'm linking to is specific to ruby 1.8. Some things have changed in ruby 1.9, such as block variables being local to the block. In 1.8, you'd get something like the following:
Whereas 1.9 would give you:
I don't have 1.9 on this machine so the above might have an error in it.
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 ado_with_name
method that when invoked, would just pass thename
attribute, to the block received.This would allow us to call that method and pass an arbitrary code block.
For instance, to print the name we would do:
Would print:
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 itname
). When the code invokesyield
it fills this parameter with the value of@name
.We could provide another block to perform a different action. For example, reverse the name:
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:
Or, we can also provide a custom sort algorithm, for instance based on the string size:
I hope this helps you to understand it better.
BTW, if the block is optional you should call it like:
If is not optional, just invoke it.
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:Or, using the special block argument syntax:
I wanted to sort of add why you would do things that way to the already great answers.
No idea what language you are coming from, but assuming it is a static language, this sort of thing will look familiar. This is how you read a file in java
Ignoring the whole stream chaining thing, The idea is this
This is how you do it in ruby
Wildly different. Breaking this one down
Here, instead of handling step one and two, you basically delegate that off into another class. As you can see, that dramatically brings down the amount of code you have to write, which makes things easier to read, and reduces the chances of things like memory leaks, or file locks not getting cleared.
Now, its not like you can't do something similar in java, in fact, people have been doing it for decades now. It's called the Strategy pattern. The difference is that without blocks, for something simple like the file example, strategy becomes overkill due to the amount of classes and methods you need to write. With blocks, it is such a simple and elegant way of doing it, that it doesn't make any sense NOT to structure your code that way.
This isn't the only way blocks are used, but the others (like the Builder pattern, which you can see in the form_for api in rails) are similar enough that it should be obvious whats going on once you wrap your head around this. When you see blocks, its usually safe to assume that the method call is what you want to do, and the block is describing how you want to do it.