Cours Ruby Complet 💎

Les 12 chapitres essentiels — blocs, mixins, metaprogramming et l'élégance du langage de Rails

12
Chapitres
100+
Exemples de code
Ruby 3.3+
Version
A1→C2
Niveaux

CHAPITRE 01

Introduction et premier programme

💎 Hello World
puts « Bonjour le monde ! »
puts « Nom: #, Age: # » # Interpolation
print « Sans saut de ligne »
p [1, 2, 3] # Debug (affiche .inspect)

Ruby est conçu pour le bonheur du développeur. Créé par Yukihiro Matsumoto (« Matz ») en 1995, sa philosophie est que le code doit être agréable à lire et à écrire. Chaque élément du langage a été pensé pour l'expressivité et l'élégance.

⚙️ Installer et exécuter
# Installer (recommandé avec rbenv ou rvm)
brew install rbenv
rbenv install 3.3.0
rbenv global 3.3.0

# Exécuter
ruby hello.rb

# REPL interactif
irb

# Gestionnaire de paquets
gem install rails
gem install bundler

# Bundler (dépendances projet)
bundle init # Crée un Gemfile
bundle install # Installe les dépendances

🏗️ Ruby en résumé
CaractéristiqueRuby
TypageDynamique et fort (duck typing)
ParadigmePOO pure (tout est objet) + fonctionnel
ExécutionInterprété (MRI/CRuby, JRuby, TruffleRuby)
Philosophie« Optimisé pour le bonheur du développeur »
UsageWeb (Rails), scripts, automatisation, DevOps
Conventionsnake_case (méthodes/variables), PascalCase (classes)

En Ruby, TOUT est objet. Même les nombres, les booléens et nil sont des objets avec des méthodes. 5.times — le nombre 5 a une méthode times. C'est très différent de Python ou Java.

🧠 Quiz
Pourquoi dit-on que Ruby est « pure orienté objet » ?
Parce que tout est un objet — les nombres (42.class → Integer), les booléens (true.class → TrueClass), nil (nil.class → NilClass), et même les classes elles-mêmes sont des objets.

CHAPITRE 02

Variables et types de données

📦 Variables et portée
# Variables locales (snake_case)
name = « Alice »
age = 25
pi = 3.14

# Constantes (MAJUSCULES)
MAX_SIZE = 100
# Ruby émet un warning si on réassigne, mais ne l'empêche pas

# Variables d'instance (@)
@name = « Alice »

# Variables de classe (@@)
@@count = 0

# Variables globales ($) — à éviter
$debug = true

🔢 Types de données
# Integer — taille arbitraire (pas de overflow !)
n = 42
big = 10_000_000_000_000_000 # Underscores pour lisibilité

# Float
pi = 3.14159

# String
s = « Hello » # Double quotes : interpolation + escape
s = 'Hello' # Single quotes : littéral (pas d'interpolation)

# Symbol — identifiant immuable et léger
status = :active # Plus rapide que « active » pour les clés/labels
:active.object_id == :active.object_id # true (même objet)

# Boolean et Nil
actif = true
vide = nil # L'absence de valeur (falsy)

# En Ruby : seuls nil et false sont falsy
# 0, «  », [] sont TRUTHY ! (≠ Python/JS)

# Array et Hash
arr = [1, « deux », :trois] h =

Piège fréquent : En Ruby, 0 et «  » sont truthy. Seuls nil et false sont falsy. C'est l'inverse de Python et JavaScript.

📝 Strings
s = « Hello World »

s.length # 11
s.empty? # false
s.upcase # « HELLO WORLD »
s.downcase # « hello world »
s.capitalize # « Hello world »
s.reverse # « dlroW olleH »
s.include?(« World ») # true
s.start_with?(« He ») # true
s.end_with?(« ld ») # true
s.gsub(« World », « Ruby ») # « Hello Ruby »
s.split( » « ) # [« Hello », « World »]
s.strip # Supprime espaces début/fin
s.chars # [« H », « e », « l », …]
s.freeze # Rend la string immuable

# Méthodes avec ! modifient l'objet en place
s.upcase! # Modifie s directement

# Conversion
« 42 ».to_i # 42
« 3.14 ».to_f # 3.14
42.to_s # « 42 »

CHAPITRE 03

Méthodes

🔧 Méthodes en Ruby
# Définir une méthode
def add(a, b)
a + b # Dernière expression = retour implicite
end

# Parenthèses optionnelles à l'appel
add 3, 4 # 7
add(3, 4) # 7

