Well now then. My last post, or the comments in it, seemed to have stirred up a little hornets nest. I had my fair share of supporters and my fair share of thanks for my writings, but I also received a little criticism. I think I've covered most of those in the comments and also on Twitter, so I won't say much more, other than to simply remind you all what I am trying to do.
In this little series of blog posts, my objective was to be as fair as possible when explaining my reasons why I think Ruby is better than PHP. But at the same time, this is not a comparison. Which means I won't be pointing out PHP's strong points - of which there are many. Also, this is my opinion, and no one elses, and I am entitled to that. And so to, are you guys.
And one last thing before I start reason #2. I want to say thanks to Chris Hartjes for his blog post earlier today. I made a comment on Twitter about lazy coders, being good coders, and he basically put me to rights, and explained what I was saying in much better terms, than I could ever do. But also, I do think he was a little harsh on me. Hear my other reasons first Chris. No hard feelings though.
Back to business. In todays post, I wanted to give more of a solid reason why I think Ruby excels over PHP. So I picked one that simply cannot be matched in any way by PHP. Add probably won't ever be. But I would love to see this being made possible.
Re-opening in Classes is soooo Useful!
Both languages support the creation of classes as part of their object-oriented design. And they do so in much the same way. Both languages allow you to set method visibility with the use of public, protected and private declarations, and behave accordingly. Classes are written in a similar fashion:
class Person {
public function greeting() {
return 'Hello you!';
}
}
class Person
def greeting
'Hello you!'
end
end
Very similar, but let me explain one thing. In the PHP example above, I returned the string "Hello you!" by prefixing it with the return keyword. Like this:
return 'Hello you!';
But in the Ruby example, you may have noticed that there is no return keyword before the "Hello you!" string. It was just this:
'Hello you!'
That works perfectly fine, and the "Hello you!" string will still be returned when calling the greeting method of the Person class. That is because Ruby will return the value from the last line of the method. You can use return if you want to, but you don't have to.
So we've determined that writing classes is pretty much on the par in both PHP and Ruby. But, you may have accidentally redeclared a class in PHP, and received a nasty error:
class Person {
public function greeting() {
return 'Hello you!';
}
}
class Person {
public function greeting() {
return 'Hey, hey, hey!';
}
}
Doing the above will give tell you PHP Fatal error: Cannot redeclare class Person. Everyone knows that you just cannot do that with PHP. And to be honest it seems to make sense for it to prevent this from happening. But Ruby actually allows you to do this. In fact, you are almost encouraged to reopen classes if and when needed.
class Person
def greeting
'Hello you!'
end
end
class Person
def greeting
'Hey, hey, hey!'
end
end
In the above case, no error will be returned. Instead, Ruby will simply redefine the greeting method in the first class, with the same method from the second class. So you get this:
p = Person.new
puts p.greeting
Hey, hey, hey!" and not "Hello you!"
Oh no, here it comes. I can hear the cries now: "But that's gonna cause untold problems and bugs in my code!". Not if you use it wisely! Part of Ruby's philosophy is to provide you with all the tools you need, then entrusting you to use that power with care. What was it Uncle Ben said to Peter? "With great power comes great responsibility."
This type of feature may seem a little dodgy, but in practice it becomes extremely useful. It means that I can reopen any class that I want, at any time, which often leads to more manageable code.
But I'm not just limited to reopening my own classes. I can even reopen Ruby's base classes. This is referred to as "Monkey patching", and is usually frowned upon, as it is not very wise to be overriding the core of the language. But if used carefully and sparingly, we are able to add a little extra functionality to our app by extending a base class or two.
When it comes to Rails, Ruby's base classes are extended quite a bit, especially within its ActiveSupport library, which reopens the String, Integer, and many other base classes. But it does so to add a lot of extra, and very useful functionality. It's done so with care and attention.
For example, I want to know if an integer is odd or even, but Ruby provides no such method for doing so, so I write my own. But instead of writing a standalone function within my class, I can reopen Ruby's Integer class like so:
class Integer
def odd?
self % 2 == 1
end
end
3.odd?
which would of course return true. All I am doing is reopening the Integer class, and adding a new method.
"But what's that question mark doing there? Is that part of the method name?" Actually, yes it is. In PHP, you can only use letters, number, and underscores in method and function names. But Ruby lets be a little more expressive, and use other characters such as ?, !, and even /. So in the odd example above, I ended the method name with a question mark, because what I am doing is effectively asking a question of the number. "Are you an odd integer?". Which it replies with a true or false value. Neat hey?
One of the most powerful uses of reopening classes, is the plugin system available in Rails. We can use Rails plugins to extend and override nearly every part of Rails. Which is why there are thousands of Rails plugins. You can do pretty much anything you want.
So I hope I have provided a good, solid reason why Ruby is better than PHP, with a feature that simply cannot be achieved with PHP. It really is very powerful, that when used carefully, can save you a lot of time, and make coding with Ruby lots of fun. Which after all, that's why we're here isn't it?!
Next time, I think I'll talk about Blocks; yet another feature of Ruby that PHP cannot emulate.
