Ruby de Newby à Ready

code 7 août 2025

Introduction à Ruby

Qu'est-ce que Ruby?

  • Langage de programmation créé par Yukihiro "Matz" Matsumoto en 1995
  • Orienté objet, dynamique et interprété
  • Philosophie: "Optimisé pour le bonheur du programmeur" et basé sur le principe de moindre surprise
  • Syntaxe élégante et naturelle
  • Fait pour créer vite.

Pourquoi apprendre Ruby?

  • Productivité élevée
  • Communauté active et bienveillante
  • Écosystème riche (Rails, Sinatra, Jekyll)
  • Utilisé par: GitHub, Airbnb, Shopify, etc.

Installation de Ruby

# Sur macOS avec Homebrew
brew install ruby

# Sur Ubuntu/Debian
sudo apt-get install ruby-full

# Avec un gestionnaire de versions (recommandé)
# RVM
\curl -sSL https://get.rvm.io | bash -s stable --ruby
# ou rbenv
brew install rbenv ruby-build
rbenv init
rbenv install 3.2.2

Vérification de l'installation

ruby -v
# Ruby 3.2.2p53 (2023-03-30 revision 957bb7) [x86_64-darwin22]

irb
# Interactive Ruby Shell

Les bases de Ruby

Premier programme Ruby

# hello.rb
puts "Hello, World!"

# Exécution
# ruby hello.rb

Deuxième programme en Ruby

firstname = gets.chomp
puts "Hello, #{firstname}"

Variables et types de données

# Variables - pas besoin de déclaration de type
name = "Ruby"
age = 28
is_oop = true
pi = 3.14159

# Types de base
puts name.class      # String
puts age.class       # Integer
puts is_oop.class    # TrueClass
puts pi.class        # Float

Chaînes de caractères

# Création et manipulation
greeting = "Hello"
name = "Ruby"

# Concaténation
message = greeting + ", " + name + "!"
puts message  # Hello, Ruby!

# Interpolation (préférable)
message = "#{greeting}, #{name}!"
puts message  # Hello, Ruby!

# Méthodes utiles
puts name.upcase      # RUBY
puts name.downcase    # ruby
puts name.length      # 4
puts "ruby".capitalize # Ruby

Nombres et opérations mathématiques

# Opérations de base
sum = 5 + 3        # 8
difference = 10 - 4 # 6
product = 4 * 3     # 12
quotient = 10 / 3   # 3 (division entière)
remainder = 10 % 3  # 1 (modulo)

# Division avec décimales
precise = 10.0 / 3  # 3.3333333333333335

# Méthodes utiles
puts 5.even?        # false
puts 6.even?        # true
puts 7.odd?         # true
puts 10.between?(5, 15) # true

Structures de contrôle

Conditions

# If, else, elsif
age = 18

if age < 18
  puts "Mineur"
elsif age == 18
  puts "Tout juste majeur"
else
  puts "Majeur"
end

# Opérateur ternaire
status = age >= 18 ? "Majeur" : "Mineur"

# Unless (if not)
unless age < 18
  puts "Vous pouvez entrer"
end

# Modificateurs de ligne
puts "Majeur" if age >= 18

Slide 10: Boucles et itérations

# While
count = 0
while count < 5
  puts count
  count += 1
end

# Until
count = 0
until count >= 5
  puts count
  count += 1
end

# For
for i in 0..4
  puts i
end

# Each (préféré en Ruby)
(0..4).each do |i|
  puts i
end

# Times
5.times { |i| puts i }

Structures de données - Arrays

# Création
fruits = ["pomme", "banane", "orange"]

# Accès
puts fruits[0]      # pomme
puts fruits[-1]     # orange (dernier élément)
puts fruits[0..1]   # ["pomme", "banane"] (slice)

# Modification
fruits << "fraise"  # Ajout à la fin
fruits.push("kiwi") # Ajout à la fin
fruits.unshift("ananas") # Ajout au début
fruits.pop          # Supprime et retourne le dernier
fruits.shift        # Supprime et retourne le premier

# Itération
fruits.each { |fruit| puts fruit }
fruits.map { |fruit| fruit.upcase } # Retourne un nouveau tableau

Structures de données - Hashes

# Création
person = {
  "name" => "Alice",
  "age" => 30,
  "city" => "Paris"
}

# Syntaxe avec symboles (préférée)
person = {
  name: "Alice",
  age: 30,
  city: "Paris"
}

# Accès
puts person[:name]  # Alice