# Paramètres par défaut
def greet(name, greeting = « Bonjour »)
« # # ! »
end

# Keyword arguments
def create_user(name:, age:, email: nil)

end
create_user(name: « Alice », age: 25)

# Splat — nombre variable d'arguments
def sum(*nums)
nums.reduce(0, :+)
end
sum(1, 2, 3) # 6

# Double splat — keyword arguments variables
def config(**opts)
opts # Hash
end

Ruby retourne implicitement la dernière expression. Pas besoin de return (sauf pour un early return). C'est une convention forte : le code est plus concis et lisible.

❓ Conventions de nommage
# ? = méthode qui retourne un booléen (predicate)[1,2,3].empty? # false
42.even? # true
nil.nil? # true
« abc ».include?(« b ») # true

# ! = méthode « dangereuse » (modifie en place ou lève une erreur)
arr = [3, 1, 2] arr.sort # Retourne un nouveau tableau trié
arr.sort! # Modifie arr en place

CHAPITRE 04

Structures de contrôle

🔀 if, unless, case
# if / elsif / else / end
if age >= 18
puts « Majeur »
elsif age >= 13
puts « Ado »
else
puts « Enfant »
end

# if en une ligne (trailing if — très Ruby)
puts « Majeur » if age >= 18

# unless = if not (plus lisible)
puts « Accès refusé » unless user.admin?

# Ternaire
label = age >= 18 ? « majeur » : « mineur »

# case / when (pattern matching)
case age
when 0..12 then « enfant »
when 13..17 then « ado »
when 18 then « tout juste majeur »
else « adulte »
end

# case avec classes (type check)
case obj
when String then « C'est un texte »
when Integer then « C'est un nombre »
when Array then « C'est un tableau »
end

# Pattern matching (Ruby 3+)
case response
in then puts body
in then puts « Not found »
end

🔁 Boucles
# each — LA boucle Ruby (avec bloc)[1, 2, 3].each

# each avec bloc multi-ligne[1, 2, 3].each do |n|
puts « Nombre: # »
end

# times, upto, downto
5.times # 0, 1, 2, 3, 4
1.upto(5) # 1, 2, 3, 4, 5

# while / until
while n > 0
n -= 1
end

# Ranges
(1..5).each # 1, 2, 3, 4, 5 (inclusif)
(15).each # 1, 2, 3, 4 (exclusif)

CHAPITRE 05

Collections

📋 Arrays
arr = [1, 2, 3, 4, 5]

arr.length # 5
arr.empty? # false
arr.first # 1
arr.last # 5
arr.push(6) # ou arr << 6
arr.pop # 6 (supprime et retourne le dernier)
arr.unshift(0) # Ajouter au début
arr.shift # Supprimer le premier
arr.include?(3) # true
arr.sort # [1, 2, 3, 4, 5]
arr.reverse # [5, 4, 3, 2, 1]
arr.uniq # Dédupliquer
arr.flatten # Aplatir [[1,2],[3]] → [1,2,3]
arr.compact # Supprimer les nil
arr.sample # Élément aléatoire
arr.min # 1
arr.max # 5
arr.sum # 15

# Slicing
arr[1..3] # [2, 3, 4]
arr[-1] # 5 (dernier)
arr[-2..] # [4, 5] (2 derniers)

🗺️ Hashes (Dictionnaires)
# Syntaxe moderne (symboles comme clés)
ages =

# CRUD
ages[:alice] # 25
ages[:eve] # nil (pas d'erreur !)
ages.fetch(:eve, 0) # 0 (valeur par défaut)
ages[:dave] = 35 # Ajouter
ages.delete(:bob) # Supprimer

# Méthodes
ages.keys # [:alice, :charlie, :dave]
ages.values # [25, 28, 35]
ages.key?(:alice) # true
ages.value?(25) # true
ages.merge() # Fusionner

