Organizing objects with classes - Ruby foundations - The Well-Grounded Rubyist, Second Edition (2014)

The Well-Grounded Rubyist, Second Edition (2014)

Part 1. Ruby foundations

Chapter 3. Organizing objects with classes

This chapter covers

· Creating multiple objects with classes

· Setting and reading object state

· Automating creation of attribute read and write methods

· Class inheritance mechanics

· Syntax and semantics of Ruby constants

Creating a new object with Object.new—and equipping that object with its own methods, one method at a time—is a great way to get a feel for the object-centeredness of Ruby programming. But this approach doesn’t exactly scale; if you’re running an online box office and your database has to process records for tickets by the hundreds, you’ve got to find another way to create and manipulate ticket-like objects in your Ruby programs.

Sure enough, Ruby gives you a full suite of programming techniques for creating objects on a batch basis. You don’t have to define a separate price method for every ticket. Instead, you can define a ticket class, engineered in such a way that every individual ticket object automatically has theprice method.

Defining a class lets you group behaviors (methods) into convenient bundles, so that you can quickly create many objects that behave essentially the same way. You can also add methods to individual objects, if that’s appropriate for what you’re trying to do in your program. But you don’t have to do that with every object if you model your domain into classes.

Everything you handle in Ruby is either an object or a construct that evaluates to an object, and every object is an instance of some class. This fact holds true even where it might at first seem a little odd. Integers are instances of a class, and classes themselves are objects. You’ll learn in this chapter how this pervasive aspect of the design of Ruby operates.

Talking about classes doesn’t mean you’re not talking about objects; that’s why this chapter has the title it has, rather than, say, “Ruby classes.” Much of what we’ll look at here pertains to objects and methods—but that’s because classes are, at heart, a way to organize objects and methods. We’ll look at the kinds of things you can and will do inside classes, as well as what classes themselves are.

3.1. Classes and instances

A typical class consists of a collection of method definitions. Classes usually exist for the purpose of being instantiated—that is, of having objects created that are instances of the class.

You’ve already seen instantiation in action. It’s our old signature tune:

obj = Object.new

Object is a built-in Ruby class. When you use the dot notation on a class, you send a message to the class. Classes can respond to messages, just like objects; in fact, as you’ll have reason to be aware of in any number of situations, classes are objects. The new method is a constructor: a method whose purpose is to manufacture and return to you a new instance of the class, a newly minted object.

You define a class with the class keyword. Classes are named with constants, a special type of identifier recognizable by the fact that it begins with a capital letter. Constants are used to store information and values that don’t change over the course of a program run.

Warning

Constants can change—they’re not as constant as their name implies. But if you assign a new value to a constant, Ruby prints a warning. The best practice is to avoid assigning new values to constants that you’ve already assigned a value to. (See section 3.7.2 for more information about reassignment to constants.)

Let’s define a Ticket class. Inside the class definition, we define a single, simple method:

Now we can create a new ticket object and ask it (pointlessly, but to see the process) to describe its event:

ticket = Ticket.new
puts ticket.event

The method call ticket.event results in the execution of our event method and, consequently, the printing out of the (rather uninformative) string specified inside that method:

Can't really be specified yet...

The information is vague, but the process is fully operational: we’ve written and executed an instance method.

Meaning what, exactly?

3.1.1. Instance methods

The examples of method definitions in chapter 2 involved defining methods directly on individual objects:

def ticket.event

The event method in the previous example, however, is defined in a general way, inside the Ticket class:

def event

That’s because this event method will be shared by all tickets—that is, by all instances of Ticket. Methods of this kind, defined inside a class and intended for use by all instances of the class, are called instance methods. They don’t belong only to one object. Instead, any instance of the class can call them.

Note

Methods that you define for one particular object—as in def ticket.price—are called singleton methods. You’ve already seen examples, and we’ll look in more depth at how singleton methods work in chapter 13. An object that has a price method doesn’t care whether it’s calling a singleton method or an instance method of its class. But the distinction is important from the programmer’s perspective.

Once you’ve defined an instance method in a class, nothing stops you from defining it again—that is, overriding the first definition with a new one.

3.1.2. Overriding methods

Here’s an example of defining the same method twice in one class:

class C
def m
puts "First definition of method m"
end

def m
puts "Second definition of method m"
end
end

Given these two definitions, what happens when we call m on an instance of C? Let’s ask the object:

C.new.m

The printed result is Second definition of method m. The second definition has prevailed: we see the output from that definition, not from the first. When you override a method, the new version takes precedence.

(The preceding example is deliberately minimalist, because it’s illustrating something that you wouldn’t normally do in exactly this form. When you override a method, it’s usually because you’ve written a class that inherits from the original class, and you want it to behave differently. We’ll look at inheritance soon.)

You can also add to a class’s methods, or override them, by reopening the class definition.

3.1.3. Reopening classes

In most cases, when you’re defining a class, you create a single class definition block:

class C
# class code here
end

But it’s possible to reopen a class and make additions or changes. Here’s an example:

class C
def x
end
end

class C
def y
end
end

We open the class definition body, add one method (x), and close the definition body. Then, we reopen the definition body, add a second method (y), and close the definition body. The result is the same as if we’d done this:

class C
def x
end

def y
end
end

Here we open the class only once and add both methods. Of course, you’re not going to break your class definitions into separate blocks just for fun. There has to be a reason—and it should be a good reason, because separating class definitions can make it harder for people reading or using your code to follow what’s going on.

One reason to break up class definitions is to spread them across multiple files. If you require a file that contains a class definition (perhaps you load it from the disk at runtime from another file, and you also have a partial definition of the same class in the file from which the second file is required), the two definitions are merged. This isn’t something you’d do arbitrarily: it must be a case where the program’s design demands that a class be defined partially in one place and partially in another.

Here’s a real-life example. Ruby has a Time class. It lets you manipulate times, format them for timestamp purposes, and so forth. You can use UNIX-style date-format strings to get the format you want. For example, the command

puts Time.new.strftime("%m-%d-%y")

prints the string "02-09-14", representing the date on which the method call was made.

In addition to the built-in Time class, Ruby also has a program file called time.rb, inside of which are various enhancements of, and additions to, the Time class. time.rb achieves its goal of enhancing the Time class by reopening that class. If you look for the file time.rb either in the lib subdirectory of the Ruby source tree or in your Ruby installation, you’ll see this on or near line 87:

