Vol. 1 · № 02 · Un atelier RubyA Ruby workshop

Tout est objet (vraiment tout) Everything is an object (truly everything)

Si tout appel est un message à un receveur, qui peut recevoir ? La réponse de Ruby n'a aucune exception : 42, nil, true, et les classes elles-mêmes. Integer est une instance de Class, et Class une instance de Class. Une règle, zéro cas particulier. If every call is a message to a receiver, who can receive? Ruby's answer has no exception: 42, nil, true, and the classes themselves. Integer is an instance of Class, and Class an instance of Class. One rule, zero special cases.

AudienceAudience
Dev venant d'un langage à primitives, prêt à tout voir comme objet Dev from a language with primitives, ready to see all as objects
Format
Self-paced
ChapitresChapters
5
Date
Déc 2025 Dec 2025
≈ 18 min ●○○○ ObjetClassMétamodèle
SommaireContents ·
№ 01CadrageFraming3 min

Demande sa classe à n'importe quoi. Il répondra.Ask anything for its class. It will answer.

Au numéro précédent, tout appel était un message à un receveur. Restait une question : qui peut recevoir un message ? La réponse de Ruby est radicale — tout. Le nombre 42, la chaîne vide, nil, true : aucun n'est une « valeur primitive » à part. Tous répondent à .class, donc tous sont des objets. Sans exception.Last issue, every call was a message to a receiver. One question remained: who can receive a message? Ruby's answer is radical — everything. The number 42, the empty string, nil, true: none is a separate 'primitive value'. They all answer .class, so they are all objects. No exceptions.

Le code qui ne devrait « pas marcher »The code that 'shouldn't work'
42.class        # => Integer
3.14.class      # => Float
"hi".class      # => String
:sym.class      # => Symbol
nil.class       # => NilClass
true.class      # => TrueClass
(1..5).class    # => Range
# pas une seule exception : tout répond à .class
Tout littéral est un objetEvery literal is an object
42
↓
Integer
"hi"
↓
String
:sym
↓
Symbol
nil
↓
NilClass
true
↓
TrueClass

Là où d'autres langages ont des primitives qui ne sont pas des objets, Ruby n'en a aucune. nil lui-même a une classe — donc nil est un objet.Where other languages have primitives that aren't objects, Ruby has none. nil itself has a class — so nil is an object.

Le réflexe du volumeThe volume's reflex

« À quel objet est-ce que je parle ? » — et la réponse n'a plus d'exception. Si une chose existe en Ruby, c'est un objet : elle a une classe, elle répond à des messages, on peut la ranger dans une variable et la passer en argument. Y compris les choses qu'ailleurs on croit « à part »."Which object am I talking to?" — and the answer no longer has exceptions. If a thing exists in Ruby, it's an object: it has a class, it responds to messages, you can store it in a variable and pass it as an argument. Including the things you'd think are 'special' elsewhere.

Pourquoi ça vient juste après l'envoi.Why this comes right after the send.

Le № 1 a posé : tout appel est un message à un receveur. Ce numéro ferme la boucle : il n'existe rien qui ne soit pas un receveur valide. Les deux ensemble forment le socle — une seule règle, zéro cas particulier — sur lequel tiennent les blocs, les mixins et la méta.Issue № 1 established: every call is a message to a receiver. This issue closes the loop: there is nothing that isn't a valid receiver. Together they form the bedrock — one rule, zero special cases — on which blocks, mixins and metaprogramming stand.

№ 02La surpriseThe surprise4 min

Ailleurs, int et null sont à part. Ici, rien ne l'est.Elsewhere, int and null are set apart. Here, nothing is.

Dans un langage statique, on apprend très tôt une frontière : d'un côté les objets, de l'autre les primitives (int, boolean, null) qui n'ont pas de méthodes — et les classes, qui ne sont pas des valeurs qu'on balade. Ruby efface la frontière entière. Le nombre a des méthodes, nil a une classe, et une classe est une valeur qu'on assigne et qu'on passe en argument.In a static language, you learn a border early: objects on one side, primitives (int, boolean, null) with no methods on the other — and classes, which aren't values you carry around. Ruby erases the entire border. The number has methods, nil has a class, and a class is a value you assign and pass as an argument.

Java — primitives sans méthodes, classes hors-jeuJava — methodless primitives, classes off-limits
// Java : des primitives qui ne sont PAS des objets
int n = 42;
n.toString();        // erreur de compilation : int n'a pas de méthodes
null.getClass();     // ne compile même pas : null ne se déréférence pas

// et une classe n'est pas une valeur ordinaire :
Class c = Integer;   // non : Integer n'est pas une expression
String.class         // un objet Class à part, réservé à la réflexion

