Cours Ruby Complet 💎
Les 12 chapitres essentiels — blocs, mixins, metaprogramming et l'élégance du langage de Rails
🏠 Hub Programmation
🐍 Python
⚡ JavaScript
🐘 PHP
☁️ AWS Cloud Practitioner
Introduction et premier programme
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.
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
| Caractéristique | Ruby |
|---|---|
| Typage | Dynamique et fort (duck typing) |
| Paradigme | POO pure (tout est objet) + fonctionnel |
| Exécution | Interprété (MRI/CRuby, JRuby, TruffleRuby) |
| Philosophie | « Optimisé pour le bonheur du développeur » |
| Usage | Web (Rails), scripts, automatisation, DevOps |
| Convention | snake_case (méthodes/variables), PascalCase (classes) |
Variables et types de données
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
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.
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 »
Méthodes
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.
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
Structures de contrôle
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
# 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)
(1…5).each # 1, 2, 3, 4 (exclusif)
Collections
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)
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 « #: # » }
Classes et POO
# 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)
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 ! »
Blocs, Procs et Lambdas
# 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 .
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]
Modules et Mixins
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…
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.
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]
Gestion d'erreurs
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 »)
Collections avancées
# 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]
Fichiers et Gems
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 »))
| Gem | Usage |
|---|---|
| rails | Framework web full-stack |
| sinatra | Micro-framework web |
| rspec | Framework de tests BDD |
| pry | Console/debugger avancé |
| rubocop | Linter / style guide |
| nokogiri | Parsing HTML/XML |
| devise | Authentification (Rails) |
| sidekiq | Jobs asynchrones |
Ruby idiomatique
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?)
✅ À 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
🏠 Hub Programmation
🐍 Cours Python
⚡ Cours JavaScript
🐘 Cours PHP
☁️ AWS Cloud Practitioner
Cours Ruby Complet — Blocs, Mixins et élégance
Référence : ruby-lang.org | ruby-doc.org | Rails