class Time

That’s a reopening of the Time class, done for the purpose of adding new methods.

You can see the effect best by trying it in irb. irb lets you call a nonexistent method without causing the session to terminate, so you can see the effects of the require command all in one session:

Here we send the unrecognized message xmlschema to our Time object, and it doesn’t work . Then, we load the time.rb file and, sure enough, the Time object now has an xmlschema method. (That method, according to its documentation, “returns a string that represents the time as dateTime defined by XML Schema.”)

You can spread code for a single class over multiple files or over multiple locations in the same file. But be aware that it’s considered better practice not to do so, when possible. In the case of the Time extensions, people often suggest the possibility of unification: giving Time objects all the extension methods in the first place, and not separating those methods into a separate library. It’s possible that such unification will take place in a later release of Ruby.

Ruby is about objects, and objects are instances of classes. We’ll look next at instance variables, a special language feature designed to allow every instance of every class in Ruby to set and maintain its own private stash of information.

3.2. Instance variables and object state

When we created individual objects and wrote methods for each action or value we needed, we hard-coded the value into the object through the methods. With this technique, if a ticket costs $117.50, then it has a method called price that returns precisely that amount:

ticket = Object.new
def ticket.price
117.50
end

But now we’re moving away from one-at-a-time object creation with Object.new and setting our sights on the practice of designing classes and creating many objects from them.

This means we’re changing the rules of the game when it comes to information like the price of a ticket. If you create a Ticket class, you can’t give it a price method that returns $117.50, for the simple reason that not every ticket costs $117.50. Similarly, you can’t give every ticket the event-name Benefit Concert, nor can every ticket think that it’s for Row G, Seat 33.

Instead of hard-coding values into every object, we need a way to tell different objects that they have different values. We need to be able to create a new Ticket object and store with that object the information about the event, price, and other properties. When we create another ticket object, we need to store different information with that object. And we want to be able to do this without having to handcraft a method with the property hard-coded into it.

Information and data associated with a particular object embodies the state of the object. We need to be able to do the following:

· Set, or reset, the state of an object (say to a ticket, “You cost $11.99.”).

· Read back the state (ask a ticket, “How much do you cost?”).

Conveniently, Ruby objects come with their own storage and retrieval mechanism for values: instance variables.

The instance variable enables individual objects to remember state. Instance variables work much like other variables: you assign values to them, and you read those values back; you can add them together, print them out, and so on. But instance variables have a few differences:

· Instance variable names always start with a single @ (at sign). This enables you to recognize an instance variable at a glance.

· Instance variables are only visible to the object to which they belong. (Being “visible to an object” has a technical definition having to do with the default object self, which you’ll see more about in chapter 5.)

· An instance variable initialized in one method inside a class can be used by any method defined within that class.

The following listing shows a simple example illustrating the way the assigned value of an instance variable stays alive from one method call to another.

Listing 3.1. An instance variable maintaining its value between method calls

Thanks to the assignment that happens as a result of the call to set_name , when you ask for the person’s name , you get back what you put in: "Joe". Unlike a local variable, the instance variable @name retains the value assigned to it even after the method in which it was initialized has terminated. This property of instance variables—their survival across method calls—makes them suitable for maintaining state in an object.

You’ll see better, more idiomatic ways to store and retrieve values in objects shortly. But they’re all based on setting and retrieving the values of instance variables, so it pays to get a good feel for how instance variables behave.

The scene is set to do something close to useful with our Ticket class. The missing step, which we’ll now fill in, is the object initialization process.

3.2.1. Initializing an object with state

When you write a class (like Ticket), you can, if you wish, define a special method called initialize. If you do so, that method will be executed every time you create a new instance of the class.

For example, given an initialize method that prints a message

class Ticket
def initialize
puts "Creating a new ticket!"
end
end

you’ll see the message "Creating a new ticket!" every time you create a new ticket object by calling Ticket.new.

You can employ this automatic initialization process to set an object’s state at the time of the object’s creation. Let’s say we want to give each ticket object a venue and date when it’s created. We can send the correct values as arguments to Ticket.new, and those same arguments will be sent toinitialize automatically. Inside initialize, we’ll have access to the venue and date information, and can save that information by means of instance variables:

class Ticket
def initialize(venue,date)
@venue = venue
@date = date
end

Before closing the class definition with end, we should add something else: a way to read back the venue and date. Let’s drop the get_ formula that we used with get_name (in listing 3.1) and instead name the get methods after the instance variables whose values they return. Add this code (which includes the end directive for the class definition) to the previous lines:

def venue
@venue
end

def date
@date
end
end

Each of these methods hands back the value of an instance variable. In each case, that variable is the last (and only) expression in the method and therefore also serves as the method’s return value.

Note

The names of the instance variables, methods, and arguments to initialize don’t have to match. You could use @v instead of @venue, for example, to store the value passed in the argument venue. You could call the second method event_date and still use @date inside it. Still, it’s usually good practice to match the names to make it clear what goes with what.

Now we’re ready to create some tickets with dynamically set values for venue and date, rather than the hard-coded values of our earlier examples:

th = Ticket.new("Town Hall", "11/12/13")
cc = Ticket.new("Convention Center", "12/13/14")
puts "We've created two tickets."
puts "The first is for a #{th.venue} event on #{th.date}."
puts "The second is for an event on #{cc.date} at #{cc.venue}."

Run this code, along with the previous class definition of Ticket, and you’ll see the following:

We've created two tickets.
The first is for a Town Hall event on 11/12/13.
The second is for an event on 12/13/14 at Convention Center.

The phrase at Convention Center is a bit stilted, but the process of saving and retrieving information for individual objects courtesy of instance variables operates perfectly. Each ticket has its own state (saved information), thanks to what our initialize method does; and each ticket lets us query it for the venue and date, thanks to the two methods with those names.

This opens up our prospects immensely. We can create, manipulate, compare, and examine any number of tickets at the same time, without having to write separate methods for each of them. All the tickets share the resources of the Ticket class. At the same time, each ticket has its own set of instance variables to store state information.

So far, we’ve arranged things in such a way that we set the values of the instance variables at the point where the object is created and can then retrieve those values at any point during the life of the object. That arrangement is often adequate, but it’s not symmetrical. What if you want to set values for the instance variables at some point other than object-creation time? What if you want to change an object’s state after it’s already been set once?

3.3. Setter methods