# Modification
person[:job] = "Developer"
person[:age] = 31

# Itération
person.each do |key, value|
  puts "#{key}: #{value}"
end

Méthodes et blocs

Définition de méthodes

# Méthode simple
def greet
  puts "Bonjour!"
end

# Avec paramètres
def greet_person(name)
  puts "Bonjour, #{name}!"
end

# Avec valeur par défaut
def greet_with_language(name, language = "fr")
  if language == "fr"
    puts "Bonjour, #{name}!"
  elsif language == "en"
    puts "Hello, #{name}!"
  end
end

# Avec retour explicite
def add(a, b)
  return a + b
end

# Le retour est implicite (dernière expression)
def multiply(a, b)
  a * b
end

Appel de méthodes

greet                     # Bonjour!
greet_person("Alice")     # Bonjour, Alice!
greet_with_language("Bob", "en") # Hello, Bob!
sum = add(5, 3)           # 8
product = multiply(4, 2)  # 8

# Arguments nommés
greet_with_language(language: "en", name: "Charlie")

Slide 15: Blocs

# Bloc avec do...end
[1, 2, 3].each do |num|
  puts num * 2
end

# Bloc avec accolades
[1, 2, 3].each { |num| puts num * 2 }

# Méthode avec bloc
def execute_block
  puts "Avant le bloc"
  yield if block_given?
  puts "Après le bloc"
end

execute_block { puts "Dans le bloc" }

Slide 16: Proc et Lambda

# Proc
square = Proc.new { |x| x * x }
puts square.call(4)  # 16

# Lambda
cube = ->(x) { x * x * x }
puts cube.call(3)    # 27

# Différences entre Proc et Lambda
# 1. Vérification du nombre d'arguments
# 2. Comportement du return

Comprendre les Appels de Méthodes en Ruby

L'Envoi de Messages en Ruby

Ruby est un langage orienté objet pur où tout est objet et les interactions se font par envoi de messages.

# Syntaxe standard
objet.methode(arguments)

# En coulisses, c'est transformé en:
objet.send(:methode, arguments)

L'appel de méthode est en réalité un message envoyé à un objet.

La Méthode call

La méthode call permet d'exécuter des objets appelables (Proc, lambda, méthodes).

# Avec un Proc
addition = proc { |a, b| a + b }
addition.call(3, 4)  # => 7

# Avec une lambda
multiplication = ->(a, b) { a * b }
multiplication.call(3, 4)  # => 12

# Avec une méthode
methode = "hello".method(:upcase)
methode.call  # => "HELLO"

Arité (Arity)

L'arité définit le nombre d'arguments qu'une méthode attend.

# Vérifier l'arité
addition = proc { |a, b| a + b }
addition.arity  # => 2

# Arité négative (arguments variables)
variadic = proc { |*args| args.sum }
variadic.arity  # => -1

Différence clé:

  • Proc: souple avec les arguments
  • Lambda: strict avec les arguments

Types de Paramètres - Arguments Positionnels

# Arguments positionnels basiques
def saluer(nom, titre)
  "Bonjour #{titre} #{nom}!"
end
saluer("Dupont", "M.")  # => "Bonjour M. Dupont!"

# Arguments avec valeurs par défaut
def accueillir(nom, message = "Bienvenue")
  "#{message}, #{nom}!"
end
accueillir("Marie")  # => "Bienvenue, Marie!"

Arguments Variables

# Arguments variables avec splat (*)
def somme(*nombres)
  nombres.sum
end
somme(1, 2, 3, 4)  # => 10

# Combinaison d'arguments fixes et variables
def presenter(titre, *noms)
  "#{titre}: #{noms.join(', ')}"
end
presenter("Participants", "Alice", "Bob", "Charlie")
# => "Participants: Alice, Bob, Charlie"

Arguments Nommés (Keywords)

# Arguments nommés
def configurer(taille:, couleur:, style: "normal")
  "Configuration: taille=#{taille}, couleur=#{couleur}, style=#{style}"
end

configurer(taille: 12, couleur: "bleu")
# => "Configuration: taille=12, couleur=bleu, style=normal"

# Arguments nommés variables
def options(obligatoire:, **autres)
  "Obligatoire: #{obligatoire}, Autres: #{autres}"
end
options(obligatoire: "oui", a: 1, b: 2)
# => "Obligatoire: oui, Autres: {:a=>1, :b=>2}"

Blocs comme Arguments

# Méthode acceptant un bloc
def executer
  puts "Avant"
  yield if block_given?
  puts "Après"
