21.2. Quick Tour

21.2.1. Expression

We start our journey at the usual place.

>> "Hello World"
=> "Hello World"
>> puts "Hello World\n"
Hello World
=> nil
>> 4 + 5 * 6
=> 34

The first impression is typical for a "scripting" language: no surprise. As an interesting detail we notice that the interactive shell always prints the value of the statement after the arrow "=>". Even the print statement returns something (albeit nil). This is a first indication that Ruby treats everything as expressions (like a functional language). This impression is quickly confirmed trying the "if" statement (or rather expression), something we lamented about when using Python.

>> if 1 < 2 then 5 else 6 end
=> 5

Even better, the same is possible with a case statement.

>> case 30
    when 0..10 then "apple"
    when 11..20 then "pear"
    else "banana"
  end
=> "banana"

Many developers will welcome another difference from Python: indentation does not matter; Ruby uses the "end" keyword to denote the end of a block if necessary. Concerning variables, we expect from a scripting language that they don't have to be declared and that they can change their type any time.

>> x = "blah"
=> "blah"
>> x = 55
=> 55

21.2.2. Collections

What about built-in collections as another typical scripting feature? Ruby's lists feel like Python lists (or Perl's anonymous arrays) with Perl's slices.

>> l = [1, 2, 3]
=> [1, 2, 3]
>> l[1]
=> 2
>> l[-1]
=> 3
>> l[1..2]
=> [2, 3]
>> l[1..-1]
=> [2, 3]

The syntax for (hash) maps corresponds to Perl's anonymous hashes (when used in a sensible way with the arrow notation), but the subscript operator is (fortunately) the same as for lists.

>> m = {"a" => 1, "b" => 2}
=> {"a"=>1, "b"=>2}
>> m["a"]
=> 1

Ruby is truely object oriented. All values are first class objects (including integers, floating point numbers, strings) and all functionality which can be attached to an object is provided as a method of the object (and not a function as in Python in many cases). Here are a few examples using strings and lists.

>> "blah".length
=> 4
>> "blah".length()
=> 4
>> "blah".index('l')
=> 1
>> -1234.abs
=> 1234
>> "blah".capitalize
=> Blah
>> "BlAh.downcase
=> blah
>> [1, 2, 3].join(";")
=> 1;2;3

Like Perl, Ruby allows to omit the parentheses around function or method arguments unless required to ensure the correct precedence. And like Perl, Ruby has to pay for this debatable convenience with a more complex handling of function objects as we will see below.

Ruby has adopted Scheme's convention for method suffixes. Predicates, that is, methods which return a boolean, end with a question mark, and destructive methods, which change the associated object, end with an exclamation mark.

>> [1, 2, 3].reverse!
=> [3, 2, 1]
>> [1, 2, 3].include?(1)
=> true
>> [1, 2, 3].include? 4
=> false
>> [].empty?
=> true
>> [1, 2, 3, 2, 1].uniq!
=> [1, 2, 3]

21.2.3. Objects and Classes

Ruby's object oriented part combines Smalltalk and Python with a more common syntax. Here is the Person class again.

>> class Person
>>   def initialize(name, age)
>>     @name = name
>>     @age = age
>>   end
>> end
=> nil
>> person = Person.new("Homer", 55)
=> #<Person:0x2a42868 @age=55, @name="Homer">

Similar to Python, methods are functions inside a class. But in contrast to Python we see no explicit reference to the instance itself self and the initializer is simply called initialize. Ruby uses "funny symbols" to mark instance, class, and global variables. The "at" sign is used for instance variables, two "at" signs for class variables, and the dollar for global variables. In this example, we set the two instance variables name and age to the given values. Like in Python, attributes do not have to be declared but string into live at the first assignment.

Our class does not do much yet. As a first step, we will override Ruby's method to_s which is used by the print function to convert an object to a string. It is pleasant for our incremental presentation that we can add features to the existing Person class as we go.

class Person
  def to_s
    "Person(name=#{@name}, age=#{age})"
  end
end
=> nil
>> print person
Person(name=Homer, age=55)
=> nil

Since the attribute itself uses the @ prefix, there is no confusion between the attribute and a method with the same name.

class Person
  def name
    @name
  end
end
=> nil
>> person.name
=> "Homer"

But how to distinguish getter and setter? The setter syntax show another example of Ruby's method suffixes. When assigning a value to a "field" of an object, the method with the same name as the field and the = suffix is called.

class Person
  def name=(aName)
    @name = aName
  end
end
=> nil
>> person.name = "Bart"
=> "Bart"
>> person.name
=> "Bart"

We don't need to code the standard accessor methods ourselves. Ruby provides a shortcut for it.