When you need to set or change an object’s state at some point in your program other than the initialize method, the heart of the matter is assigning (or reassigning) values to instance variables. You can, of course, change any instance variable’s value in any method. For example, if we wanted tickets to have the ability to discount themselves, we could write an instance method like this inside the Ticket class definition:

def discount(percent)
@price = @price * (100 - percent) / 100.0
end

But the most common case is the simplest: calling a setter method with an argument and setting the appropriate instance variable to the argument. That’s what set_name does in the Person class example.

There’s more to it, though. Ruby has some specialized method-naming conventions that let you write setter methods in a way that’s more elegant than sticking set_ in front of a descriptive word like name. We’ll make another pass at Ticket, this time with an eye on setter methods and the techniques available for streamlining them.

3.3.1. The equal sign (=) in method names

Let’s say we want a way to set the price of a ticket. As a starting point, price can be set along with everything else at object-creation time:

class Ticket
def initialize(venue,date,price)
@venue = venue
@date = date
@price = price
end
# etc.

def price
@price
end
# etc.
end

th = Ticket.new("Town Hall", "11/12/13", 63.00)

The initialization command is getting awfully long, though, and requires that we remember what order to put the many arguments in so we don’t end up with a ticket whose price is "Town Hall". And we still don’t have a way to change a ticket’s price later.

Let’s solve the problem, initially, with a set_price method that allows us to set, or reset, the price of an existing ticket. We’ll also rewrite the initialize method so that it doesn’t expect a price figure:

class Ticket
def initialize(venue, date)
@venue = venue
@date = date
end

def set_price(amount)
@price = amount
end

def price
@price
end
end

Here’s some price manipulation in action:

The output is

The ticket costs $63.00.
Whoops -- it just went up. It now costs $72.50.

This technique works: you can write all the set_property methods you need, and the instance variable–based retrieval methods to go with them. But there’s a nicer way.

Tip

The percent sign technique you saw in the last example allows you to format strings using sprintf-like syntax. Ruby also has a sprintf method (also available with the name format); we could rewrite the ticket price example as sprintf("%.2f", ticket.price). Possible format specifiers (the% things inside the pattern string) include %d for decimal numbers, %s for strings, %f for floats, and %x for hexadecimal numbers. Run ri sprintf for full documentation.

Ruby allows you to define methods that end with an equal sign (=). Let’s replace set_price with a method called price= (“price” plus an equal sign):

def price=(amount)
@price = amount
end

price= does exactly what set_price did, and in spite of the slightly odd method name, you can call it just like any other method:

ticket.price=(63.00)

The equal sign gives you that familiar “assigning a value to something” feeling, so you know you’re dealing with a setter method. It still looks odd, though; but Ruby takes care of that, too.

3.3.2. Syntactic sugar for assignment-like methods

Programmers use the term syntactic sugar to refer to special rules that let you write your code in a way that doesn’t correspond to the normal rules but that’s easier to remember how to do and looks better.

Ruby gives you some syntactic sugar for calling setter methods. Instead of

ticket.price=(63.00)

you’re allowed to do this:

ticket.price = 63.00

When the interpreter sees this sequence of code, it automatically ignores the space before the equal sign and reads price = as the single message price= (a call to the method whose name is price=, which we’ve defined). As for the right-hand side, parentheses are optional for method arguments, as long as there’s no ambiguity. So you can put 63.00 there, and it will be picked up as the argument to the price= method.

The intent behind the inclusion of this special syntax is to allow you to write method calls that look like assignments. If you just saw ticket.price = 63.00 in a program, you might assume that ticket.price is some kind of l-value to which the value 63.00 is being assigned. But it isn’t. The whole thing is a method call. The receiver is ticket, the method is price=, and the single argument is 63.00.

The more you use this setter style of method, the more you’ll appreciate how much better the sugared version looks. This kind of attention to appearance is typical of Ruby.

Keep in mind, too, that setter methods can do more than simple variable assignment.

3.3.3. Setter methods unleashed

The ability to write your own =-terminated methods and the fact that Ruby provides the syntactic sugar way of calling those methods open up some interesting possibilities.

One possibility is abuse. It’s possible to write =-terminated methods that look like they’re going to do something involving assignment but don’t:

class Silly
def price=(x)
puts "The current time is #{Time.now}"
end
end

s = Silly.new
s.price = 111.22

This example discards the argument it receives (111.22) and prints out an unrelated message:

The current time is 2014-02-09 09:53:31 -0500

This example is a deliberate caricature. But the point is important: Ruby checks your syntax but doesn’t police your semantics. You’re allowed to write methods with names that end with =, and you’ll always get the assignment-syntax sugar. Whether the method’s name makes any sense in relation to what the method does is in your hands.

Equal sign methods can also serve as filters or gatekeepers. Let’s say we want to set the price of a ticket only if the price makes sense as a dollar-and-cents amount. We can add intelligence to the price= method to ensure the correctness of the data. Here, we’ll multiply the number by 100, lop off any remaining decimal-place numbers with the to_i (convert to integer) operation, and compare the result with the original number multiplied by 100. This should expose any extra decimal digits beyond the hundredths column:

class Ticket
def price=(amount)
if (amount * 100).to_i == amount * 100
@price = amount
else
puts "The price seems to be malformed"
end
end

def price
@price
end
end

You can also use this kind of filtering technique to normalize data—that is, to make sure certain data always takes a certain form. For example, let’s say you have a travel agent website where the user needs to type in the desired date of departure. You want to allow both mm/dd/yy and mm/dd/yyyy.

If you have, say, a Ruby CGI script that’s processing the incoming data, you might normalize the year by writing a setter method like this:

Then, assuming you have a variable called date in which you’ve stored the date field from the form (using Ruby’s CGI library), you can get at the components of the date like this:

month, day, year = date.split('/')
self.year = year

The idea is to split the date string into three strings using the slash character (/) as a divider, courtesy of the built-in split method, and then to store the year value in the TravelAgentSession object using that object’s year= method.

Warning

Setter methods don’t return what you might think. When you use the syntactic sugar that lets you make calls to = methods that look like assignments, Ruby takes the assignment semantics seriously. Assignments (like x = 1) evaluate to whatever’s on their right-hand side. Methods usually return the value of the last expression evaluated during execution. But = method calls behave like assignments: the value of the expression ticket.price = 63.00 is 63.00, even if the ticket= method returns the string "Ha ha!". The idea is to keep the semantics consistent. Under the hood, it’s a method call; but it looks like an assignment and behaves like an assignment with respect to its value as an expression.