end

executer { puts "Pendant" }
# Affiche:
# Avant
# Pendant
# Après

# Bloc explicite avec &
def transformer(tableau, &bloc)
  tableau.map(&bloc)
end

transformer([1, 2, 3]) { |n| n * 2 }  # => [2, 4, 6]

Exemple Complet

def traiter_donnees(id, *valeurs, format: "json", strict: false, &bloc)
  resultat = {
    id: id,
    valeurs: valeurs,
    format: format,
    strict: strict
  }
  
  # Applique le bloc si fourni
  resultat = bloc.call(resultat) if bloc
  
  resultat
end

traiter_donnees(42, "a", "b", format: "xml") { |r| r.merge(traite: true) }
# => {:id=>42, :valeurs=>["a", "b"], :format=>"xml", :strict=>false, :traite=>true}

Méthodes comme Objets de Première Classe

# Obtenir un objet Method
class Calculateur
  def additionner(a, b)
    a + b
  end
end

calc = Calculateur.new
methode_addition = calc.method(:additionner)

# Utiliser l'objet Method
methode_addition.call(5, 3)  # => 8
methode_addition.arity  # => 2

# Créer un Proc à partir d'une méthode
proc_addition = methode_addition.to_proc
[1, 2, 3].map(&proc_addition.curry[10])  # => [11, 12, 13]

Programmation Orientée Objet

Classes et objets

# Définition d'une classe
class Person
  def initialize(name, age)
    @name = name
    @age = age
  end
  
  def introduce
    puts "Je m'appelle #{@name} et j'ai #{@age} ans."
  end
end

# Création d'objets
alice = Person.new("Alice", 30)
bob = Person.new("Bob", 25)

# Appel de méthodes
alice.introduce  # Je m'appelle Alice et j'ai 30 ans.
bob.introduce    # Je m'appelle Bob et j'ai 25 ans.

Attributs et accesseurs

class Person
  # Getters et setters automatiques
  attr_accessor :name, :age
  
  def initialize(name, age)
    @name = name
    @age = age
  end
end

alice = Person.new("Alice", 30)
puts alice.name  # Alice (getter)
alice.name = "Alicia"  # (setter)
puts alice.name  # Alicia

# Variantes
# attr_reader :name  # Getter uniquement
# attr_writer :name  # Setter uniquement

Méthodes d'instance et de classe

class MathHelper
  # Méthode d'instance
  def square(x)
    x * x
  end
  
  # Méthode de classe
  def self.cube(x)
    x * x * x
  end
end

helper = MathHelper.new
puts helper.square(4)    # 16 (méthode d'instance)
puts MathHelper.cube(3)  # 27 (méthode de classe)

Héritage

class Animal
  def speak
    "Je suis un animal"
  end
end

class Dog < Animal
  def speak
    "Woof!"
  end
end

class Cat < Animal
  def speak
    "Meow!"
  end
end

puts Animal.new.speak  # Je suis un animal
puts Dog.new.speak     # Woof!
puts Cat.new.speak     # Meow!

Modules et mixins

# Module comme namespace
module Math
  PI = 3.14159
  
  def self.square(x)
    x * x
  end
end

puts Math::PI        # 3.14159
puts Math.square(4)  # 16

# Module comme mixin
module Swimmable
  def swim
    "Je nage!"
  end
end

class Fish
  include Swimmable
end

class Duck
  include Swimmable
end

puts Fish.new.swim  # Je nage!
puts Duck.new.swim  # Je nage!

Visibilité des méthodes

class BankAccount
  def initialize(balance)
    @balance = balance
  end
  
  # Méthode publique
  def display_balance
    "Solde: #{@balance}€"
  end
  
  # Méthode protégée
  protected
  def transfer_allowed?(amount)
    amount <= @balance
  end
  
  # Méthode privée
  private
  def update_balance(amount)
    @balance += amount
  end
end

Exceptions

# Lever une exception
def divide(a, b)
  raise ArgumentError, "Division par zéro!" if b == 0
  a / b
end

# Gérer une exception
begin
  result = divide(10, 0)
  puts result
rescue ArgumentError => e
  puts "Erreur: #{e.message}"
ensure
  puts "Cette partie s'exécute toujours"
end

# Créer une exception personnalisée
class InsufficientFundsError < StandardError; end

def withdraw(amount)
  raise InsufficientFundsError, "Fonds insuffisants" if amount > @balance
  # ...
end

Gems et gestion de dépendances

Introduction aux gems

