So onwards and upwards! It appears that I still have a little work to do to convince you non-believers, so lets try again...
As previously mentioned, Ruby has some features that simply cannot be found in many other languages, let alone PHP. So here I introduce yet another one, and this is a biggie. But, please be warned that this one is a little harder to explain, so please be bear with me as I attempt to enlighten you.
Some of you may have heard of a language feature known in computer science as closures. If you have done any Javascript development, you should be familiar with closures, but may not know them by name. Ruby uses closures extensively, but refers to them simply as blocks. Which is actually easier to understand.
Many developers seem to think that blocks are somewhat of an obscure language feature, as they are pretty hard to find an equivalent in other languages. PHP is not the only language without support for blocks, but you should understand them, as they can be very useful, and quite powerful.
Although there is no direct translation of blocks in PHP, we can make some analogies to PHP that will help us understand and become familiar with blocks, and how they are useful. Lets use PHP to start a family:
function start_family($num) {
echo "There are now $num kids in my family"
}
for ($kids = 99; $kids > 0; $kids--) {
start_family($kids);
}
When the above code is executed, PHP evaluates the condition in the for loop, which calls the start_family function on each iteration.
The for and function keywords are built-in constructs of PHP, in the same way that semi-colons and curly braces are built-in. But there are very few control constructs in PHP that allow code to be attached to them within the curly braces. It's not actually possible to add or create new control constructs in PHP, unless of course you fancy whipping up a PHP extension in C. (eurghh!)
To be fair, we could condense the above into the following, as there really would be no need to use a function within the for loop.
for ($num = 99; $num > 0; $num--) {
echo "There are now $num kids in my family"
}
So now lets do the same thing in Ruby:
99.downto(1) { |num| puts "There are now #{$num} kids in my family" }
Slightly unfamilar syntax, but that's nothing new when comparing Ruby with PHP. But the above is actually quite declarative. Lets break this down a little.
Hopefully, if you read reason #1 you will now know that 99 is an object, which has a method called downto. It's pretty obvious that this method simply counts down from 99 to 1, and iterates through each number. It serves roughly the same purpose as the for construct in our PHP example. The odd looking bit between the curly braces, with the goalposts, is the block.
There are many similarities between the Ruby block and the PHP for loop. This is because the Ruby block is actually just a function in another form. A block is a function without a name, or an anonymous function. Yes I know PHP 5.3 will include some sort of support for anonymous functions, but again, it's an afterthought.
A Ruby block can receive parameters in much the same way a method or PHP function can, but they are placed within the goalposts of the block. In our case, we place the number (num) of kids between the goal posts, and this becomes our one and only parameter. We can pass more parameters to the block, by simply separating them with a comma. If we don't want to pass any parameters, then we leave off the goalposts completely. The rest of the block is executable code, just like any other Ruby method.
Every method in Ruby, whether it be a built-in one, or one that you create, can be passed a block. And that one simple fact is what makes blocks so powerful. In fact, if the downto method were written in pure ruby, it could look something like this:
class Integer
def downto(value, &block)
n = self
while n >= value
block.call n
n -= 1
end
return self
end
end
So we reopen the Integer class, and redeclare the downto method. We then pass two parameters to that method. Pay particular attention to the second parameter: &block, as it is very important.
When a block is passed to a Ruby method, it can be captured in a variable. In fact, the code that we pass within the block to downto is converted into an object, and then stored inside a variable. The ampersand &block tells Ruby to store this block in a variable called block. The keyword self in this example refers to the object itself, which in this case is the integer 99.
Still with me? Good! Lets take our earlier example again, and apply it to the downto method that we just created:
99.downto(1) do |num|
puts "There are now #{$num} kids in my family"
end
Hold a second, the above code is different to our first example isn't it? Yes it is, well spotted! Just as in everything in Ruby, you don't need the curly braces. The use of the do keyword is usually used for larger blocks, that need to span multiple lines. But it does exactly the same thing.
When the downto method is called, it receives two parameters: value contains the number of kids we have, and block contains the block of code that will be run on each iteration.
The inside of our downto method should look familiar to you, as it is effectively doing the same thing as our PHP for loop. It just counts down from 99 to 1, using a while loop. Then on each iteration of the while loop, it yields control to the code that is passed in the block variable, by calling the block.call method. Any parameters passed to block.call, are passed in the same way to the goalposts in the block.
A little confusing I know, but I really wanted to explain how blocks work as well as I could, as they are becoming ever more useful to me, the more that I develop with Ruby.
To finish off, I really want to show you an awesome way that Ruby's blocks are used. I won't go into it too much though, as it leads on to another of my reasons. There is a Ruby library (Gem) called Shoulda which improves the Test:Unit unit testing library that is supported as standard in Ruby on Rails. Shoulda improves the language of your unit tests, and turns them into an almost human readable form. So here we have a test:
context "a User instance" do
should "return its full name"
assert_equal 'John Doe', user.full_name
end
end
Now start from the beginning of that code and just read it out load. It may end up something like this "a User instance should return its full name". Of course there is more in between, but what I am trying to convey is how expressive and readable the above code is. And that is because of the power of blocks.
Hope that helps a little. Keep the comments coming guys, and thanks for reading.