You’ll write complex getter and setter methods sometimes, but the simple get and set operations, wrapped around instance variables, are the most common—so common, in fact, that Ruby gives you some shortcuts for writing them.

3.4. Attributes and the attr_* method family

An attribute is a property of an object whose value can be read and/or written through the object. In the case of ticket objects, we’d say that each ticket has a price attribute as well as a date attribute and a venue attribute. Our price= method can be described as an attribute writer method.date, venue, and price (without the equal sign) are attribute reader methods. (The write/read terminology is equivalent to the set/get terminology used earlier, but write/read is more common in Ruby discussions.)

The attributes of Ruby objects are implemented as reader and/or writer methods wrapped around instance variables—or, if you prefer, instance variables wrapped up in reader and/or writer methods. There’s no separate “attribute” construct at the language level. Attribute is a high-level term for a particular configuration of methods and instance variables. But it’s a useful term, and Ruby does embed the concept of attributes in the language, in the form of shortcuts that help you write the methods that implement them.

3.4.1. Automating the creation of attributes

Consider the following listing’s full picture of what we have, by way of attribute reader and/or writer methods, in our Ticket class. (There’s nothing new here; the code is just being pulled together in one place.)

Listing 3.2. Ticket class, with the attribute reader/writer methods spelled out

class Ticket
def initialize(venue, date)
@venue = venue
@date = date
end

def price=(price)
@price = price
end

def venue
@venue
end

def date
@date
end

def price
@price
end
end

There’s one read/write attribute (price) and two read attributes (venue and date). It works, but the code is repetitive. Three methods look like this:

def something
@something
end

And there’s repetition on top of repetition: not only are there three such methods, but each of those three methods repeats its name in the name of the instance variable it uses.

Any time you see repetition on that scale, you should try to trim it—not by reducing what your program does, but by finding a way to express the same thing more concisely. In pursuit of this conciseness, Ruby is one step ahead: it provides a built-in shortcut that automatically creates a method that reads and returns the value of the instance variable with the same name as the method (give or take an @). It works like this:

class Ticket
attr_reader :venue, :date, :price
end

The elements that start with colons (:venue, and so on) are symbols. Symbols are a kind of naming or labeling facility. They’re a cousin of strings, although not quite the same thing. We’ll look at symbols in more depth in chapter 8. For our present purposes, you can think of them as functionally equivalent to strings.

self as default receiver

You’re seeing more method calls without an explicit receiver; there’s no left-hand object and no dot in attr_reader, for example. In the absence of an explicit receiver, messages go to self, the default object. In the topmost level of a class definition body, self is the class object itself. So the object receiving the attr_reader message is the actual class object Ticket. We’ll go into more depth about classes as objects and thus as message receivers later in this chapter, and into more depth about self in chapter 5.

The attr_reader (attribute reader) method automatically writes for you the kind of method we’ve just been looking at. And there’s an attr_writer method, too:

class Ticket
attr_writer :price
end

With that single line, we wrote (or, rather, Ruby wrote for us) our price= setter method. One line takes the place of three. In the case of the reader methods, one line took the place of nine!

The whole program now looks like the following listing.

Listing 3.3. Ticket class, with getter and setter methods defined via attr_* calls

class Ticket
attr_reader :venue, :date, :price
attr_writer :price
def initialize(venue, date)
@venue = venue
@date = date
end
end

Not only is the code in listing 3.3 shorter, it’s also more informative—self-documenting, even. You can see at a glance that each ticket object has a venue, date, and price. The first two are readable attributes, and price can be read or written.

You can even create reader and writer methods with one command.

Creating reader/writer attributes with attr_accessor

In the realm of object attributes, combination reader/writer attributes like price are common. Ruby provides a single method, attr_accessor, for creating both a reader and a writer method for an attribute. attr_accessor is the equivalent of attr_reader plus attr_writer. We can use this combined technique for price, because we want both operations:

class Ticket
attr_reader :venue, :date
attr_accessor :price
# ... etc.
end

Alternately, you can achieve attr_accessor functionality with the plain attr method, as follows:

attr :price, true

Calling attr with true as the second argument triggers the creation of both reader and writer attributes, like attr_accessor. But attr_accessor is clearer in its intention—the word “accessor” tells you what’s going on—and it also has the advantage that you can give it more than one accessor name at a time (whereas attr takes only one, plus the optional true argument). Without the second argument of true, attr just provides a reader attribute.

3.4.2. Summary of attr_* methods

The attr_* family of methods is summarized in table 3.1.

Table 3.1. Summary of the attr_* family of getter/setter creation methods

Method name

Effect

Example

Equivalent code

attr_reader

Creates a reader method

attr_reader :venue

def venue
@venue
end

attr_writer

Creates a writer method

attr_writer :price

def price=(price)
@price = price
end

attr_accessor

Creates reader and writer methods

attr_accessor :price

def price=(price)
@price = price
end

def price
@price
end

attr

Creates a reader and optionally a writer method (if the second argument is true)

1. attr :venue

2. attr :price, true

1. See attr_reader

2. See attr_accessor

In all cases, the attr_ techniques have the effect of writing one or more get and/or set methods for you. They’re a powerful set of coding shortcuts.

Let’s zoom back out to a broader view of classes—specifically, to the matter of class inheritance.

3.5. Inheritance and the Ruby class hierarchy

Inheritance is a kind of downward-chaining relationship between two classes (the superclass and the subclass), whereby one class “inherits” from another and the instances of the subclass acquire the behaviors—the methods—defined in the superclass.

In this example, Magazine inherits from Publication. Note the syntax in Magazine’s class definition:

class Publication
attr_accessor :publisher
end

class Magazine < Publication
attr_accessor :editor
end

The symbol < designates Magazine as a subclass of Publication. Because every publication object has publisher and publisher= methods (thanks to attr_accessor :publisher), every magazine object has those methods too. In addition, magazine objects have editor and editor= methods:

mag = Magazine.new
mag.publisher = "David A. Black"
mag.editor = "Joe Smith"
puts "Mag is published by #{mag.publisher}, and edited by #{mag.editor}."

We can continue the cascade downward:

class Ezine < Magazine
end

