---
title: 'An Intuition for the Object Model'
source: 'https://academia.sh/en/courses/ruby-fundamentals/the-object-model'
course: 'Ruby Fundamentals'
language: en
updated: '2026-10-21T05:03:43+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Shows by running code that values such as numbers, nil, and true are objects belonging to a class'
    - 'Separates the receiver from the responder in a message and finds the responder in 5 + 3 with an owner measurement'
    - 'Measures that classes are objects too and that another class answers the message sent to them'
---

# An Intuition for the Object Model

Numbers, nil, and true are objects too; in 5 + 3 the receiver is 5 and the responder is Integer; classes are objects themselves, and another class answers the messages sent to them.

The previous lesson established that every operation is a message sent to a receiver, and showed within the twelve observations that the `+` of `5 + 3` is defined in the `Integer` class. That sentence assumed something: `5` is an object and belongs to a class. This lesson measures that assumption. Primitive types were measured in the JavaScript Fundamentals course (M06/K01); in Ruby the same question is asked in a different form: if a message can be sent to a value, who handles that message?

Object orientation was established in M08 with Java, and the concepts of object and class were defined there. What is new here is that in Ruby numbers, `nil`, and truth values are inside the same model. In addition, this lesson takes a single row from the core: `integer addition`.

## Every Value Is an Object

Ruby has no separate category of primitive values. The file below sends the same message, `class`, to eight different values. On each line the representation of the value and its class are printed side by side.

```ruby
# values.rb — every value has a class
values = [5, 3.5, "name", :name, nil, true, [1], 1..2]

values.each do |value|
  printf("%-6s %s\n", value.inspect, value.class)
end
```

```
5      Integer
3.5    Float
"name" String
:name  Symbol
nil    NilClass
true   TrueClass
[1]    Array
1..2   Range
```

The integer, the floating-point number, the string, the symbol, `nil`, the truth value, the array, and the range all come from a class. `nil` and `true` are the single instance of a class: `NilClass` and `TrueClass`. This means messages can be sent to values. The file below sends one message each to a number, to `true`, and to `nil`, and asks the owner of each method.

```ruby
# primitive_messages.rb — numbers, true, and nil answer messages
puts 5.times.class
puts 5.method(:times).owner
puts true.method(:&).owner
puts nil.method(:to_a).owner
p nil.to_a
p true & false
```

```
Enumerator
Integer
TrueClass
NilClass
[]
false
```

`5.times` returns an enumerator, and its owner is `Integer`. `true & false` looks like an operator but is a method defined in `TrueClass`. `nil.to_a` gives the empty array, and its owner is `NilClass`. All three messages found their answer in the receiver's own class.

**LB5.** In this lesson, an "object" is any value a message can be sent to. How a value is held in memory is not measured; only the behavior is measured, that is, who responds to which message. For this reason `object_id` and `inspect` output that carries a memory address are not printed.

## Receiver and Responder

In the expression `5 + 3`, the receiver is `5`, the message is `+`, and the argument is `3`. M11/K04 measured which object is the target of an operation. Here the receiver is known; the question is which definition answers the message that reaches the receiver. In `5 + 3` the receiver is `5` and the responder is `Integer`.

```ruby
# receiver_responder.rb — in 5 + 3 the receiver and the responder are different things
receiver = 5

puts defined?(receiver + 3)
puts receiver.method(:+).owner
puts receiver.method(:+).owner.instance_of?(Class)
puts receiver + 3
```

```
method
Integer
true
8
```

`defined?` returned `"method"` for the expression: there really is a message here. The owner of the method is `Integer`, and `Integer` is an instance of `Class`; in this case the classification rule of the course counts the responder as a class. The receiver is a value, the responder is a definition. The two often do not carry the same name: `5` is a value, and `Integer` is the class it belongs to.

