Saturday 8 September 2007

Ruby Surprises - Back to the Beginning with the PickAxe

I've recently bought the famous PickAxe book. The plan? To go back over all I know about Ruby from a different perspective to make sure I understand all the subtleties of the language. Here are some of the surprises so far for me, someone coming from Java land.

Surprise 1 - Classes are Always Open

In Ruby, classes are always open. This means that when you want to add a new method to them, you simply open them (with a new "class ... end") and add the code you require:

Here we've defined a new class "Song" and given it an initializer. Later (just below in fact) we've realised we need to add a "to_s" method. This is simple to do, simply by re opening the definition of Song. Ruby now sees the Song class as being a combination of all that we've defined for it. Therefore, when we run our code we get the following result:

NOTE: You can redefine any class you want, not just ones you've created yourself. This scares some hardcore Java programmers.

Surprise 2 - To Redefine a Subclass, No Need to Restate the Extension

Here we're going to do exactly the same as before with the "Song" example, but now we'll use a Subclass of Song; "KaraokeSong". As you'd expect, the first time we define KaraokeSong, we need to tell Ruby that it extends Song. However, when we re define it to add a "to_s" method, we can treat it simply as the class that it is. Ruby already knows it extends Song:

(Netbeans) Surprise 3 - Netbeans can't do Code Completion on Already Defined Variables in a Class Re-Definition

I guess this is probably too much to ask, but as I'm trying to learn how to use the Netbeans Ruby Editor at the same time as I learn Ruby, it is worth nothing that code completion for variables doesn't work if you are in a piece of code which is re defining a class which contains them:

On the positive side, it is very cool that code completion works within String literals. Regex completion is a godsend too.

Surprise 4 - Slimline Ruby Encapsulation

Anyone who has nearly gone insane hand-coding Javabean encapsulation code without the refactoring help of an IDE will have railed against the verbosity of the getXxxx and setXxxx paradigm. I was pleasantly surprised therefore to discover the simplicity and expressivity of the Ruby encapsulation code:

This to me makes it look as if the attribute is not encapsulated (which makes client code simpler and easier to read) when in fact it is (which means you get all the good points of encapsulation). Cool.

Surprise 5 - You can Encapsulate Virtual Attributes

Using this Ruby encapsulation idiom, you can encapsulate virtual attributes (i.e. ones which do not exist as actual instance variables):

(Netbeans) Surprise 6 - Class Attributes Must be Initialised Before You Use Them

It's a rule that a Ruby class attribute must be initialised before you use it. Netbeans reinforces this by not providing it as an option in code completion if you forget to do so (Though it would be cool if there was an associated tool tip to remind you to follow the rules):

That's it for now. More to follow as I progress through the book...

3 comments:

Tor Norbye said...

Hi Andrew,
this is Tor Norbye (I work on the Ruby support for NetBeans). I'd like to make sure code completion works in the scenario you found. From just looking at the screenshot it doesn't look like it should work, since I see a complete class definition, not extending a class or including any modules, so it would known anything about a field name @lyrics. But I'm assuming you boiled this down from a larger example where it shoudl have worked but didn't. Can you post it, or file a bug as described in http://wiki.netbeans.org/wiki/view/RubyFeedback ?

Tor Norbye said...

Doh! I was too trigger happy. After posting I reread that part and see that you stated exactly what the problem was. I'll figure out why this isn't working (it should be).

Tor Norbye said...

Ok, this should be fixed now. Get build 3780 or later from http://deadlock.netbeans.org/hudson/job/ruby/
to get the fix. It now recognizes fields properly for redefinitions, inherited from other classes in other files etc.