Ruby de Newby à Ready
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