# Qu'est-ce qu'une gem?
# - Bibliothèque Ruby packagée
# - Facilite la réutilisation du code
# - Gérée par RubyGems (https://rubygems.org)

# Installation d'une gem
# gem install httparty

# Utilisation dans un script
require 'httparty'

response = HTTParty.get('https://api.github.com')
puts response.code    # 200
puts response.message # OK
puts response.headers.inspect

Bundler et Gemfile

# Installation de Bundler
# gem install bundler

# Gemfile
source 'https://rubygems.org'

gem 'httparty', '~> 0.21.0'
gem 'json', '~> 2.6'

group :development, :test do
  gem 'rspec', '~> 3.12'
  gem 'pry', '~> 0.14'
end

# Installation des dépendances
# bundle install

# Utilisation dans un script
require 'bundler/setup'
require 'httparty'

Exemple pratique avec HTTParty

require 'httparty'
require 'json'

class GithubClient
  include HTTParty
  base_uri 'https://api.github.com'
  
  def initialize(token = nil)
    @headers = {
      'User-Agent' => 'Ruby GitHub Client',
      'Authorization' => "token #{token}"
    }
  end
  
  def get_user(username)
    self.class.get("/users/#{username}", headers: @headers)
  end
  
  def get_repos(username)
    self.class.get("/users/#{username}/repos", headers: @headers)
  end
end

client = GithubClient.new
user = client.get_user('octocat')
puts "Nom: #{user['name']}"
puts "Bio: #{user['bio']}"

YARD : Documentation pour Ruby

Introduction

YARD (Yet Another Ruby Documentation) est un outil de documentation pour le langage Ruby, conçu pour remplacer RDoc. Il offre une syntaxe plus riche et des fonctionnalités plus avancées pour documenter votre code Ruby.

Installation

gem install yard

Syntaxe de base

YARD utilise des commentaires précédés par # et des tags commençant par @.

Structure d'un commentaire YARD

# Description générale sur une ou plusieurs lignes
#
# @param [Type] nom_paramètre description du paramètre
# @return [Type] description de la valeur de retour
# @example Titre de l'exemple
#   code_exemple

Exemples pratiques

Documenter une méthode

# Calcule la somme de deux nombres
#
# @param [Integer] a Premier nombre à additionner
# @param [Integer] b Second nombre à additionner
# @return [Integer] La somme des deux nombres
# @example Addition de 2 et 3
#   add(2, 3) #=> 5
def add(a, b)
  a + b
end

Documenter une classe

# Une classe représentant un utilisateur dans le système
#
# @attr [String] name Le nom complet de l'utilisateur
# @attr [String] email L'adresse email de l'utilisateur
# @attr_reader [Time] created_at Date de création du compte
class User
  attr_accessor :name, :email
  attr_reader :created_at
  
  # Initialise un nouvel utilisateur
  #
  # @param [String] name Le nom de l'utilisateur
  # @param [String] email L'email de l'utilisateur
  # @raise [ArgumentError] Si l'email est invalide
  def initialize(name, email)
    @name = name
    @email = email
    @created_at = Time.now
    
    raise ArgumentError, "Email invalide" unless email.include?('@')
  end
end

Tags YARD courants

Tag Description
@param Documente un paramètre de méthode
@return Documente la valeur de retour
@example Fournit un exemple d'utilisation
@raise Documente les exceptions possibles
@see Référence à d'autres éléments
@author Auteur du code
@since Version d'introduction
@deprecated Marque comme déprécié
@todo Indique une tâche à faire
@note Ajoute une note importante

Types complexes

YARD permet de spécifier des types complexes :

# @param [Array<String>] noms Liste de noms
# @param [Hash{Symbol => String}] options Options de configuration
# @return [String, nil] Un texte ou nil si non trouvé

Génération de la documentation

Générer la documentation

yard doc

Lancer un serveur de documentation

yard server

Puis visitez http://localhost:8808

Intégration avec le code

Documentation en ligne

class Product
  # @!attribute [r] price
  #   @return [Float] le prix du produit
  attr_reader :price
  
  # @!attribute name
  #   @return [String] le nom du produit
  attr_accessor :name
  
  # @!method self.find(id)
  #   Trouve un produit par son ID
  #   @param [Integer] id L'identifiant du produit
  #   @return [Product, nil] Le produit trouvé ou nil
end

Astuces avancées

Grouper des méthodes

# @!group Opérations mathématiques