# Itérer
ages.each { |name, age| puts « #: # » }

🧠 Quiz
Différence entre Symbol (:name) et String (« name ») ?
Symbol = immuable et unique en mémoire (un seul objet :name partagé). String = mutable, chaque « name » crée un nouvel objet. Les symbols sont plus rapides comme clés de hash et labels, les strings pour le texte manipulable.

CHAPITRE 06

Classes et POO

🏛️ Classes Ruby
class Person
# attr_accessor = getter + setter
# attr_reader = getter seulement
# attr_writer = setter seulement
attr_accessor :name, :age

def initialize(name, age)
@name = name # Variable d'instance
@age = age
end

def greet
« Bonjour, je suis # »
end

def adult?
@age >= 18
end

def to_s
« # (# ans) »
end
end

alice = Person.new(« Alice », 25)
puts alice.name # « Alice » (via attr_accessor)
puts alice.greet # « Bonjour, je suis Alice »
puts alice.adult? # true
puts alice # « Alice (25 ans) » (to_s)

🔗 Héritage
class Animal
attr_reader :name

def initialize(name)
@name = name
end

def speak
« … »
end
end

class Dog < Animal # < = hérite de
def speak
« Wouf ! »
end
end

class Cat < Animal
def speak
« Miaou ! »
end
end

rex = Dog.new(« Rex »)
puts « # fait # » # « Rex fait Wouf ! »

CHAPITRE 07

Blocs, Procs et Lambdas

🧱 Blocs — le cœur de Ruby
# Un bloc = code entre ou do…end passé à une méthode

# pour une seule ligne[1,2,3].each

# do…end pour plusieurs lignes[1,2,3].each do |n|
result = n * 2
puts result
end

# yield — appeler le bloc dans une méthode
def with_logging
puts « Début »
yield # Exécute le bloc passé
puts « Fin »
end

with_logging
# Début
# Action !
# Fin

# yield avec argument
def repeat(n)
n.times
end
repeat(3)

Les blocs sont la fonctionnalité la plus distinctive de Ruby. Ils permettent de passer du code comme argument à n'importe quelle méthode. C'est ce qui rend Ruby si expressif : 5.times , File.open , array.map .

⚡ Proc et Lambda
# Proc — bloc stocké dans une variable
doubler = Proc.new
doubler.call(5) # 10

# Lambda — comme Proc mais plus strict
doubler = ->(n)
doubler.call(5) # 10
doubler.(5) # 10 (raccourci)

# Différences Proc vs Lambda :
# – Lambda vérifie le nombre d'arguments
# – Lambda : return retourne de la lambda
# – Proc : return retourne de la méthode englobante

# Method reference[« hello », « world »].map(&:upcase) # [« HELLO », « WORLD »][1,nil,3].select(&:nil?) # [nil]

CHAPITRE 08

Modules et Mixins

📦 Modules comme namespaces
# Module = conteneur de méthodes et constantes
module MathUtils
PI = 3.14159

def self.circle_area(r)
PI * r ** 2
end
end

MathUtils::PI # 3.14159
MathUtils.circle_area(5) # 78.539…

🔀 Mixins — la composition Ruby
# Module comme mixin (partage de comportement)
module Printable
def print_info
puts to_s
end
end

module Serializable
def to_json
# Sérialisation JSON
instance_variables.each_with_object() .to_json
end
end

class User
include Printable # Ajoute comme méthodes d'instance
include Serializable

attr_accessor :name, :age

def initialize(name, age)
@name = name
@age = age
end

def to_s
« # (#) »
end
end

user = User.new(« Alice », 25)
user.print_info # « Alice (25) »

Les mixins remplacent l'héritage multiple. Ruby n'a que l'héritage simple (<), mais include permet de « mixer » autant de modules qu'on veut dans une classe. C'est la composition Ruby : un seul parent, mais des comportements illimités via les modules.

⚡ Enumerable — le super-mixin
# Inclure Enumerable + définir each = 50+ méthodes gratuites
class NumberList
include Enumerable

def initialize(*nums)
@nums = nums
end

def each(&block)
@nums.each(&block)
end
end

list = NumberList.new(3, 1, 4, 1, 5)
list.sort # [1, 1, 3, 4, 5]
list.min # 1
list.select(&:odd?) # [3, 1, 1, 5]

CHAPITRE 09

Gestion d'erreurs

🛡️ begin / rescue / ensure
# begin…rescue…ensure…end
begin
result = 10 / 0
rescue ZeroDivisionError => e
puts « Erreur: # »
rescue StandardError => e
puts « Autre erreur: # »
ensure
puts « Toujours exécuté (finally) »
end

# rescue en une ligne
result = 10 / n rescue 0

# raise — lever une erreur
raise « Quelque chose a échoué »
raise ArgumentError, « Age invalide »

# retry — réessayer le bloc begin
attempts = 0
begin
attempts += 1
connect_to_server
rescue ConnectionError
retry if attempts < 3
raise
end

# Erreur custom
class NotFoundError < StandardError
def initialize(resource)
super(« # non trouvé »)
end
end

raise NotFoundError.new(« User »)

CHAPITRE 10

Collections avancées

🌊 Enumerable — le couteau suisse
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# map / collect — transformer
nums.map # [2, 4, 6, 8, …]

# select / filter — garder si true
nums.select # [2, 4, 6, 8, 10]
nums.select(&:even?) # Raccourci &:method

# reject — supprimer si true
nums.reject(&:odd?) # [2, 4, 6, 8, 10]

# reduce / inject — accumuler
nums.reduce(0) # 55
nums.reduce(:+) # 55 (raccourci)

# each_with_object
nums.each_with_object([])

# Chaîner
nums
.select(&:even?)
.map
.sum # 220

# Autres essentiels
nums.any?(&:even?) # true
nums.all? # true
nums.none?(&:negative?) # true
nums.count(&:even?) # 5
nums.find # 6 (premier match)
nums.min_by # 7 (plus proche de 7)
nums.max_by(&:itself) # 10
nums.sort_by # [10, 9, …, 1]
nums.group_by(&:even?) #
nums.flat_map # [1,2, 2,4, 3,6, …]
nums.zip([« a »,« b »]) # [[1, »a »], [2, »b »], [3,nil], …]
nums.each_slice(3).to_a # [[1,2,3], [4,5,6], [7,8,9], [10]]
nums.tally # (Ruby 2.7+)

# Lazy (évaluation paresseuse — grandes collections)
(1..Float::INFINITY).lazy.select(&:odd?).take(5).to_a
# [1, 3, 5, 7, 9]

CHAPITRE 11

Fichiers et Gems

📄 Lecture/écriture de fichiers
# Lire un fichier entier
content = File.read(« data.txt »)

# Lire ligne par ligne
File.foreach(« data.txt »)

# Écrire
File.write(« output.txt », « Contenu »)

# Avec bloc (fermeture automatique)
File.open(« data.txt », « r ») do |f|
f.each_line
end

# JSON
require « json »
data =
json = data.to_json # ''
parsed = JSON.parse(json) # Hash

# HTTP (net/http intégré)
require « net/http »
require « uri »
response = Net::HTTP.get(URI(« https://api.example.com/users »))

💎 Gems populaires
GemUsage
railsFramework web full-stack
sinatraMicro-framework web
rspecFramework de tests BDD
pryConsole/debugger avancé
rubocopLinter / style guide
nokogiriParsing HTML/XML
deviseAuthentification (Rails)
sidekiqJobs asynchrones

CHAPITRE 12

Ruby idiomatique

⚡ Patterns Ruby
# Struct — data class rapide
User = Struct.new(:name, :email, :age)
alice = User.new(« Alice », « alice@mail.com », 25)
alice.name # « Alice »

# Data class (Ruby 3.2+) — immuable
Point = Data.define(😡, :y)
p = Point.new(x: 3, y: 4)

# Frozen string literals (performance)
# frozen_string_literal: true

# Open classes — modifier une classe existante
class Integer
def factorial
(1..self).reduce(1, :*)
end
end
5.factorial # 120

# Ractor (Ruby 3+) — concurrence vraie
r = Ractor.new
r.take # 42

# Safe navigation (&.) — comme ?. en Swift/Kotlin
user&.address&.city # nil si user ou address est nil

# tap — debug dans une chaîne[1,2,3] .map
.tap
.select(&:even?)

✅ Bonnes pratiques

✅ À FAIRE
snake_case pour méthodes/variables
PascalCase pour classes/modules
? pour les prédicats (empty?)
! pour les méthodes destructives
• Blocs et Enumerable partout
freeze les constantes
rubocop pour le style
• Composition via modules/mixins
&:method raccourci

❌ À ÉVITER
• Variables globales ($)
for loop (utiliser each)
• return explicite (sauf early return)
• Monkey patching excessif
rescue Exception (trop large)
• Méthodes > 10 lignes
• Héritage profond (mixins)
eval / send abusif
• Ignorer rubocop

🧠 Quiz
Qu'est-ce que le duck typing en Ruby ?
« Si ça marche comme un canard, c'est un canard. » Ruby ne vérifie pas le type d'un objet, mais s'il répond à la méthode appelée. respond_to?(:method) compte plus que is_a?(Class). Cela rend le code flexible et extensible.
Différence entre include et extend ?
include ajoute les méthodes du module comme méthodes d'instance (appelées sur un objet). extend les ajoute comme méthodes de classe (appelées sur la classe elle-même).

Cours Ruby Complet — Blocs, Mixins et élégance

Référence : ruby-lang.org | ruby-doc.org | Rails