Jimako’s Blog

Looking forward, aware of the past…

Ruby

I’m going to get off the topic of the Apple for today — not that nothing has happened, but because in reading over the blog I sound like some Mac fanatic. Today, Chris, a good friend of mine, showed me his new HP laptop. Huge, 17″ monster, very powerful, but battery life of about an hour, and he couldn’t get it set up to access the network. Sigh!

But I said, no Apple today.

Over the past couple of weeks, I have been working on a particular Java application, and I needed to extract a whole bunch of data into flat files for a particular client requirement. Cutting a long story short, I ended up writing a set of scripts to generate an XML specification of an extract that is going to be used to control the total extract process, and this gave me a chance to try my hand at Ruby.

Now, I have heard a lot of good things about Ruby, but had not really used it before. Everyone I knew, who I respected as a programmer, and who had tried Ruby, raved about it. So, even though I knew Python, I made a point of nutting my way round Ruby.

Obviously, it took me a little while to get moving — there is always a bit to learn when starting with a new language. But I bought a PDF copy of the Pickaxe book and zoomed through the highlights. I have to say, I like Ruby a LOT.

Ruby is OO to the core. Everything is an object, and it has a remarkably convenient set of built-in functionality. I am not going to put together a tutorial on Ruby, at least not here, but here are a few examples to whet your appetite.

In Java, to define a class with a set of accessor methods, you do something like this:

public class Dog
private String name;
public Dog(String name)
{
super();
this.name = name;
}
public String getName()
{
return name;
}
public void setName(String value)
{
name = value;
}
}
Here’s the same thing in Ruby:

class Dog
attr_accessor :name
def initialize(name)
@name = name
end
end
Creating an instance in Java:

Dog dog = new Dog("Rover");

and in Ruby:

dog = Dog.new("Rover")

so the classes a pretty much equivalent, except that the Ruby one is (a) much shorter and (b) eliminates the need to write a whole lot of plumbing, no-brain code. Now I know that any modern IDE generates this boilerplate code for you, but it is still there and needs to be navigated and mentally discounted while you work on the stuff that DOES matter. In Ruby, the only code you write is what you need for the application — well, most of the time anyway :)

Here’s a really cool thing you can do in Ruby. When you call a function, as well as passing a number of arguments to it, you can also, optionally, attach a code block to it. A code block is delimited by either a the keywords do and end, or braces (they’re the same). Inside the called function, the code can determine whether a code block has been attached to it and, if so, essentially call that block any number of times. Here is an example:

def send(message)
if block_given?
yield "connecting"
end
connect(...)
if block_given?
yield "sending"
end
send(message)
if block_given?
yield "sent"
end
disconnect(...)
if block_given?
yield "done"
end
end
This is a dummy, skeletal procedure. We assume that it sends a message somewhere, and there are several steps — connecting, sending and disconnecting.

If you call it like this:

send("Hello world")

it just does its thing. But you can optionally attach a code block like this:

send("Hello world") {|stage| puts "... now #{stage}" }

Let’s look at this line. The braces define a code block — the convention seems to be that short blocks like this use braces, while long, multi-line blocks use do/end. The two vertical bars delineate a parameter list; here, the parameter is called “stage”. The single line inside the code block uses puts to display a string. I’ll get to the string in a moment, but for now just accept that this results in the following printout:

... now connecting
... now sending
... now sent
... now done

The string that is displayed is delimited by double-quote characters, which means that the string is processed by Ruby. One of the effects of this is that the #{x} construct embedded in the string is replaced with the value of the variable x — this works everywhere, not just in these attached code blocks.

This mechanism is used to implement a really simple, generic and pervasive iterator-like mechanism. For example, to allow arrays to be iterated, the Array built-in class implements a method “each” which, you guessed it, takes a code block. So, to iterate over an array, you use this sort of code:

my_array.each {|element| puts element }

The beauty of this is that any object can exhibit this behaviour — just implement an “each” method that expects a code block, and “yield” once for each element your object contains. There is no need to be in any other way related to an array.

Which leads me to the topic of Duck Typing. This is the Ruby philosophy about object typing. While Ruby does implement a single-inheritance object hierarchy model, you can actually use unrelated object polymorphically as long as they implement a common subset of methods. The idea is that if it walks like a duck, and looks like a duck, and quacks like a duck, then it can be treated like a duck. Yes, this is NOT as bullet-proof as a strongly-typed language like Java, but in reality I don’t actually end up assigning a Debit object instance to an Animal object reference very often, and if I do, I will rely on my tests to pick that up. In return, I save myself a lot of unnecessary casting and fiddling in perfectly good code just to tell the compiler what I already know.

Ruby also has mix-ins, called modules. A module is a bit like an interface and a bit like an abstract class. Like an interface, a module aggregates a set of methods — these are included by classes that want to, regardless of their position in the object inheritance hierarchy. But unlike interfaces, modules have code in them too — implemented methods. These methods become part of any class that includes the module, and have access to class methods, exactly as if the code had been copied and pasted into that class. Also like interfaces, a single class can mix in, or include, any number of modules.

Like an abstract class, it implements some code, and through access to non-coded variables and methods, can set up an expectation on the classes that include it, but unlike an abstract class, the class that includes it does NOT need to descend from it (indeed, it can’t do so, because modules are not classes per se).

Very powerful indeed, and I don’t claim to fully appreciate all the implications of how these can be used, but just intuitively it seems to be really useful. And just plain cool.

Anyway, that’s more than enough for one post. Tomorrow is going to be a busy day. Toodles.

No comments

Comments are closed.