Chapter 1. Just Enough Ruby

The code in this book is written in Ruby, a programming language that was designed to be simple, friendly, and fun. I’ve chosen it because of its clarity and flexibility, but nothing in the book relies on special features of Ruby, so you should be able to translate the code examples into whatever language you prefer—especially another dynamic language like Python or JavaScript—if that helps to make the ideas clearer.

All of the example code is compatible with both Ruby 2.0 and Ruby 1.9. You can find out more about Ruby, and download an official implementation, at the official Ruby website.

Let’s take a quick tour of Ruby’s features. We’ll concentrate on the parts of the language that are used in this book; if you want to learn more, O’Reilly’s The Ruby Programming Language is a good place to start.

Note

If you already know Ruby, you can safely skip to Chapter 2 without missing anything.

Interactive Ruby Shell

One of Ruby’s friendliest features is its interactive console, IRB, which lets us enter pieces of Ruby code and immediately see the results. In this book, we’ll use IRB extensively to interact with the code we’re writing and explore how it works.

You can run IRB on your development machine by typing irb at the command line. IRB shows a >> prompt when it expects you to provide a Ruby expression. After you type an expression and hit Enter, the code gets evaluated, and the result is shown at a => prompt:

$ irb --simple-prompt
>> 1 + 2
=> 3
>> 'hello world'.length
=> 11

Whenever we see these >> and => prompts in the book, we’re interacting with IRB. To make longer code listings easier to read, they’ll be shown without the prompts, but we’ll still assume that the code in these listings has been typed or pasted into IRB. So once the book has shown some Ruby code like this…

x = 2
y = 3
z = x + y

…then we’ll be able to play with its results in IRB:

>> x * y * z
=> 30

Ruby is an expression-oriented language: every valid piece of code produces a value when it’s executed. Here’s a quick overview of the different kinds of Ruby value.

Data Structures

Ruby array literals are written as a comma-separated list of values surrounded by square brackets:

>> numbers = ['zero', 'one', 'two']
=> ["zero", "one", "two"]
>> numbers[1]
=> "one"
>> numbers.push('three', 'four')
=> ["zero", "one", "two", "three", "four"]
>> numbers
=> ["zero", "one", "two", "three", "four"]
>> numbers.drop(2)
=> ["two", "three", "four"]

A range represents a collection of values between a minimum and a maximum. Ranges are written by putting a pair of dots between two values:

>> ages = 18..30
=> 18..30
>> ages.entries
=> [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>> ages.include?(25)
=> true
>> ages.include?(33)
=> false

A hash is a collection in which every value is associated with a key; some programming languages call this data structure a “map,” “dictionary,” or “associative array.” A hash literal is written as a comma-separated list of key => value pairs inside curly brackets:

>> fruit = { 'a' => 'apple', 'b' => 'banana', 'c' => 'coconut' }
=> {"a"=>"apple", "b"=>"banana", "c"=>"coconut"}
>> fruit['b']
=> "banana"
>> fruit['d'] = 'date'
=> "date"
>> fruit
=> {"a"=>"apple", "b"=>"banana", "c"=>"coconut", "d"=>"date"}

Hashes often have symbols as keys, so Ruby provides an alternative key: value syntax for writing key-value pairs where the key is a symbol. This is more compact than the key => value syntax and looks a lot like the popular JSON format for JavaScript objects:

>> dimensions = { width: 1000, height: 2250, depth: 250 }
=> {:width=>1000, :height=>2250, :depth=>250}
>> dimensions[:depth]
=> 250

Ruby has if, case, and while expressions, which work in the usual way:

>> if 2 < 3
     'less'
   else
     'more'
   end
=> "less"
>> quantify =
     -> number {
       case number
       when 1
         'one'
       when 2
         'a couple'
       else
         'many'
       end
     }
=> #<Proc (lambda)>
>> quantify.call(2)
=> "a couple"
>> quantify.call(10)
=> "many"
>> x = 1
=> 1
>> while x < 1000
     x = x * 2
   end
=> nil
>> x
=> 1024

Ruby looks like other dynamic programming languages but it’s unusual in an important way: every value is an object, and objects communicate by sending messages to each other.[1] Each object has its own collection of methods that determine how it responds to particular messages.

A message has a name and, optionally, some arguments. When an object receives a message, its corresponding method is executed with the arguments from the message. This is how all work gets done in Ruby; even 1 + 2 means “send the object 1 a message called + with the argument 2,” and the object 1 has a #+ method for handling that message.

We can define our own methods with the def keyword:

>> o = Object.new
=> #<Object>
>> def o.add(x, y)
     x + y
   end
=> nil
>> o.add(2, 3)
=> 5

Here we’re making a new object by sending the new message to a special built-in object called Object; once the new object’s been created, we define an #add method on it. The #add method adds its two arguments together and returns the result—an explicit return isn’t necessary, because the value of the last expression to be executed in a method is automatically returned. When we send that object the add message with 2 and 3 as arguments, its #add method is executed and we get back the answer we wanted.

We’ll usually send a message to an object by writing the receiving object and the message name separated by a dot (e.g., o.add), but Ruby always keeps track of the current object (called self) and will allow us to send a message to that object by writing a message name on its own, leaving the receiver implicit. For example, inside a method definition the current object is always the object that received the message that caused the method to execute, so within a particular object’s method, we can send other messages to the same object without referring to it explicitly:

>> def o.add_twice(x, y)
     add(x, y) + add(x, y)
   end
=> nil
>> o.add_twice(2, 3)
=> 10

Notice that we can send the add message to o from within the #add_twice method by writing add(x, y) instead of o.add(x, y), because o is the object that the add_twice message was sent to.

Outside of any method definition, the current object is a special top-level object called main, and any messages that don’t specify a receiver are sent to it; similarly, any method definitions that don’t specify an object will be made available through main:

>> def multiply(a, b)
     a * b
   end
=> nil
>> multiply(2, 3)
=> 6

It’s convenient to be able to share method definitions between many objects. In Ruby, we can put method definitions inside a class, then create new objects by sending the new message to that class. The objects we get back are instances of the class and incorporate its methods. For example:

>> class Calculator
     def divide(x, y)
       x / y
     end
   end
=> nil
>> c = Calculator.new
=> #<Calculator>
>> c.class
=> Calculator
>> c.divide(10, 2)
=> 5

Note that defining a method inside a class definition adds the method to instances of that class, not to main:

>> divide(10, 2)
NoMethodError: undefined method `divide' for main:Object

One class can bring in another class’s method definitions through inheritance:

>> class MultiplyingCalculator < Calculator
     def multiply(x, y)
       x * y
     end
   end
=> nil
>> mc = MultiplyingCalculator.new
=> #<MultiplyingCalculator>
>> mc.class
=> MultiplyingCalculator
>> mc.class.superclass
=> Calculator
>> mc.multiply(10, 2)
=> 20
>> mc.divide(10, 2)
=> 5

A method in a subclass can call a superclass method of the same name by using the super keyword:

>> class BinaryMultiplyingCalculator < MultiplyingCalculator
     def multiply(x, y)
       result = super(x, y)
       result.to_s(2)
     end
   end
=> nil
>> bmc = BinaryMultiplyingCalculator.new
=> #<BinaryMultiplyingCalculator>
>> bmc.multiply(10, 2)
=> "10100"

Another way of sharing method definitions is to declare them in a module, which can then be included by any class:

>> module Addition
     def add(x, y)
       x + y
     end
   end
=> nil
>> class AddingCalculator
     include Addition
   end
=> AddingCalculator
>> ac = AddingCalculator.new
=> #<AddingCalculator>
>> ac.add(10, 2)
=> 12

Here’s a grab bag of useful Ruby features that we’ll need for the example code in this book.

Ruby has a built-in module called Enumerable that’s included by Array, Hash, Range, and other classes that represent collections of values. Enumerable provides helpful methods for traversing, searching, and sorting collections, many of which expect to be called with a block. Usually the code in the block will be run against some or all values in the collection as part of whatever job the method does. For example:

>> (1..10).count { |number| number.even? }
=> 5
>> (1..10).select { |number| number.even? }
=> [2, 4, 6, 8, 10]
>> (1..10).any? { |number| number < 8 }
=> true
>> (1..10).all? { |number| number < 8 }
=> false
>> (1..5).each do |number|
     if number.even?
       puts "#{number} is even"
     else
       puts "#{number} is odd"
     end
   end
1 is odd
2 is even
3 is odd
4 is even
5 is odd
=> 1..5
>> (1..10).map { |number| number * 3 }
=> [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

It’s common for the block to take one argument and send it one message with no arguments, so Ruby provides a &:message shorthand as a more concise way of writing the block { |object| object.message }:

>> (1..10).select(&:even?)
=> [2, 4, 6, 8, 10]
>> ['one', 'two', 'three'].map(&:upcase)
=> ["ONE", "TWO", "THREE"]

One of Enumerable’s methods, #flat_map, can be used to evaluate an array-producing block for every value in a collection and concatenate the results:

>> ['one', 'two', 'three'].map(&:chars)
=> [["o", "n", "e"], ["t", "w", "o"], ["t", "h", "r", "e", "e"]]
>> ['one', 'two', 'three'].flat_map(&:chars)
=> ["o", "n", "e", "t", "w", "o", "t", "h", "r", "e", "e"]

Another useful method is #inject, which evaluates a block for every value in a collection and accumulates a final result:

>> (1..10).inject(0) { |result, number| result + number }
=> 55
>> (1..10).inject(1) { |result, number| result * number }
=> 3628800
>> ['one', 'two', 'three'].inject('Words:') { |result, word| "#{result} #{word}" }
=> "Words: one two three"


[1] This style comes from the Smalltalk programming language, which had a direct influence on the design of Ruby.