Instances of Ezine have both publisher and editor attributes, as defined in the superclass and super-superclass of Ezine. Note that it’s not mandatory to add new methods to every subclass. You might want to create an Ezine class just for the sake of being able to call Ezine.new rather thanMagazine.new, to make your code more expressive.

Of course it’s not all about attribute accessor methods. Any instance method you define in a given class can be called by instances of that class, and also by instances of any subclasses of that class:

In this example, the Rubyist class descends from Person . That means a given Rubyist instance, such as david, can call the species method that was defined in the Person class . As always in Ruby, it’s about objects: what a given object can and can’t do at a given point in the program. Objects get their behaviors from their classes, from their individual or singleton methods, and also from the ancestors (superclass, super-superclass, and so on) of their classes (and from one or two places we haven’t looked at yet). All in all, Ruby objects lead interesting and dynamic lives. Inheritance is part of that picture.

Inheritance has an important limitation, though.

3.5.1. Single inheritance: One to a customer

In some object-oriented languages, it’s possible for a given class to inherit from more than one class. You might, for example, have a Teacher class that inherits from a Person class and also inherits from an Employee class, or a Car class that inherits from Machine, Powered, and Driveable. Ruby doesn’t allow multiple inheritance; every Ruby class can have only one superclass, in keeping with the principle of single inheritance.

Despite what might be your first impression, Ruby’s single inheritance doesn’t restrict you: Ruby provides modules, which are bundles of programming functionality similar to classes (except that they don’t have instances), that you can easily graft onto your class’s family tree to provide as many methods for your objects as you need. (Chapter 4 will focus on modules.) There’s no limit to how richly you can model your objects—it just can’t be done strictly with classes and inheritance.

The single inheritance principle means that you can’t just draw a big tree of entities and then translate the tree directly into a class hierarchy. Inheritance often functions more as a convenient way to get two or more classes to share method definitions than as a definitive statement of how real-world objects relate to each other in terms of generality and specificity. There’s some of that involved; every class in Ruby, for example, ultimately descends (as subclass or sub-subclass, and so on) from the Object class, and obviously Object is a more general class than, say, String or Ticket. But the single-inheritance limitation means that you can’t bank on designing a hierarchy of classes that cascade downward in strict tree-graph fashion.

Again, modules play a key role here, and they’ll get their due in chapter 4. For now, though, we’ll follow the thread of inheritance upward, so to speak, and look at the classes that appear at the top of the inheritance tree of every Ruby object: the Object and BasicObject classes.

3.5.2. Object ancestry and the not-so-missing link: The Object class

You’ve seen the standard technique for creating a generic object:

obj = Object.new

You’re now in a position to understand more deeply what’s going on in this snippet.

The class Object is almost at the top of the inheritance chart. Every class is either a subclass of Object, a sub-subclass of Object, or, at some distance, a direct descendant of Object:

class C
end

class D < C
end

puts D.superclass
puts D.superclass.superclass

The output is

C
Object

because C is D’s superclass (that’s our doing) and Object is C’s superclass (that’s Ruby’s doing).

If you go up the chain far enough from any class, you hit Object. Any method available to a bare instance of Object is available to every object; that is, if you can do

obj = Object.new
obj.some_method

then you can call some_method on any object.

There’s that “almost,” though. There is, as it turns out, another generation at the top.

3.5.3. El Viejo’s older brother: BasicObject

My father’s younger brother, now an 85-year-old great-grandfather, is known to his descendants as El Viejo: The Old Man. This presented my cousin with a conundrum: namely, how to explain to his little daughter—El Viejo’s granddaughter—exactly who my father was, the first time she met him. In the end, he took the bull by the horns and introduced my father to his great-niece as “El Viejo’s older brother.”

The BasicObject class, like my late father in his time, is older than old: it comes before Object in the Ruby class family tree. The idea behind BasicObject is to offer a kind of blank-slate object—an object with almost no methods. (Indeed, the precedent for BasicObject was a library by Jim Weirich called BlankSlate.) BasicObjects have so few methods that you’ll run into trouble if you create a BasicObject instance in irb:

