What is a code block anyway? Well, a code block is a chunk of code… that’s all there is to say about it. I know, this part is not really interesting. What IS interesting though, is that you can pass a block to a ruby function and then have this function call the block whenever it wants. C++ programmers might think these code blocks are the equivalent of function pointers. Well, they maybe are, but there are some key differences. In ruby, you can pass a code block to ANY ruby method, not just the methods that expect a parameter of a certain type. Every ruby methods can receives a block, regardless of the number of parameters they accept. That may sound confusing, but in fact it is just that you don’t pass a code block to a function like if it was a standard parameter (well, you could… but that’s beyond the scope of this article).
Let’s see how it works :
Here is an example of a method who decides to call a block that may have been passed to it by the caller
def my_ordinary_method()
#do stuff
yield #the instruction that calls the block
#do more stuff
end
See how there is no parameter representing the block to call? There’s no parameter of type TBlockCode or anything of the sort. my_ordinary_method is just a regular method with one small exception : That method decided to make use of the “yield” method in it’s implementation to call an eventual block that might have been passed to it by the caller.
Now, let’s see how the caller can pass a code block to my_ordinary_method
def the_caller()
#do stuff
my_ordinary_method() do
puts "I am the block. The one everyone talks about!"
puts "I am gentle, colorful and polite"
end
end
A code block is just a chunk of code between the keyword do and the keyword end. (you can use { and } instead if you want).
You can also pass parameters from the callee (the method that calls the block) to the caller. To achieve this, you have to place those parameters between two pipes (|) and separate them by commas. When you’re on the caller side, you can think of these parameters as containers waiting to be filled in. The method that will execute the block (callee) will fill them by passing the values the “standard way” via the yield method.
Let’s start from the beginning with this in mind :
def my_ordinary_method()
i=6 #6 is a number I like
j=9 #I don't have any feelings towards this number
yield(i,j) #call the block
end
def the_caller()
#do stuff
my_ordinary_method() do |fill_me, fill_me_too|
puts fill_me #will writes 6 on the screen
puts fill_me_too #will writes 9 on the screen
end
end
Code blocks are extremely useful and powerful. They are used a lot by ruby programmers to achieve what we could call “intelligent iterations”. For example, there is a method called times in the Integer class. Well, times is a method that makes use of the yield keyword in it’s implementation. You realize what does this means right? Yep, you can pass a code block to it!
Let’s see how the times method in the class Integer could be implemented (the code is invalid…but it doesn’t matter).
class Integer
#the getter and setter that holds the internal numerical value
#of the Integer instance.
def internal_value
#variables preceded by @ are instance variables
return @internal_value
end
def internal_value=(internal_value)
@internal_value = internal_value
end
#Now, the times method
def times
#regular loop. As you can see, the actual looping is done by
#the callee, NOT by the caller
for i=0 to @internal_value-1
yield
end
end
end
Now, let’s use that times method
6.times {puts "I... can't... believe... this..."}
I know, This kind of stuff impresses me a lot too. No need to play with the index, you just tell ruby that you want to display something on the screen 6 times. Now, that’s the kind of abstraction I love!
Question : how do I know the parameters passed by the yield() in a class that I do not see the source code?
I mean, I have yield(name, password, userid) in a list_users() function of a User class that I do not own the code?
When I call it, how can I know that the parameters are name, password, userid in that order?
Dan, you have to know in advance what are the “requirements” for that method. For example, the Array class exposes a method named “each” that loop through every elements of an array instance. At every passages in the loop, the each method will yield the current item in the collection. You have to know in advance this fact to use the each method properly. I hope it does answer your question.
To everyone else, click on Dan’s link to do some remarkable javascript katas!
Dan:
def list_users(params)
..
yield params
..
end
list_users( :name => name, :password => password, :userid => id ) do |user|
.. whatever .. user[:name] etc.
end
I think Peter understood the question better than me. He’s right. By using a hash, the order of the various parameters doesn’t matter anymore.
I must confess I haven’t really read much of this, I just wanted to throw out an inspiration in case he was stuck 🙂
I guess generally in these situations you would use a collection. At least, that’s how Rails does it. find(:all).each do.. end.. etc. 🙂
The code for the Integer#times implementation could be a bit cleaner. To match how the real Integer class behaves, you’d do something like:
class Integer
def times
# we can give for loops a Range to make it a bit cleaner
for i in 0…self
# just like the default times implementation we yield the current index
# your block doesn’t have to handle this so this will work for
# blocks with any number of arguments
yield i
end
# the default implementation returns a reference to itself after it’s done
self
end
end
How’s that cleaner than self.times do … end ?
That is assuming we wanted to provide a pure ruby implementation of the idea that the times method is based on and not depend on anything defined in the superclass.