This line is the `integer addition` observation in the shared core of the course, and, as in the whole core, its responder is a class. This lesson does not pull in `reply.rb`; the `defined?` result and the owner of the method are printed directly. The rule is the same: first `defined?`, then the owner.

## Classes Are Objects Too

Since every value has a class, `Integer` must have a class too. In Ruby `Integer` is a constant and points to an object; that object can answer a message as well. But who answers when a message is sent to `Integer`? The file below prints the class of `5` and of `Integer`, and then the owner of the `===` message sent to `Integer`.

```ruby
# class_objects.rb — classes are objects, and another class answers their messages
puts 5.class
puts Integer.class
puts Integer.method(:===).owner
puts Integer.method(:===).owner.instance_of?(Class)
puts Integer === 5
puts Integer === "5"
```

```
Integer
Class
Module
true
true
false
```

The class of `5` is `Integer`; the class of `Integer` is `Class`. So `Integer` is an object in its own right, and the `===` message sent to it is answered not by `Integer` but by `Module`. `Module` is a class too, so the rule again counts the responder as a class. `Integer === 5` returns true and `Integer === "5"` returns false: here `===` asks "does this value belong to this class".

## A Message Depends on the Receiver's Class

That every value has a class means that not every value can answer every message. The `times` message is defined in `Integer`; it is not in `NilClass`, the class of `nil`. The file below asks three messages of two receivers, and looks at what happens when `times` is sent to `nil`.

```ruby
# message_probe.rb — a message depends on the receiver's class
[5, nil].each do |receiver|
  printf("%-4s times: %-5s to_a: %-5s to_s: %s\n", receiver.inspect, receiver.respond_to?(:times), receiver.respond_to?(:to_a), receiver.respond_to?(:to_s))
end

begin
  nil.times
rescue NoMethodError => e
  puts e.class
end
```

```
5    times: true  to_a: false to_s: true
nil  times: false to_a: true  to_s: true
NoMethodError
```

`respond_to?` asks whether an object answers a message. `5` answers the `times` and `to_s` messages; `nil` answers `to_a` and `to_s` but does not answer `times`. Sending a message that is not answered raises `NoMethodError`, and here only the class of the exception was printed. So the sentence "everything is an object" does not mean "every object accepts every message": the counterpart of a message must be found in the receiver's class or in the definitions above that class.

## Probing That Everything Is an Object

The previous sections built the object model through the receiver, the responder, and the class. This section tests the claim "everything is an object" one by one, on numbers, `nil`, and the classes themselves. The limit of the claim is also measured: the two queries that ask what kind of thing a value is, `instance_of?` and `is_a?`, do not ask the same question. The axis is again the responder; the owner of one of the queries is read on the last line of the output.

```ruby
# everything_object.rb — there is no value that is not an object
puts 5.respond_to?(:+)
puts nil.respond_to?(:to_a)
puts 5.frozen?
puts 5.methods.size > 50
puts Integer.class
puts Class.class
puts 5.instance_of?(Integer)
puts 5.is_a?(Comparable)
puts 5.instance_of?(Comparable)
puts 5.method(:frozen?).owner
```

```
true
true
true
true
Class
Class
true
true
false
Kernel
```

The first two lines gave `true`: `5` answers the addition message, and `nil` answers the `to_a` message. `5.frozen?` also returned `true`; a number is an immutable object. The question `5.methods.size > 50` was `true`. Here we deliberately did not print the number itself: how many methods an object has changes with the version, but the result that it has quite a few does not. The fifth and sixth lines are the real finding of the section: `Integer.class` and `Class.class` both printed `Class`. `Integer` is a class and an object; the class of that object is `Class`. `Class` itself is an object too, and its class is also `Class`. The chain closes on itself, so the question "what is the class of a class" does not go on forever.