# Addition de deux nombres
# @param [Numeric] a Premier nombre
# @param [Numeric] b Second nombre
# @return [Numeric] Résultat de l'addition
def add(a, b)
  a + b
end

# @!endgroup

Documenter des API privées

# @private
def méthode_interne
  # ...
end

Conclusion

YARD est un outil puissant qui permet de créer une documentation riche et détaillée pour vos projets Ruby. Sa syntaxe flexible et ses nombreuses fonctionnalités en font un choix excellent pour maintenir une documentation de qualité.

Pour plus d'informations, consultez la documentation officielle de YARD.

Rake - Automatisation des tâches

Introduction à Rake

# Installation
# gem install rake

# Qu'est-ce que Rake?
# - Outil d'automatisation de tâches en Ruby
# - Similaire à Make, mais en Ruby
# - Utilisé pour: tests, migrations, déploiements, etc.

Création d'un Rakefile

# Rakefile
require 'rake'

desc "Affiche un message de bienvenue"
task :hello do
  puts "Bonjour, Rake!"
end

desc "Nettoie les fichiers temporaires"
task :clean do
  sh "rm -rf tmp/*"
  puts "Répertoire tmp nettoyé"
end

# Exécution: rake hello

Tâches avec dépendances

desc "Compile les fichiers source"
task :compile do
  puts "Compilation..."
end

desc "Exécute les tests"
task :test => :compile do
  puts "Tests en cours..."
end

desc "Déploie l'application"
task :deploy => [:compile, :test] do
  puts "Déploiement..."
end

# Exécution: rake deploy
# Exécutera compile, puis test, puis deploy

Espaces de noms et règles

namespace :db do
  desc "Crée la base de données"
  task :create do
    puts "Création de la base de données..."
  end
  
  desc "Migre la base de données"
  task :migrate => :create do
    puts "Migration de la base de données..."
  end
end

# Exécution: rake db:migrate

# Règle pour compiler des fichiers .c en .o
rule '.o' => '.c' do |t|
  sh "gcc -c -o #{t.name} #{t.source}"
end

Tests avec RSpec

Introduction à RSpec

# Installation
# gem install rspec

# Initialisation d'un projet RSpec
# rspec --init

# Structure d'un projet avec RSpec
# ├── lib/
# │   └── calculator.rb
# └── spec/
#     ├── calculator_spec.rb
#     └── spec_helper.rb

Écriture de tests simples

# lib/calculator.rb
class Calculator
  def add(a, b)
    a + b
  end
  
  def subtract(a, b)
    a - b
  end
end

# spec/calculator_spec.rb
require 'calculator'

RSpec.describe Calculator do
  describe "#add" do
    it "adds two numbers correctly" do
      calc = Calculator.new
      expect(calc.add(2, 3)).to eq(5)
    end
  end
  
  describe "#subtract" do
    it "subtracts two numbers correctly" do
      calc = Calculator.new
      expect(calc.subtract(5, 2)).to eq(3)
    end
  end
end

Matchers RSpec

# Égalité
expect(actual).to eq(expected)    # ==
expect(actual).to eql(expected)   # eql?
expect(actual).to be(expected)    # equal?/same object

# Comparaisons
expect(actual).to be > expected
expect(actual).to be_between(min, max)
expect(actual).to match(/regex/)

# Types et classes
expect(actual).to be_an_instance_of(Expected)
expect(actual).to be_a(Expected)
expect(actual).to be_kind_of(Expected)

# Vérité
expect(actual).to be_truthy
expect(actual).to be_falsey
expect(actual).to be_nil

# Collections
expect(array).to include(expected)
expect(array).to start_with(expected)
expect(array).to end_with(expected)
expect(hash).to have_key(expected)

Hooks et contextes

RSpec.describe User do
  # Exécuté avant chaque test
  before(:each) do
    @user = User.new("Alice")
  end
  
  # Exécuté après chaque test
  after(:each) do
    # Nettoyage
  end
  
  # Exécuté une fois avant tous les tests
  before(:all) do
    # Configuration globale
  end
  
  # Contextes pour grouper les tests
  context "when newly created" do
    it "has a name" do
      expect(@user.name).to eq("Alice")
    end
    
    it "has no posts" do
      expect(@user.posts).to be_empty
    end
  end
  
  context "when posting" do
    before(:each) do
      @user.add_post("Hello world")
    end
    
    it "has one post" do
      expect(@user.posts.size).to eq(1)
    end
  end
end

Mots clés

Romain GEORGES

Open Source evangelist & Ruby enthousiast