Learn Ruby on Rails: the Ultimate Beginner’s Tutorial
Object Oriented Programming in Ruby
Let’s build on the theory that we covered at the start of this chapter as we take a look at Ruby’s implementation of OOP.
As we already know, the structure of an application based on OOP principles is focused on interaction with objects. These objects are often representations of real-world objects, like a Car. Interaction with an object occurs when we send it a message or ask it a question. If we really did have a
Car object called
kitt (we don’t — yet), starting the car might be as simple as:
This short line of Ruby code sends the message start to the object
kitt. Using OOP terminology, we would say that this code statement calls the
start method of the
As I mentioned before, in contrast to other object oriented programming languages such as Python and PHP, in Ruby, everything is an object. Especially when compared with PHP, Ruby’s OOP doesn’t feel like a “tacked-on” afterthought — it was clearly intended to be a core feature of the language from the beginning, which makes using the OOP features in Ruby a real pleasure.
As we saw in the previous section, even the simplest of elements in Ruby (like literal strings and numbers) are objects to which you can send messages.
Classes and Objects
As in any other OOP language, in Ruby, each object belongs to a certain class (for example,
PontiacFirebird might be an object of class
Car). As we saw in the discussion at the beginning of this chapter, a class can group objects of a certain kind, and equip those objects with common functionality. This functionality comes in the form of methods, and in the object’s ability to store information. For example, a
PontiacFirebird object might need to store its mileage, as might any other object of the class
In Ruby, the instantiation of a new object that’s based on an existing class is accomplished by sending that class the new message. The result is a new object of that class. The following few lines of code show an extremely basic class definition into Ruby — the third line is where we create an instance of the class that we just defined.
irb> class Car irb> end => nil irb> kitt = Car.new => #<Car:0x75e54>
Another basic principle in OOP is encapsulation. According to this principle, objects should be treated as independent entities, each taking care of its own internal data and functionality. If we need to access an object’s information — for instance, its internal variables — we make use of the object’s interface, which is the subset of the object’s methods that are made available for other objects to call.
Ruby provides objects with functionality at two levels — the object level, and class level — and it adheres to the principle of encapsulation while it’s at it! Let’s dig deeper.
At the object level, data storage is handled by instance variables (a name that’s derived from the instantiation process mentioned above). Think of instance variables as storage containers that are attached to the object, but to which other objects do not have direct access.
To store or retrieve data from these variables, another object must call an accessor method on the object. An accessor method has the ability to set (and get) the value of the object’s instance variables.
Let’s look at how instance variables and accessor methods relate to each other, and how they’re implemented in Ruby.
Instance variables are bound to an object, and contain values for that object only.
Revisiting our cars example, the mileage values for a number of different
Car objects are likely to differ, as each car will have a different mileage. Therefore, mileage is held in an instance variable.
An instance variable can be recognized by its prefix: a single “at” sign (
@). And what’s more, instance variables don’t even need to be declared! There’s only one problem: we don’t have any way to retrieve or change them once they do exist. This is where instance methods come into play.
Data storage and retrieval is not the only capability that can be bound to a specific object — functionality, too, can be bound to objects. We achieve this binding through the use of instance methods, which are specific to an object. Invoking an instance method (in other words, sending a message that contains the method name to an object) will invoke that functionality on the receiving object only.
Instance methods are defined using the def keyword, and end with the end keyword. Enter the following example into a new Ruby shell:
$ irb irb> class Car irb> def open_trunk irb> # code to open trunk goes here irb> end irb> end => nil irb> kitt = Car.new => #<Car:0x75e54>
What you’ve done is define a class called
Car, which has an instance method with the name
Car object instantiated from this class will (possibly using some fancy robotics connected to our Ruby program) open its trunk when its
open_trunk method is called. (Ignore that
nil return value for the moment; we’ll look at nil values in the next section.)
Indenting your Code
While the indentation of code is a key element of the syntax of languages such as Python, in Ruby, indentation is purely cosmetic — it aids readability, but does not affect the code in any way. In fact, while we’re experimenting with the Ruby shell, you needn’t be too worried about indenting any of the code. However, when we’re saving files that will be edited later, you’ll want the readability benefits that come from indenting nested lines.
The Ruby community has agreed upon two spaces as being optimum for indenting blocks of code such as class or method definitions. We’ll adhere to this indentation scheme throughout this book.
With our class in place, we can make use of this method:
irb> kitt.open_trunk => nil
Since we don’t want the trunks of all cars to open at once, we’ve made this functionality available as an instance method.
I know, I know: we still haven’t modified any data. We use accessor methods for this task.
An accessor method is a special type of instance method, and is used to read or write to an instance variable. There are two types: readers (sometimes called “getters”) and writers (or “setters”).
A reader method will look inside the object, fetch the value of an instance variable, and hand this value back to us. A writer method, on the other hand, will look inside the object, find an instance variable, and assign the variable the value that it was passed.
Let’s add some methods for getting and setting the
@mileage attribute of our
Car objects. Once again, exit from the Ruby shell so that we can create an entirely new
Car class definition. Our class definition is getting a bit longer now, so enter each line carefully. If you make a typing mistake, exit the shell and start over.
$ irb irb> class Car irb> def set_mileage(x) irb> @mileage = x irb> end irb> def get_mileage irb> @mileage irb> end irb> end => nil irb> kitt = Car.new => #<Car:0x75e54>
Now, we can finally modify and retrieve the mileage of our
irb> kitt.set_mileage(5667) => 5667 irb> kitt.get_mileage => 5667
This is still a bit awkward. Wouldn’t it be nice if we could give our accessor methods exactly the same names as the attributes that they read from or write to? Luckily, Ruby contains shorthand notation for this very task. We can rewrite our class definition as follows:
$ irb irb> class Car irb> def mileage=(x) irb> @mileage = x irb> end irb> def mileage irb> @mileage irb> end irb> end => nil irb> kitt = Car.new => #<Car:0x75e54>
With these accessor methods in place, we can read to and write from our instance variable as if it were available from outside the object.
irb> kitt.mileage = 6032 => 6032 irb> kitt.mileage => 6032
These accessor methods form part of the object’s interface.
At the class level, class variables handle data storage. They’re commonly used to store state information, or as a means of configuring default values for new objects. Class variables are typically set in the body of a class, and can be recognized by their prefix: a double “at” sign (
First, enter the following class definition into a new Ruby shell.
$ irb irb> class Car irb> @@number_of_cars = 0 irb> def initialize irb> @@number_of_cars = @@number_of_cars + 1 irb> end irb> end => nil
The class definition for the class
Car above has an internal counter for the total number of
Car objects that have been created. Using the special instance method
initialize, which is invoked automatically every time an object is instantiated, this counter is incremented for each new
By the way, we have actually already used a class method. Do you like how I snuck it in there? The new method is an example of a class method that ships with Ruby and is available to all classes — whether they’re defined by you, or form part of the Ruby Standard Library. (The Ruby Standard Library is a large collection of classes that’s included with every Ruby installation. The classes facilitate a wide range of common functionality, such as accessing web sites, date calculations, file operations, and more.)
Custom class methods are commonly used to create objects with special properties (such as a default color for our
Car objects), or to gather statistics about the class’s usage.
Extending the earlier example, we could use a class method called count to return the value of the
@@number_of_cars class variable. Remember that this is a variable that’s incremented for every new
Car object that’s created. Class methods are defined identically to instance methods: using the
end keywords. The only difference is that class method names are prefixed with
self. Enter this code into a new Ruby shell:
$ irb irb> class Car irb> @@number_of_cars = 0 irb> def self.count irb> @@number_of_cars irb> end irb> def initialize irb> @@number_of_cars+=1 irb> end irb> end => nil
The following code instantiates some new
Car objects, then makes use of our new class method:
irb> kitt = Car.new # Michael Knight's talking car => #<0xba8c> irb> herbie = Car.new # The famous VolksWagen love bug! => #<0x8cd20> irb> batmobile = Car.new # Batman's sleek automobile => #<0x872e4> irb> Car.count => 3
The method tells us that three instances of the
Car class have been created. Note that we can’t call a class method on an object (Ruby actually does provide a way to invoke some class methods on an object, using the
:: operator, but we won’t worry about that for now. We’ll see the
:: operator in use in Chapter 4, Rails Revealed.):
irb> kitt.count NoMethodError: undefined method 'count' for #<Car:0x89da0>
As implied by the name, the count class method is available only to the
Car class, not to any objects instantiated from that class.
I sneakily introduced something else in there. Did you spot it? In many languages, including PHP and Java, the ++ and — operators are used to increment a variable by one. Ruby doesn’t support this notation; instead, when working with Ruby, we need to use the
+= operator. Therefore, the shorthand notation for incrementing our counter in the class definition is:
This code is identical to the following:
irb> @@number_of_cars = @@number of cars + 1
Both of these lines can be read as “
my_variable becomes equal to
my_variable plus one.”
If your application deals with more than the flat hierarchy we’ve explored so far, you might want to construct a scenario whereby some classes inherit from other classes. Continuing with the car analogy, let’s suppose that we had a green limousine named Larry (this assigning of names to cars might feel a bit strange, but it’s important for this example, so bear with me). In Ruby, the
larry object would probably descend from a
StretchLimo class, which could in turn descend from the class
Car. Let’s implement that, to see how it works:
$ irb irb> class Car irb> @@wheels = 4 irb> end => nil irb> class StretchLimo < Car irb> @@wheels = 6 irb> def turn_on_television irb> # Invoke code for switching on on-board TV here irb> end irb> end => nil
Now, if we were to instantiate an object of class
StretchLimo, we’d end up with a different kind of car. Instead of the regular four wheels that standard
Car objects have, this one would have six wheels (stored in the class variable
@@wheels). It would also have extra functionality, made possible by an extra method —
turn_on_television — which would be available to be called by other objects.
However, if we were to instantiate a regular
Car object, the car would have only four wheels, and there would be no instance method for turning on an on-board television. Think of inheritance as a way for the functionality of a class to become more specialized the further we move down the inheritance path.
Don’t worry if you’re struggling to wrap your head around all the aspects of OOP — you’ll automatically become accustomed to them as you work through this book. You might find it useful to come back to this section, though, especially if you need a reminder about a certain term later on.
It’s always great to receive feedback. Remember our talk about passing arguments to methods? Well, regardless of whether or not a method accepts arguments, invoking a method in Ruby always results in feedback — it comes in the form of a return value, which is returned either explicitly or implicitly.
To return a value explicitly, use the
return statement in the body of a method:
irb> def toot_horn irb> return "toooot!" irb> end => nil
toot_horn method in this case would produce the following:
irb> toot_horn => "toooot!"
However, if no return statement is used, the result of the last statement that was executed is used as the return value. This behavior is quite unique to Ruby:
irb> def toot_loud_horn irb> "toooot!".upcase irb> end => nil
toot_loud_horn method in this case would produce:
irb> toot_loud_horn => "TOOOOT!"
When you need to show output to the users of your application, use the
puts (“put string”) statements. Both methods will display the arguments passed to them as
puts also inserts a carriage return at the end of its output. Therefore, in a Ruby program the following lines:
print "The quick " print "brown fox"
would produce this output:
The quick brown fox
puts like so:
puts "jumps over" puts "the lazy dog"
would produce this output:
jumps over the lazy dog
At this stage, you might be wondering why all of the trial-and-error code snippets that we’ve typed into the Ruby shell actually produced output, given that we haven’t been making use of the
puts methods. The reason is that irb automatically writes the return value of the last statement it executes to the screen before displaying the irb prompt. This means that using a print or puts from within the Ruby shell might in fact produce two lines of output — the output that you specify should be displayed, and the return value of the last command that was executed, as in the following example:
irb> puts "The quick brown fox" "The quick brown fox" => nil
nil is actually the return value of the
puts statement. Looking back at previous examples, you will have encountered nil as the return value for class and method definitions, and you’ll have received a hexadecimal address, such as
<#Car:0x89da0>, as the return value for object definitions. This hexadecimal value showed the location in memory that the object we instantiated occupied, but luckily we won’t need to bother with such geeky details any further.
Having met the
puts statements, you should be aware that a Rails application actually has a completely different approach to displaying output, called templates. We’ll look at templates in Chapter 4, Rails Revealed.