The last three queries show the difference between two methods. `5.instance_of?(Integer)` gave `true`, `5.is_a?(Comparable)` gave `true`, and `5.instance_of?(Comparable)` gave `false`. `instance_of?` asks only about the object's direct class; `is_a?` asks the broader question of whether the object counts as being of that kind. We do not open up how the chain works here; we only record the difference. The owner on the last line turned out to be `Kernel`: the `5.frozen?` message is answered not by the number's class but by a module that every object takes in common.

**LB6.** The `> 50` threshold is a choice: the measurement only shows that the number is above the threshold, and the number itself is not a promise of the language.

```ruby
# nil_object.rb — nil is an object too and has an owner
puts nil.class
puts nil.to_a.inspect
puts nil.to_s.empty?
puts nil.method(:to_a).owner
puts true.class
puts true.method(:&).owner
```

```
NilClass
[]
true
NilClass
TrueClass
TrueClass
```

`nil` is an object too: `nil.class` gave `NilClass`. `nil.to_a` returned an empty array, and because `nil.to_s` gives an empty string, the question `empty?` was `true`. So even the "no value" state responds to messages, and this is why calling `to_a` on `nil` produces `[]` rather than an error. `nil.method(:to_a).owner` turned out to be `NilClass`: the responder is `nil`'s own class. The same pattern holds for `true`: its class is `TrueClass`, and the owner of the `&` message is `TrueClass`. The classes of `true` and `nil` are separate, and the owners of the two measured messages turned out to be those two classes themselves. The common conclusion of these two sections tightens the intuition at the start of the lesson: in Ruby no value is closed to messages. Whatever the value of an expression is, a message can be written after it, and the responder can be found as a class or a module.

## Chained Messages and the Return Object

Every message returns an object, and the returned object becomes the receiver of the next message. This section measures that at each link of the chain the responder can change. The expression measured is a chain such as `5.to_s.size.to_f`; the chain has to be read from the left: first `5` receives the message, the object that comes out receives the next one, and so it goes on. The axis is again the owner: the owners of two links are printed separately.

```ruby
# chain.rb — every message returns an object and the chain goes on
result = 5.to_s.size.to_f
puts result
puts result.class
puts 5.to_s.class
puts 5.to_s.size.class
puts [3, 1, 2].sort.reverse.first
puts "ruby".reverse.upcase.class
puts 5.method(:to_s).owner
puts 5.to_s.method(:size).owner
```

```
1.0
Float
String
Integer
3
String
Integer
String
```

The result of the chain was `1.0`, and its class is `Float`. Probing the intermediate links one by one shows how the chain is built: `5.to_s` gave a `String`, and `5.to_s.size` gave an `Integer`. So within the same expression there are four separate objects and three separate classes: `Integer` for `5`, `String` for `"5"`, `Integer` again for `1`, and finally `Float` for `1.0`. `[3, 1, 2].sort.reverse.first` went down to an element after two array links and gave `3`; `"ruby".reverse.upcase.class` printed `String`, because both links return strings.

The owner lines showed that two different links are defined in two different classes: `5.method(:to_s).owner` turned out to be `Integer`, and `5.to_s.method(:size).owner` turned out to be `String`. The same names `to_s` and `size` could have gone to a different owner for another receiver; the receiver's class determines the responder, not the position in the chain. This is the concrete counterpart of the claim "Ruby is object-oriented": an operation is sending a message to an object, and the answer is again an object. To read a chain, one has to know the class of the return object of each link; when the class changes, the next message goes to a different responder. So when an error occurs in a long chain, the way to find where it arose is to split the chain link by link and probe the class of each intermediate object.

## Equality and Identity Are Separate Questions

Equality and identity are two separate questions. `==` asks whether two objects are equal in value, and `equal?` asks whether the two are the same object. This section places the two queries side by side on the same values. The axis is the owner: the place where the method answering the two queries is defined is printed at the end.