>> BasicObject.new
(Object doesn't support #inspect)

The object gets created, but irb can’t display the customary string representation of it because it has no inspect method!

A newly created BasicObject instance has only 8 instance methods—whereas a new instance of Object has 55. (These numbers may change a little among different versions or releases of Ruby, but they’re accurate enough to make the point about BasicObject having few methods.) You’re not likely to need to instantiate or subclass BasicObject on a regular basis, if ever. It’s mainly handy for situations where you’re modeling objects closely to some particular domain, almost to the point of writing a kind of Ruby dialect, and you don’t want any false positives when you send messages to those objects. The 55 methods can get in the way, if you have your own ideas about whether your objects should play dumb when you send them messages like display, extend, or clone. (There’ll be more to say about this when we take up the topic thread of BasicObject inchapter 13.)

Having put inheritance into the mix and looked at some of the key components of the lineage of Ruby objects, let’s return to the subject of classes—specifically, to one of the most striking aspects of classes: the fact that they are objects and can therefore serve as receivers of messages, just like other objects.

3.6. Classes as objects and message receivers

Classes are special objects: they’re the only kind of object that has the power to spawn new objects (instances). Nonetheless, they’re objects. When you create a class, like Ticket, you can send messages to it, add methods to it, pass it around to other objects as a method argument, and generally do anything to it you would to another object.

Like other objects, classes can be created—indeed, in more than one way.

3.6.1. Creating class objects

Every class—Object, Person, Ticket—is an instance of a class called Class. As you’ve already seen, you can create a class object with the special class keyword formula:

class Ticket
# your code here
end

That formula is a special provision by Ruby—a way to make a nice-looking, easily accessible class-definition block. But you can also create a class the same way you create most other objects, by sending the message new to the class object Class:

my_class = Class.new

In this case, the variable my_class is assigned a new class object.

Class.new corresponds precisely to other constructor calls like Object.new and Ticket.new. When you instantiate the class Class, you create a class. That class, in turn, can create instances of its own:

instance_of_my_class = my_class.new

In section 3.1, you saw that class objects are usually represented by constants (like Ticket or Object). In the preceding scenario, the class object is bound to a regular local variable (my_class). Calling the new method sends the message new to the class through that variable.

Defining instance methods in connection with Class.new

If you want to create an anonymous class using Class.new, and you also want to add instance methods at the time you create it, you can do so by appending a code block after the call to new. A code block is a fragment of code that you supply as part of a method call, which can be executed from the method. You’ll see much more about code blocks when we look at iterators in chapter 6. Meanwhile, here’s a small example of Class.new with a block:

c = Class.new do
def say_hello
puts "Hello!"
end
end

If you now create an instance of the class (with c.new), you’ll be able to call the method say_hello on that instance.

And yes, there’s a paradox here...

The class/object chicken-or-egg paradox

The class Class is an instance of itself—that is, it’s a Class object. And there’s more. Remember the class Object? Well, Object is a class—but classes are objects. So, Object is an object. And Class is a class. And Object is a class, and Class is an object.

Which came first? How can the class Class be created unless the class Object already exists? But how can there be a class Object (or any other class) until there’s a class Class of which there can be instances?

The best way to deal with this paradox, at least for now, is to ignore it. Ruby has to do some of this chicken-or-egg stuff to get the class and object system up and running—and then the circularity and paradoxes don’t matter. In the course of programming, you just need to know that classes are objects, instances of the class called Class. (If you want to know in brief how it works, it’s like this: every object has an internal record of what class it’s an instance of, and the internal record inside the object Class points back to Class itself.)

Classes are objects, and objects receive messages and execute methods. How exactly does the method-calling process play out in the case of class objects?

3.6.2. How class objects call methods

When you send a message to a class object, it looks like this:

Ticket.some_message

Or, if you’re inside a class-definition body and the class is playing the role of the default object self, it looks like this:

That’s how the class object gets messages. But where do the methods come from to which the messages correspond?

To understand where classes get their methods, think about where objects in general get their methods (minus modules, which we haven’t explored yet):

· From their class

· From the superclass and earlier ancestors of their class

· From their own store of singleton methods (the “talk” in def obj.talk)

The situation is basically the same for classes. There are some, but very few, special cases or bells and whistles for class objects. Mostly they behave like other objects.

Let’s look at the three scenarios for method calling just listed, in the case of class objects.

Instances of Class can call methods that are defined as instance methods in their class. Ticket, for example, is an instance of Class, and Class defines an instance method called new. That’s why we can write

Ticket.new

That takes care of scenario 1. Now, scenario 2.

The superclass of Class is Module. Instances of Class therefore have access to the instance methods defined in Module; among these are the attr_accessor family of methods. That’s why we can write

class Ticket
attr_reader :venue, :date
attr_accessor :price

Those method calls go directly to the class object Ticket, which is in the role of the default object self at the point when the calls are made.

That leaves just scenario 3: calling a singleton method of a class object.

3.6.3. A singleton method by any other name...

Here’s an example. Let’s say we’ve created our Ticket class. At this point, Ticket isn’t only a class from which objects (ticket instances) can arise. Ticket (the class) is also an object in its own right. As we’ve done with other objects, let’s add a singleton method to it. Our method will tell us which ticket, from a list of ticket objects, is the most expensive. There’s some black-box code here. Don’t worry about the details; the basic idea is that the max_by operation will find the ticket whose price is highest:

def Ticket.most_expensive(*tickets)
tickets.max_by(&:price)
end

Now we can use the Ticket.most_expensive method to tell which of several tickets is the most expensive. (We’ll avoid having two tickets with the same price, because our method doesn’t deal gracefully with that situation.)

th = Ticket.new("Town Hall","11/12/13")
cc = Ticket.new("Convention Center","12/13/14/")
fg = Ticket.new("Fairgrounds", "13/14/15/")
th.price = 12.55
cc.price = 10.00
fg.price = 18.00
highest = Ticket.most_expensive(th,cc,fg)
puts "The highest-priced ticket is the one for #{highest.venue}."

The output is

The highest-priced ticket is the one for Fairgrounds.

The method most_expensive is defined directly on the class object Ticket, in singleton-method style. A singleton method defined on a class object is commonly referred to as a class method of the class on which it’s defined. The idea of a class method is that you send a message to the object that’s the class rather than to one of the class’s instances. The message most_expensive goes to the class Ticket, not to a particular ticket.

The term class method: More trouble than it’s worth?

Ruby lets objects have singleton methods, and classes are objects. So when you do def Ticket.most_expensive, you’re basically creating a singleton method for Ticket. On the calling side, when you see a method called on a class object—like Ticket.new—you can’t tell just by looking whether you’re dealing with a singleton method defined directly on this class (def Ticket.new) or an instance method of the class Class.

Just to make it even more fun, the class Class has both a class-method version of new and an instance-method version; the former is called when you write Class.new and the latter when you write Ticket.new. Unless, of course, you override it by defining new for Ticket yourself...

Admittedly, new is a particularly thorny case. But in general, the term class method isn’t necessarily a great fit for Ruby. It’s a concept shared with other object-oriented languages, but in those languages there’s a greater difference between class methods and instance methods. In Ruby, when you send a message to a class object, you can’t tell where and how the corresponding method was defined.

So class method has a fuzzy meaning and a sharp meaning. Fuzzily, any method that gets called directly on a Class object is a class method. Sharply, a class method is defined, not just called, directly on a Class object. You’ll hear it used both ways, and as long as you’re aware of the underlying engineering and can make the sharp distinctions when you need to, you’ll be fine.

Why would you want to do that? Doesn’t it mess up the underlying order—that is, the creation of ticket objects and the sending of messages to those objects?

3.6.4. When, and why, to write a class method

Class methods serve a purpose. Some operations pertaining to a class can’t be performed by individual instances of that class. The new method is an excellent example. We call Ticket.new because, until we’ve created an individual ticket, we can’t send it any messages! Besides, the job of spawning a new object logically belongs to the class. It doesn’t make sense for instances of Ticket to spawn each other. But it does make sense for the instance-creation process to be centralized as an activity of the class Ticket.

Another similar case is the built-in Ruby method File.open—a method that, as you saw in chapter 1, opens a file for reading and/or writing. The open operation is a bit like new; it initiates file input and/or output and returns a File object. It makes sense for open to be a class method of File: you’re requesting the creation of an individual object from the class. The class is acting as a point of departure for the objects it creates.

Ticket.most_expensive is a different case, in that it doesn’t create a new object—but it’s still a method that belongs logically to the class. Finding the most expensive ticket in a list of tickets can be viewed as an operation from above, something that’s done collectively with respect to tickets, rather than something that’s done by an individual ticket object. Writing most_expensive as a class method of Ticket lets us keep the method in the ticket family, so to speak, while assigning it to the abstract, supervisory level represented by the class.

It’s not unheard of to create a class only for the purpose of giving it class methods. Our earlier temperature-conversion exercises offer an opportunity for using this approach.

Converting the converter

Let’s convert the converter to a converter class, adding class methods for conversion in both directions:

class Temperature
def Temperature.c2f(celsius)
celsius * 9.0 / 5 + 32
end

def Temperature.f2c(fahrenheit)
(fahrenheit - 32) * 5 / 9.0
end
end

And let’s try it out:

The idea is that we have temperature-related utility methods—methods pertaining to temperature as a concept but not to a specific temperature. The Temperature class is a good choice of object to own those methods. We could get fancier and have Temperature instances that knew whether they were Celsius or Fahrenheit and could convert themselves; but practically speaking, having a Temperature class with class methods to perform the conversions is adequate and is an acceptable design. (Even better, because we don’t need instances of Temperature at all, would be to use a module—a kind of “instanceless” class, which you’ll learn about in detail in chapter 4.)

Class methods and instance methods aren’t radically different from each other; they’re all methods, and their execution is always triggered by sending a message to an object. It’s just that the object getting the message may be a class object. Still, there are differences and important points to keep in mind as you start writing methods at various levels.

3.6.5. Class methods vs. instance methods

By defining Ticket.most_expensive, we’ve defined a method that we can access through the class object Ticket but not through its instances. Individual ticket objects (instances of the class Ticket) don’t have this method. You can test this easily. Try adding this to the code from section 3.6.3, where the variable fg referred to a Ticket object (for an event at the fairgrounds):

puts "Testing the response of a ticket instance...."
wrong = fg.most_expensive

You get an error message, because fg has no method called most_expensive. The class of fg, namely Ticket, has such a method. But fg, which is an instance of Ticket, doesn’t.

Remember:

· Classes are objects.

· Instances of classes are objects, too.

· A class object (like Ticket) has its own methods, its own state, and its own identity. It doesn’t share these things with instances of itself. Sending a message to Ticket isn’t the same thing as sending a message to fg or cc or any other instance of Ticket.

If you ever get tangled up over what’s a class method and what’s an instance method, you can usually sort out the confusion by going back to these three principles.

A note on method notation

In writing about and referring to Ruby methods (outside of code, that is), it’s customary to refer to instance methods by naming the class (or module, as the case may be) in which they’re defined, followed by a hash mark (#) and the name of the method; and to refer to class methods with a similar construct but using a period instead of the hash mark. Sometimes you’ll see a double colon (::) instead of a period in the class-method case.

Here are some examples of this notation and what they refer to:

· Ticket#price refers to the instance method price in the class Ticket.

· Ticket.most_expensive refers to the class method most_expensive in the class Ticket.

· Ticket::most_expensive also refers to the class method most_expensive in the class Ticket.

From now on, when you see this notation (in this book or elsewhere), you’ll know what it means. (The second example—class-method reference using a dot—looks the same as a call to the method, but you’ll know from the context whether it’s a method call or a reference to the method in a discussion.)

Discussion of classes always entails the use of a lot of constants—and so will the upcoming discussion of modules in chapter 4. So let’s take a deeper look than we have so far at what constants are and how they work.

3.7. Constants up close

Many classes consist principally of instance methods and/or class methods. But constants are an important and common third ingredient in many classes. You’ve already seen constants used as the names of classes. Constants can also be used to set and preserve important data values in classes.

Later, we’ll look at the scope of constants and techniques for nesting them inside multilevel classes and modules. For now, we’ll focus on the basics of how to use them—and the question of how constant these constants really are.

3.7.1. Basic use of constants

The name of every constant begins with a capital letter. You assign to constants much as you do to variables.

Let’s say we decide to establish a list of predefined venues for the Ticket class—a list that every ticket object can refer to and select from. We can assign the list to a constant. Constant definitions usually go at or near the top of a class definition:

class Ticket
VENUES = ["Convention Center", "Fairgrounds", "Town Hall"]

A constant defined in a class can be referred to from inside the class’s instance or class methods. Let’s say you wanted to make sure that every ticket was for a legitimate venue. You could rewrite the initialize method like this:

It’s also possible to refer to a constant from outside the class definition entirely, using a special constant lookup notation: a double colon (::). Here’s an example of setting a constant inside a class and then referring to that constant from outside the class:

The double-colon notation pinpoints the constant VENUES inside the class known by the constant Ticket, and the list of venues is printed out.

Ruby comes with some predefined constants that you can access this way and that you may find useful.

Ruby’s predefined constants

Try typing this into irb:

Math::PI

Math is a module (the subject of chapter 4), but the principle is the same as in the case of a constant defined inside a class: you use the :: connector to do a lookup on the constant PI defined in the Math module. You can look up E the same way.

Many of the predefined constants you can examine when you start up Ruby (or irb) are the names of the built-in classes: String, Array, Symbol, and so forth. Some are informational; even without loading the rbconfig package (which you saw in chapter 1), you can get the interpreter to tell you a fair amount about its settings. Here are some examples:

>> RUBY_VERSION
=> "2.1.0"
>> RUBY_PATCHLEVEL
=> 0
>> RUBY_RELEASE_DATE
=> "2013-12-25"
>> RUBY_REVISION
=> 44422
>> RUBY_COPYRIGHT
=> "ruby - Copyright (C) 1993-2013 Yukihiro Matsumoto"

As you can see, the information stored in these constants corresponds to the information you get with the -v switch:

$ ruby -v
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin12.0]

One peculiarity of Ruby constants is that they aren’t constant. You can change them, in two senses of the word change—and therein lies an instructive lesson.

3.7.2. Reassigning vs. modifying constants

It’s possible to perform an assignment on a constant to which you’ve already assigned something—that is, to reassign to the constant. But you get a warning if you do this (even if you’re not running with the -w command-line switch). Try this in irb:

A = 1
A = 2

You’ll receive the following message:

(irb):2: warning: already initialized constant A
(irb):1: warning: previous definition of A was here

The fact that constant names are reusable while the practice of reusing them is a warnable offense represents a compromise. On the one hand, it’s useful for the language to have a separate category for constants, as a way of storing data that remains visible over a longer stretch of the program than a regular variable. On the other hand, Ruby is a dynamic language, in the sense that anything can change during runtime. Engineering constants to be an exception to this would theoretically be possible, but would introduce an anomaly into the language.

In addition, because you can reload program files you’ve already loaded, and program files can include constant assignments, forbidding reassignment of constants would mean that many file-reloading operations would fail with a fatal error.

So you can reassign to a constant, but doing so isn’t considered good practice. If you want a reusable identifier, you should use a variable.

The other sense in which it’s possible to “change” a constant is by making changes to the object to which the constant refers. For example, adding a venue to the Ticket class’s venue list is easy:

There’s no warning, because there’s no redefinition of a constant. Rather, we’re modifying an array—and that array has no particular knowledge that it has been assigned to a constant. It just does what you ask it to.

The difference between reassigning a constant name and modifying the object referenced by the constant is important, and it provides a useful lesson in two kinds of change in Ruby: changing the mapping of identifiers to objects (assignment) and changing the state or contents of an object. With regular variable names, you aren’t warned when you do a reassignment; but reassignment is still different from making changes to an object, for any category of identifier.

If you put together the topics in this chapter with some of the examples you’ve seen previously, you start to get a good overall picture of how Ruby objects are engineered: they derive their functionality from the instance methods defined in their classes and the ancestors of those classes, but they’re also capable of “learning” specific, individualized behaviors in the form of singleton methods. This is what makes Ruby so fascinating. The life of a Ruby object is, at least potentially, a mixture of the circumstances of its “birth” and the traits it acquires across its lifetime. We’ll wrap up this chapter with some further exploration along these important lines.

3.8. Nature vs. nurture in Ruby objects

The relation between classes and their instances is essentially a relation between the general and the specific—a familiar pattern from the world at large. We’re used to seeing the animal kingdom in general/specific terms, and likewise everything from musical instruments to university departments to libraries’ shelving systems to pantheons of gods.

To the extent that a programming language helps you model the real world (or, conversely, that the real world supplies you with ways to organize your programs), you could do worse than to rely heavily on the general-to-specific relationship. As you can see, inheritance—the superclass-to-subclass relationship—mirrors the general/specific ratio closely. Moreover, if you hang out in object-oriented circles you’ll pick up some shorthand for this relationship: the phrase is a. If, say, Ezine inherits from Magazine, we say that “an ezine is a magazine.” Similarly, a Magazine object is aPublication, if Magazine inherits from Publication.

Ruby lets you model this way. You can get a lot of mileage out of thinking through your domain as a cascaded, inheritance-based chart of objects. Ruby even provides an is_a? method that tells you whether an object has a given class either as its class or as one of its class’s ancestral classes:

>> mag = Magazine.new
=> #<Magazine:0x36289c>
>> mag.is_a?(Magazine)
=> true
>> mag.is_a?(Publication)
=> true

Organizing classes into family trees of related entities, with each generation a little more specific than the last, can confer a pleasing sense of order and determinism on your program’s landscape.

But Ruby objects (unlike objects in some other object-oriented languages) can be individually modified. An instance of a given class isn’t stuck with only the behaviors and traits that its class has conferred upon it. You can always add methods on a per-object basis, as you’ve seen in numerous examples. Furthermore, classes can change. It’s possible for an object to gain capabilities—methods—during its lifetime, if its class or an ancestral class acquires new instance methods.

In languages where you can’t add methods to individual objects or to classes that have already been written, an object’s class (and the superclass of that class, and so forth) tells you everything you need to know about the object. If the object is an instance of Magazine, and you’re familiar with the methods provided by the class Magazine for the use of its instances, you know exactly how the object behaves.

But in Ruby the behavior or capabilities of an object can deviate from those supplied by its class. We can make a magazine sprout wings:

This demonstrates that the capabilities the object was born with aren’t necessarily the whole story.

Thus the inheritance tree—the upward cascade of class to superclass and super-superclass—isn’t the only determinant of an object’s behavior. If you want to know what a brand-new magazine object does, look at the methods in the Magazine class and its ancestors. If you want to know what a magazine object can do later, you have to know what’s happened to the object since its creation. (And respond_to?—the method that lets you determine in advance whether an object knows how to handle a particular method—can come in handy.)

Ruby objects are tremendously flexible and dynamic. That flexibility translates into programmer power: you can make magazines fly, make cows tell you who published them, and all the rest of it. As these silly examples make clear, the power implies responsibility. When you make changes to an individual object—when you add methods to that object, and that object alone—you must have a good reason.

Most Ruby programmers are conservative in this area. You’ll see less adding of methods to individual objects than you might expect. The most common use case for adding methods directly to objects is the adding of class methods to class objects. The vast majority of singleton-style method definitions you’ll see (def some_object.some_method) will be class-method definitions. Adding methods to other objects (magazines, tickets, cows, and so on) is also possible—but you have to do it carefully and selectively, and with the design of the program in mind.

In most cases, object individuation (the subject of the entirety of chapter 13, by the way) has to do with dynamically determined conditions at runtime; for example, you might add accessor methods to objects to match the names of database columns that you don’t know until the program is running and you’ve queried the database. Or you might have a library of special methods that you’ve written for string objects, and that you want only certain strings to have access to. Ruby frees you to do these things, because an object’s class is only part of the story—its nature, you might say, as opposed to its nurture.

And there’s another piece to the puzzle: modules, a Ruby construct you’ve seen mentioned here several times in passing, which you’ll meet up close and in depth in the next chapter.

3.9. Summary

In this chapter, you’ve learned the basics of Ruby classes:

· How writing a class and then creating instances of that class allow you to share behaviors among numerous objects.

· How to use setter and getter methods, either written out or automatically created with the attr_* family of methods, to create object attributes, which store an object’s state in instance variables.

· As objects, classes can have methods added to them on a per-object basis—such methods being commonly known as class methods, and providing general utility functionality connected with the class.

· Ruby constants are a special kind of identifier usually residing inside class (or module) definitions.

· Inheritance is a class-to-class relationship between a superclass and one or more subclasses, and all Ruby objects have a common ancestry in the Object and BasicObject classes.

· The superclass/subclass structure can lend itself to modeling entities in a strictly hierarchical, taxonomical way, but the dynamic qualities of Ruby objects (including class objects!) can offer less strictly determined ways of thinking about objects and how their behaviors might unfold over the course of their lives.

This look at classes gives you a firm foundation for understanding how objects come into being and relate to each other in Ruby. Next, we’ll build on that foundation by looking at modules, the other important building block of the object system.