A Quick Introduction To Ruby

I wrote a no-nonsense introduction to Ruby for people who already know how to program in some other language. This article does not waste time with programming basics or syntax details which one can learn easily anyway. I hope this gets you started in the beautiful, happy world of Ruby.

Introduction to Ruby

Ruby is an interpreted object-oriented programming language. This means you have classes, objects, instance methods, instance variables, class methods, class variables and so on. Ruby also supports other programming paradigms like functional programming, ad-hoc scripting etc. I will directly jump to features which are unique to Ruby - especially if you are coming from C, C++, Java background.

Everything is an object
Yes, everything is an object. Integers, Floats, Booleans, Strings, Arrays, Hash - everything is an object. Even "Class" and "Module" are objects. You can have code like:

>> 24.to_s + true.to_s
=> "24true"

Classes are open
Classes and Modules in Ruby are not set in stone after loading. Methods can be added, modified or removed from such classes at any point of time in the program execution. For example:

>> s = String.new("hi")
>> puts s.upcase
=> "HI"

#Now let's modify the built-in method dynamically. No restart is needed.


>> class String; def upcase; "abc"; end; end
>> puts s.upcase
=> "abc"

We have modified the method "upcase", which is built in the core Ruby class "String", dynamically. You can do this with any other class. This means that features can be added and removed as you wish. In fact, even the core language features can be improved without ever restarting the program. It gives the developers tremendous flexibility in designing frameworks and applications. If a library has a bug, you can fix it yourself without touching its code base. You can also add features to ruby/library at runtime. 

Syntax is delightful
Ruby does away with a lot of verbal overhead with some syntactic sugar. Consider:


1. Parantheses are optional while calling methods. Instead of 

>> employee.promote("manager")

you can write:

>> employee.promote "manager"

2. Operators like +, -, =, ==, * , /, [] are implemented as regular methods and there is nothing special about them. You can still use them as you are used to. For example:

>> a + b

is equivalent to:

>> a.+(b) # calls the "+" method on object "a" with "b" as the method argument

When you write:

>> obj.name = "hi"

the code that actually gets executed is this:

>> obj.name=("hi")

so "hi" is passed as an argument to a method called "name=" (note the equal sign). 

Encapsulation by default.
Java has a convention of using getter and setter methods like:

String getName() {
  return this.name;
}

and 

void setName(String name) {
  this.name = name;
}

This kind of design is good practice because it hides the internal variable "this.name" from the outside world and protects against misuse. However, the code calling getters and setters is too verbose and painful.

In Ruby, you can do:

def name
  @name
end

def name=(name)
  @name = name
end

And then you can simply use:

>> obj.name
=> "xyz"
>> obj.name = "abc"
>> obj.name
=> "abc"

So you get the best of both worlds. Getters and setters so that data is encapsulated as well as a code syntax which looks like as if you were just using a property variable normally. Instance variables are private by default so encapsulation is enforced.

Still shorter and prevalent way of defining the above two methods is:

attr_accessor :name
# 6 lines of code reduced to one. More importantly, look how readable the code becomes. It's declarative.

We are calling a method "attr_accessor" with a string argument "name". This methods creates the getter and setter methods dynamically which are equivalent to the ones we wrote above. Isn't metaprogramming awesome? Code that writes code for us. :) Note that attr_accessor is a regular method and not a keyword. This means that full power of ruby is available to you even when you are defining classes. No need to worry about compile-time vs runtime. Everything is runtime (almost).


Modules
Multiple-inheritance (having more than one superclass) is a tricky issue in programming. Tricky because it causes ambiguity in method lookups. Lets say class A inherits from class B and class C. Both B and C have a method called "say_hello()". Now see this code:

obj = A.new
obj.say_hello()

Which method should be executed now? The one in class B or class C? C++ had a messy solution for this with virtual functions where as Java simply wiped its hands off by enforcing single-inheritance (single parent per class). But most real-world objects inherit their behaviour from multiple parents. For eg., Car should be the sub-class of both FourWheeler and PetrolVehicle. This kind of modelling becomes difficult in Java.

Ruby brings the concept of Module. Although a class can have only one immediate superclass, it can include as many modules as it needs. Including a module is almost similar to inheriting from it. So we could write our class as:

class Car < Vehicle
  include FourWheeler
  include PetrolVehicle
end

The method lookup ambiguity is resolved with a specific method lookup order. Read more on this topic as this is one of the most fundamental aspects of Ruby.

String interpolation
How often have you seen code like this? :

"my name is " + obj.name() + " and I live in " + obj.city() + " city."

Functional but ugly code. In Ruby you can do the above but there is a cleaner and more preferred method.

"my name is #{obj.name} and I live in #{obj.city}."

Code is data
Ruby supports meta-programming - which means that code can be used, passed around, and manipulated as any form of data. You can write methods which accept a block of code - apart from the regular data variables - as an argument. This make Ruby really powerful. Read more on BlocksProcsYield and Lambdas.

# Standard way of programming.
>> "hi".upcase # returns "HI"

# Dynamic programming
# works same as above but notice that the name of method to call is a string so can be decided at runtime
>> "hi".send("upcase") # returns "HI"

Here, you are sending the string object ("hi") a message called "upcase". If a method exists with this name, it will be executed. So method calling is basically message-passing. Makes it easy to design actor-based systems.

If you have read SICP, you would of course remember the methods map, filter, enumerate. What a revelation they were! They are implemented beautifully in Ruby using blocks.

# An array of students is given. Promote all students who are boys and whose age is greater than 12 to the next class and return an array of their names
>> students.select { |s| s.is_boy? && s.age > 12 }.each { |s| s.promote }.collect(&:name)

That's it. No nested loops, and iterators/enumerators to worry about. The code here is simply the procedural expression of your requirements! You can easily see how easy it is to fix bugs in this code. Abstraction and hiding details is a good thing and Ruby provides techniques to do it really well. It is also a great vehicle for developing higher-level domain-specific languages.

Meta-programming is my favorite aspect of Ruby. All the power of Lisp. And what pretty syntax it has! :)

Cool ? :)

I hope this gets you excited about this language. There are tons of really nice features that we have come to love, but you can discover them on your own. Feel free to ask any questions or doubts you have. And remember, the underlying philosophy of Ruby design was: "Programmer's happiness". :)