```ruby
# identity.rb — equality and identity are separate questions
a = "x"
b = a
puts a.equal?(b)
puts a.equal?("x")
puts a == "x"
puts a.dup.equal?(a)
puts 1.equal?(1)
puts :s.equal?(:s)
puts nil.equal?(nil)
puts 1 == 1.0
puts 1.eql?(1.0)
puts 1.equal?(1.0)
puts a.method(:==).owner
puts a.method(:equal?).owner
```

```
true
false
true
false
true
true
true
true
false
false
String
BasicObject
```

After the assignment `b = a`, `a.equal?(b)` gave `true`: two names point to a single object. `a.equal?("x")` was `false` and `a == "x"` was `true`: the text is the same, the object is different. `a.dup.equal?(a)` also gave `false`, because `dup` produces a new object. A number, a symbol, and `nil` behaved differently: `1.equal?(1)`, `:s.equal?(:s)`, and `nil.equal?(nil)` all three gave `true`; every spelling of these values is the same object.

In comparisons among numbers, the three queries gave two different answers. `1 == 1.0` was `true`, while `1.eql?(1.0)` and `1.equal?(1.0)` gave `false`. So `==` looks at values and sees equality between an integer and a floating-point number; `eql?` and `equal?` ask stricter questions. The owner lines showed where the two queries are defined: `String` for `==` and `BasicObject` for `equal?`. So `==` is a question left to each class's own definition, while `equal?` is defined in a single common place. For this reason, before asking whether a value is "the same", one has to state in which sense it is asked. In conclusion, `equal?` is used for the identity question and `==` for the value question; confusing the two opens the way to taking two separate objects for the same one or the same object for two.

## The Bounding Measurement

In most of this lesson's measurements the responder is a class; `Kernel`, a module, is the exception for `class` and `frozen?`, and `BasicObject` for `equal?`. The expression "the class responder" can slip toward two different things. The slip goes like this: one may assume that the responder of the `Integer.===` message is `Integer`. The run above showed this is wrong. The responder is not the receiver itself but the chain of classes the receiver belongs to: if the receiver is `5`, it is `Integer`; if the receiver is `Integer`, it is `Module` in the chain of `Class`.

```ruby
# receivers_class.rb — the responder is the class the receiver belongs to, not the receiver itself
[5, "name", Integer, String].each do |receiver|
  printf("%-8s class %-8s method owner %s\n", receiver.inspect, receiver.class, receiver.method(:class).owner)
end
```

```
5        class Integer  method owner Kernel
"name"   class String   method owner Kernel
Integer  class Class    method owner Kernel
String   class Class    method owner Kernel
```

The owner of the `class` message is `Kernel` for all four receivers, because this message is defined in a module common to every object. The classes of the receivers, however, differ. The question "who answers this message" has two layers: which class the receiver belongs to, and whether that class answers the message itself. What was measured in this lesson is the first layer. The measurement of the second, the cases where the method is found not in the receiver's class but in a definition above it, continues in the Methods lesson, whose subject is the module responder.

## Summary

- In Ruby, numbers, `nil`, `true`, strings, symbols, arrays, and ranges are all objects; each has a class.
- In `5 + 3` the receiver is `5` and the responder is `Integer`; the receiver is a value, the responder is a definition.
- The messages `5.times`, `true & false`, and `nil.to_a` are answered by `Integer`, `TrueClass`, and `NilClass` respectively.
- Classes are objects too: `Integer.class` is `Class`, and the `Integer.===` message is answered by `Module`.
- The responder of this lesson is mostly a class: the message is answered not by the receiver itself but by the class the receiver belongs to. The owner of messages common to every object, such as `class` and `frozen?`, is the `Kernel` module.
- `Integer.class` and `Class.class` both give `Class`; `equal?` asks identity and `==` asks value, and the responder of each link in a chain is the class of that link's receiver.

## Next Step

It was seen that objects have a class and that messages are sent to them. But how is a name bound to an object, and who is at work when `x = 5` is written? Whether assignment is a message, the naming rules, and the sigils are the subject of the next lesson, Variables and Constants.