Deux mondes coexistent : les objets, qui ont des méthodes, et les primitives, qui n'en ont pas. null n'est pas un objet : l'interroger plante. Et une classe est un citoyen de seconde zone, réservé à la réflexion.Two worlds coexist: objects, which have methods, and primitives, which don't. null isn't an object: querying it crashes. And a class is a second-class citizen, confined to reflection.

La frontière objet / primitive d'ailleurs, Ruby ne la trace nulle part. Il n'y a qu'un seul genre de chose.The object / primitive border drawn elsewhere, Ruby draws nowhere. There is only one kind of thing.
№ 03La mécaniqueThe mechanics5 min

Deux axes, jamais confondus : « est une instance de » et « hérite de ».Two axes, never confused: "is an instance of" and "inherits from".

Si une classe est un objet, elle a forcément, elle aussi, une classe. C'est Class. Et Class est une instance de Class — la tour se referme sur elle-même. Le piège, ici, est de mélanger deux relations distinctes : « 42 est une instance d'Integer » (l'axe de la classe) n'est pas « Integer hérite de Numeric » (l'axe de l'héritage). Démêler les deux, c'est tenir tout le métamodèle.If a class is an object, it too must have a class. That's Class. And Class is an instance of Class — the tower closes on itself. The trap here is mixing two distinct relations: '42 is an instance of Integer' (the class axis) is not 'Integer inherits from Numeric' (the inheritance axis). Untangle the two and you hold the whole metamodel.

Axe « est une instance de »The 'is an instance of' axis
42une instancean instance
.class →.class →
Integerune classe (objet)a class (object)
.class →.class →
Classla classe des classesthe class of classes
↺ .class → Class.class → Class
Symboles et méthodes : objets aussi.Symbols and methods: objects too.

:foo.class est Symbol, 42.method(:+).class est Method. Même ce qui sert à nommer un message, ou à le capturer, est un objet. La règle ne souffre vraiment aucune exception.:foo.class is Symbol, 42.method(:+).class is Method. Even what names a message, or captures it, is an object. The rule truly admits no exception.

Les deux axes, en codeBoth axes, in code
42.class             # => Integer    — l'objet 42 est une instance d'Integer
Integer.class        # => Class      — Integer est une instance de Class
Class.class          # => Class      — et Class est une instance… de Class (ça boucle)

Integer.superclass   # => Numeric    — l'héritage, lui, est un AUTRE axe
Numeric.superclass   # => Object
Object.superclass    # => BasicObject
BasicObject.superclass # => nil      — la racine de tout
Et puisqu'une classe est un objet, on en fabriqueAnd since a class is an object, you can build one
# Une classe est un objet : on peut donc en FABRIQUER une à l'exécution
Point = Class.new do
  def initialize(x, y) = (@x, @y = x, y)
  def to_s = "(#{@x}, #{@y})"
end

Point.new(1, 2).to_s   # => "(1, 2)"
# class Point ... end fait le même travail que ce Class.new — à deux
# nuances près : class ouvre une portée étanche (pas de closure), et
# rouvre la classe si elle existe déjà

class Point ... end n'est pas une construction magique du langage : c'est du sucre par-dessus Class.new, qui retourne un objet qu'on range dans la constante Point. Définir une classe, c'est créer un objet et lui donner un nom.class Point ... end is no magic language construct: it's sugar over Class.new, which returns an object you store in the constant Point. Defining a class is creating an object and giving it a name.

№ 04Le piègeThe trap4 min

« Tout est objet » donne envie de comparer des classes. N'en fais rien."Everything is an object" tempts you to compare classes. Don't.

Le revers de la médaille : une fois qu'on a compris qu'une classe est une valeur ordinaire, on se met à brancher dessus — if value.class == String. C'est ramener à Ruby le réflexe d'un langage à types : on se ferme aux sous-classes, on oublie tous les types qu'on n'a pas énumérés, et on tourne le dos à la seule chose qui compte — est-ce que l'objet répond au message ?The flip side: once you grasp that a class is an ordinary value, you start branching on it — if value.class == String. That's importing a typed-language reflex into Ruby: you shut out subclasses, forget every type you didn't enumerate, and turn your back on the only thing that matters — does the object respond to the message?

Le branchement rigide sur la classeRigid branching on the class
# Le réflexe : puisque la classe est une valeur, je compare des classes
def render(value)
  if value.class == String
    value
  elsif value.class == Integer
    value.to_s
  end
end

render("hi")          # => "hi"
render(42)            # => "42"
render(:sym)          # => nil  — Symbol n'était pas prévu, on retombe dans le vide
render(3.14)          # => nil  — Float non plus

# pire : == compare la classe EXACTE
class Money < String; end
render(Money.new("5")) # => nil  — une sous-classe de String est rejetée !
Envoie le message, ne teste pas la classeSend the message, don't test the class
# Le correctif : n'interroge pas la classe — envoie le message
def render(value) = value.to_s

render("hi")          # => "hi"
render(42)            # => "42"
render(:sym)          # => "sym"
render(Money.new("5")) # => "5"   — la sous-classe répond, donc elle passe
# Tout objet répond à to_s (Vol 1 № 1). On fait confiance au message.
Le contre-exemple obligatoire.The mandatory counter-example.

Comparer value.class == String rejette une sous-classe de String et tout type non listé — on a recréé à la main un système de types rigide, en pire. Si tu dois vraiment tester, is_a? respecte au moins l'héritage ; mais le vrai réflexe Ruby, c'est de ne pas tester du tout et d'envoyer to_s. C'est le sujet du Vol 3.Comparing value.class == String rejects a subclass of String and every unlisted type — you've hand-rebuilt a rigid type system, but worse. If you truly must test, is_a? at least respects inheritance; but the real Ruby reflex is not to test at all and to send to_s. That's the subject of Vol 3.

Savoir qu'une classe est un objet ne veut pas dire brancher dessus. La classe d'un objet est rarement la question — sa réponse au message l'est toujours.Knowing a class is an object doesn't mean branching on it. An object's class is rarely the question — its answer to the message always is.
№ 05Bilan & éditoWrap-up & editorial2 min

Une règle, zéro exception.One rule, zero exceptions.

« Tout est objet » n'est pas un slogan : c'est une absence d'exceptions qu'on peut vérifier ligne à ligne. Le nombre, nil, la classe, la classe de la classe — chacun a une classe, chacun répond à des messages. Une fois ce socle posé, la question n'est plus « est-ce un objet ? » mais « à quel objet je parle, et que répond-il ? ».'Everything is an object' is not a slogan: it's an absence of exceptions you can check line by line. The number, nil, the class, the class of the class — each has a class, each responds to messages. With that bedrock in place, the question is no longer 'is this an object?' but 'which object am I talking to, and what does it answer?'.

Ce qu'on croit « spécial »What you think is 'special'Sa classeIts classDoncTherefore
Un nombreA number42.class → Integer. Il a des méthodes : 42.even?, 42.times.42.class → Integer. It has methods: 42.even?, 42.times.Pas de primitive sans méthodes. Le nombre est un receveur comme un autre.No methodless primitive. The number is a receiver like any other.
nil, truenil, truenil.class → NilClass ; true.class → TrueClass.nil.class → NilClass; true.class → TrueClass.nil n'est pas un « rien » spécial : c'est un objet. Son cas, c'est le № 3.nil is no special 'nothing': it's an object. Its case is № 3.
Une classeA classInteger.class → Class. On l'assigne, on l'itère, on lui parle.Integer.class → Class. You assign it, iterate it, message it.Une classe est une valeur de première classe — d'où la métaprog (Vol 6) sans magie.A class is a first-class value — hence metaprogramming (Vol 6) without magic.
Class elle-mêmeClass itselfClass.class → Class. La tour se referme sur elle-même.Class.class → Class. The tower closes on itself.« Est une instance de » et « hérite de » sont deux axes distincts. Ne les mélange jamais.'Is an instance of' and 'inherits from' are two distinct axes. Never mix them.
La frontière entre « ce qui est un objet » et « ce qui n'en est pas un » est, en Ruby, vide des deux côtés sauf un : tout est dedans.The border between 'what is an object' and 'what isn't' is, in Ruby, empty on one side: everything sits inside.
Mot de l'éditeurFrom the editor

La cohérence n'est pas un luxe.Consistency isn't a luxury.

On peut trouver excessif que Ruby fasse d'un entier, de nil et d'une classe des objets de plein droit — c'est plus lent qu'une primitive câblée, on y reviendra au Vol 9. Mais cette uniformité achète quelque chose de rare : il n'y a qu'une seule histoire à apprendre. Pas de règle pour les objets et d'exception pour les primitives, pas de syntaxe à part pour manipuler les classes. Une chose existe ? Alors elle a une classe et répond à des messages. C'est tout. La « magie » de Ruby tient à ce qu'il n'y a, justement, pas de cas particulier à mémoriser.You might find it excessive that Ruby makes an integer, nil and a class full-fledged objects — it's slower than a wired-in primitive, we'll return to that in Vol 9. But this uniformity buys something rare: there's only one story to learn. No rule for objects and an exception for primitives, no separate syntax to handle classes. A thing exists? Then it has a class and answers messages. That's all. Ruby's 'magic' comes precisely from there being no special case to memorize.

Prochain numéro : nil n'est pas null. Puisque nil est un objet — l'unique instance de NilClass — il répond à des messages, et son fameux NoMethodError n'est pas un crash de pointeur mais un objet qui dit « je n'ai pas cette méthode ».Next issue: nil is not null. Since nil is an object — the sole instance of NilClass — it responds to messages, and its infamous NoMethodError isn't a pointer crash but an object saying 'I don't have that method'.