Ruby de Newby à Ready

code 7 août 2025

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

Premier programme Ruby

# hello.rb
puts "Hello, World!"

# Exécution
# ruby hello.rb

Deuxième programme en Ruby

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

IRB

Définition et rôle

IRB signifie Interactive Ruby Shell. C'est un environnement de ligne de commande qui vous permet d'écrire et d'exécuter du code Ruby de manière interactive, instruction par instruction. Il est souvent appelé un REPL (Read-Eval-Print Loop), ce qui décrit son fonctionnement cyclique :

  • Read (Lecture) : Il lit le code que vous tapez.
  • Eval (Évaluation) : Il exécute ce code.
  • Print (Affichage) : Il affiche le résultat de l'exécution.
  • Loop (Boucle) : Il revient au début, attendant la prochaine instruction.

Pourquoi l'utiliser ?

IRB est un outil incroyablement puissant et polyvalent pour tout développeur Ruby, qu'il soit débutant ou expérimenté :

  • Apprentissage et Expérimentation : C'est le "bac à sable" parfait pour tester de nouvelles idées, comprendre le comportement de Ruby, ou essayer des méthodes sans avoir à créer un fichier .rb complet.
  • Débogage Rapide : Vous pouvez rapidement vérifier la valeur d'une variable, tester une expression complexe, ou isoler un problème dans votre code.
  • Prototypage : Pour tester de petits extraits de code ou des algorithmes avant de les intégrer dans un projet plus vaste.
  • Exploration de la Bibliothèque Standard : Vous pouvez facilement explorer les méthodes disponibles sur les objets Ruby.
  • Calculatrice Avancée : Pour des calculs rapides et complexes.

Démarrage d'IRB

Comment lancer IRB

Pour démarrer IRB, ouvrez simplement votre terminal (ou invite de commande sur Windows) et tapez la commande suivante :

irb

Appuyez sur Entrée. Vous devriez voir un message de bienvenue et le prompt d'IRB.

Comprendre le prompt

Le prompt par défaut d'IRB ressemble généralement à ceci :

irb(main):001:0>

Décortiquons-le :

  • irb : Indique que vous êtes dans la session IRB.
  • (main) : Représente le contexte actuel. Au début, vous êtes dans le contexte global (main). Si vous entrez dans une classe ou un module, ce contexte peut changer.
  • 001 : C'est le numéro de la ligne de code que vous êtes sur le point de taper. Il s'incrémente à chaque nouvelle instruction.
  • :0 : Indique le niveau d'imbrication de l'instruction. 0 signifie que vous êtes au niveau le plus externe. Si vous commencez une instruction multi-lignes (comme un if ou une définition de méthode), ce nombre augmentera.
  • > : Le curseur, indiquant qu'IRB attend votre saisie.

Le Cycle REPL (Read-Eval-Print-Loop)

C'est le cœur du fonctionnement d'IRB. Chaque fois que vous tapez une instruction et appuyez sur Entrée, IRB suit ce cycle :

Read (Lecture)

IRB lit l'instruction que vous avez tapée.

irb(main):001:0> 1 + 1 # Vous tapez ceci

Eval (Évaluation)

IRB exécute le code Ruby que vous avez fourni.

Print (Affichage)

IRB affiche le résultat de l'évaluation. Le résultat est précédé de =>.

irb(main):001:0> 1 + 1
=> 2 # IRB affiche ceci

Si une instruction ne retourne pas de valeur explicite (par exemple, une affectation de variable ou un appel à puts qui affiche sur la console mais retourne nil), IRB affichera => nil.

irb(main):002:0> x = 10
=> 10 # La valeur de l'affectation est la valeur affectée

irb(main):003:0> puts "Bonjour"
Bonjour # Ceci est affiché sur la console par puts
=> nil # La méthode puts elle-même retourne nil

Loop (Boucle)

Après avoir affiché le résultat, IRB revient au prompt, prêt pour votre prochaine instruction.

Opérations Fondamentales dans IRB

Explorons quelques opérations de base que vous pouvez effectuer.

Calculs arithmétiques

irb(main):001:0> 5 * 3
=> 15

irb(main):002:0> 10 / 3 # Division entière
=> 3

irb(main):003:0> 10.0 / 3 # Division flottante
=> 3.3333333333333335

irb(main):004:0> (2 + 3) * 4
=> 20

Variables

Vous pouvez déclarer et utiliser des variables.

irb(main):005:0> nom = "Alice"
=> "Alice"

irb(main):006:0> age = 30
=> 30

irb(main):007:0> puts "Bonjour, je m'appelle #{nom} et j'ai #{age} ans."
Bonjour, je m'appelle Alice et j'ai 30 ans.
=> nil

irb(main):008:0> nom.upcase # Les variables conservent leur valeur entre les lignes
=> "ALICE"

Chaînes de caractères

irb(main):009:0> "Hello" + " World"
=> "Hello World"

irb(main):010:0> "Ruby " * 3
=> "Ruby Ruby Ruby "

irb(main):011:0> "Ceci est une chaîne".length
=> 19

Tableaux (Arrays) et Hachages (Hashes)

irb(main):012:0> fruits = ["pomme", "banane", "cerise"]
=> ["pomme", "banane", "cerise"]

irb(main):013:0> fruits[0]
=> "pomme"

irb(main):014:0> fruits << "orange" # Ajouter un élément
=> ["pomme", "banane", "cerise", "orange"]

irb(main):015:0> personne = { nom: "Bob", age: 25, ville: "Paris" }
=> {:nom=>"Bob", :age=>25, :ville=>"Paris"}

irb(main):016:0> personne[:ville]
=> "Paris"

Appel de méthodes

Vous pouvez appeler des méthodes sur n'importe quel objet.

irb(main):017:0> 10.even?
=> true

irb(main):018:0> "Bonjour".reverse
=> "ruojnoB"

irb(main):019:0> Time.now
=> 2023-10-27 10:30:45.123456 +0200 # L'heure actuelle

Conditions et boucles (aperçu)

IRB gère très bien les structures de contrôle multi-lignes.

irb(main):020:0> if 5 > 2
irb(main):021:1>   puts "5 est plus grand que 2"
irb(main):022:1> else
irb(main):023:1>   puts "5 n'est pas plus grand que 2"
irb(main):024:1> end
5 est plus grand que 2
=> nil # Le résultat de l'expression if/else est nil ici

irb(main):025:0> 3.times do |i|
irb(main):026:1>   puts "Répétition #{i + 1}"
irb(main):027:1> end
Répétition 1
Répétition 2
Répétition 3
=> 3 # La méthode times retourne le nombre de répétitions

Notez comment le prompt change (:1>) pour indiquer que vous êtes dans une instruction multi-lignes. IRB attendra le mot-clé end pour évaluer le bloc.

Fonctionnalités Avancées (Introductives)

Saisie multi-lignes

Comme vu précédemment, IRB est intelligent et sait quand une instruction n'est pas complète. Il change le prompt pour vous indiquer que vous êtes dans un bloc.

irb(main):028:0> def saluer(nom) # Début de la définition d'une méthode
irb(main):029:1>   "Bonjour, #{nom} !"
irb(main):030:1> end # Fin de la définition
=> :saluer # IRB retourne le nom du symbole de la méthode définie

irb(main):031:0> saluer("Monde")
=> "Bonjour, Monde !"

Historique des commandes

Vous pouvez naviguer dans l'historique de vos commandes avec les flèches Haut et Bas de votre clavier. C'est extrêmement utile pour réexécuter une commande, la modifier, ou simplement se souvenir de ce que vous avez fait.

Autocomplétion (Tab)

IRB offre une autocomplétion très pratique. Tapez le début d'un nom de variable, de méthode ou de classe, puis appuyez sur la touche Tab.

  • Si une seule correspondance existe, IRB complétera automatiquement.
  • Si plusieurs correspondances existent, IRB affichera une liste des options possibles.

Exemple :

irb(main):032:0> "hello".upc # Appuyez sur Tab ici

IRB complétera en "hello".upcase

irb(main):033:0> [1, 2, 3].s # Appuyez sur Tab ici

IRB affichera une liste de méthodes commençant par s (comme size, sort, shuffle, etc.).

Chargement de fichiers Ruby (load et require)

Vous pouvez charger des fichiers Ruby existants dans votre session IRB.

  • load 'chemin/vers/votre_fichier.rb' :Exemple :
    • Charge le fichier spécifié.
    • Important : Si vous modifiez le fichier et que vous le load à nouveau, IRB rechargera la nouvelle version. C'est utile pour le développement et le test itératif.
    • Le chemin peut être relatif ou absolu.
    • Charge le fichier spécifié (ou une gemme).
    • Important : require ne charge un fichier qu'une seule fois par session. Si vous essayez de le require à nouveau, il ne fera rien (sauf si le fichier a été explicitement déchargé). C'est le comportement standard pour les bibliothèques et les dépendances.
    • Pour les fichiers locaux, vous devez souvent utiliser ./ pour indiquer le chemin relatif : require './mon_script'. Notez que l'extension .rb est souvent omise avec require.

require 'chemin/vers/votre_fichier' (ou require 'nom_gemme') :Exemple :

irb(main):037:0> require './mon_script' # Charge le fichier une première fois
Bonjour depuis mon_script.rb !
=> true

irb(main):038:0> require './mon_script' # Ne fait rien la deuxième fois
=> false # Indique que le fichier n'a pas été rechargé

Dans IRB :

irb(main):034:0> load 'mon_script.rb'
Bonjour depuis mon_script.rb ! # Le puts est exécuté lors du chargement
=> true

irb(main):035:0> dire_bonjour
Bonjour depuis mon_script.rb !
=> nil

irb(main):036:0> MA_CONSTANTE
=> 123

Créez un fichier nommé mon_script.rb avec le contenu suivant :

# mon_script.rb
def dire_bonjour
  puts "Bonjour depuis mon_script.rb !"
end

MA_CONSTANTE = 123

Sortir d'IRB

Pour quitter votre session IRB, vous avez plusieurs options :

  • Tapez exit et appuyez sur Entrée.
  • Tapez quit et appuyez sur Entrée.
  • Appuyez sur Ctrl + D (sur la plupart des systèmes Unix/Linux/macOS).
irb(main):039:0> exit

Vous reviendrez à votre prompt de terminal habituel.

Conseils et Bonnes Pratiques

  • N'ayez pas peur d'expérimenter ! IRB est un environnement sûr. Vous ne pouvez pas "casser" votre système en l'utilisant. C'est l'endroit idéal pour faire des erreurs et apprendre d'elles.
  • Utilisez-le pour tester des extraits de code : Avant d'intégrer un algorithme complexe ou une nouvelle méthode dans votre application, testez-la dans IRB.
    • p affiche la représentation de débogage de l'objet.
    • pp (pretty print) est encore plus lisible pour les structures complexes. Vous devrez peut-être require 'pp' au début de votre session IRB pour l'utiliser.
  • Explorez la documentation avec ri : Bien que ri ne soit pas dans IRB, c'est un compagnon essentiel. Ouvrez un nouveau terminal et tapez ri String#reverse pour obtenir la documentation de la méthode reverse sur les chaînes de caractères.

Utilisez p ou pp pour un affichage lisible : Pour des objets complexes (comme des tableaux imbriqués ou des hachages), puts peut ne pas être suffisant.

irb(main):042:0> data = { user: { name: "John Doe", email: "john@example.com", address: { street: "123 Main St", city: "Anytown" } } }
=> {:user=>{:name=>"John Doe", :email=>"john@example.com", :address=>{:street=>"123 Main St", :city=>"Anytown"}}}

irb(main):043:0> p data
{:user=>{:name=>"John Doe", :email=>"john@example.com", :address=>{:street=>"123 Main St", :city=>"Anytown"}}}
=> {:user=>{:name=>"John Doe", :email=>"john@example.com", :address=>{:street=>"123 Main St", :city=>"Anytown"}}}

irb(main):044:0> require 'pp'
=> true

irb(main):045:0> pp data
{:user=>
  {:name=>"John Doe",
   :email=>"john@example.com",
   :address=>{:street=>"123 Main St", :city=>"Anytown"}}}
=> {:user=>{:name=>"John Doe", :email=>"john@example.com", :address=>{:street=>"123 Main St", :city=>"Anytown"}}}

Inspectez les objets : Si vous avez une variable et que vous voulez savoir ce qu'elle contient ou quelles méthodes elle a, tapez simplement le nom de la variable.

irb(main):040:0> ma_chaine = "Bonjour"
=> "Bonjour"
irb(main):041:0> ma_chaine.methods.sort # Liste toutes les méthodes disponibles sur l'objet
=> [:!, :!=, :!~, :%, :*, :+, :+@, :-, :-@, :<, :<<, :<=, :<=>, :==, :===, :=~, :>, :>=, :[], :[]=, :__id__, :__send__, :_dump, :_id2ref, :ascii_only?, :b, :between?, :bytes, :bytesize, :byteslice, :capitalize, :capitalize!, :casecmp, :casecmp?, :center, :chars, :chomp, :chomp!, :chop, :chop!, :chr, :clear, :clone, :codepoints, :concat, :count, :crypt, :delete, :delete!, :downcase, :downcase!, :dump, :each_byte, :each_char, :each_codepoint, :each_line, :empty?, :encode, :encode!, :encoding, :end_with?, :eql?, :extend, :freeze, :frozen?, :getbyte, :gsub, :gsub!, :hash, :hex, :include?, :index, :initialize_copy, :insert, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :intern, :is_a?, :itself, :kconv, :ljust, :lines, :lstrip, :lstrip!, :match, :match?, :method, :methods, :module_eval, :next, :next!, :nil?, :oct, :ord, :partition, :prepend, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :q, :rindex, :rjust, :reverse, :reverse!, :rpartition, :rstrip, :rstrip!, :scrub, :scrub!, :setbyte, :shellescape, :singleton_class, :slice, :slice!, :split, :squeeze, :squeeze!, :start_with?, :strip, :strip!, :sub, :sub!, :succ, :succ!, :sum, :swapcase, :swapcase!, :taguri, :taguri=, :taint, :tainted?, :tap, :to_c, :to_d, :to_f, :to_i, :to_json, :to_r, :to_s, :to_sym, :tr, :tr!, :tr_s, :tr_s!, :trust, :trusted?, :type, :unicode_normalize, :unicode_normalize!, :unicode_normalized?, :untaint, :untrust, :untrusted?, :upcase, :upcase!, :using_utf8?, :valid_encoding?, :yield_self, :zip]

Conclusion sur IRB

IRB est bien plus qu'une simple calculatrice pour Ruby ; c'est un environnement de développement interactif complet qui accélérera votre apprentissage et votre productivité. En maîtrisant ses bases et en l'intégrant à votre flux de travail, vous découvrirez à quel point il est indispensable pour explorer, tester et déboguer votre code Ruby.

N'hésitez pas à l'ouvrir régulièrement, à taper du code, à tester des idées, et à vous familiariser avec son fonctionnement. C'est en pratiquant que vous en tirerez le meilleur parti !

Variables et types de données

Après ce premier aperçu avec IRB, revenons en détail sur les aspects du langage

Introduction

Les variables en Ruby sont des conteneurs qui permettent de stocker des données. Elles sont essentielles pour manipuler et gérer l'information dans un programme.

Aspect technique

  • Typage dynamique : Ruby est un langage à typage dynamique, ce qui signifie que vous n'avez pas besoin de déclarer explicitement le type d'une variable. Le type est déterminé automatiquement lors de l'affectation.
  • Passage par référence : Les variables en Ruby pointent vers des objets en mémoire. Lorsqu'on affecte une variable à une autre, elles peuvent référencer le même objet ou des objets différents selon le contexte.

Types de variables

Ruby possède plusieurs types de variables, classés selon leur portée et leur usage :

a. Variables locales

  • Syntaxe : nom_variable
  • Portée : limitée à la méthode ou au bloc où elles sont déclarées.
  • Exemple : x = 10

b. Variables d'instance

  • Syntaxe : @nom_variable
  • Portée : accessible dans toutes les méthodes de l'objet.
  • Exemple : @nom

c. Variables de classe

  • Syntaxe : @@nom_variable
  • Portée : partagée entre toutes les instances de la classe.
  • Exemple : @@count

d. Variables globales

  • Syntaxe : $nom_variable
  • Portée : accessible partout dans le programme.
  • Exemple : $global_var

Affectation et utilisation

  • Affectation : variable = valeur
  • Ruby est sensible à la casse.
  • La variable prend la référence de l'objet affecté.
  • Exemple : name = "Alice"

Recommandations

  • Utilisez des variables locales autant que possible pour limiter la portée.
  • Nommez vos variables de manière descriptive pour améliorer la lisibilité.
  • Évitez d'utiliser des variables globales sauf si nécessaire.
  • Respectez la convention de nommage : snake_case (ex : mon_variable).

Bonnes pratiques

  • Initialisez toujours vos variables avant de les utiliser.
  • Évitez de réaffecter des variables de manière imprévisible.
  • Utilisez des constantes (CONSTANTE = valeur) pour des valeurs fixes.

Exemple complet

class Person
  def initialize(name)
    @name = name          # variable d'instance
  end

  def greet
    greeting = "Hello, #{@name}!"  # variable locale
    puts greeting
  end
end

person = Person.new("Alice")
person.greet  # Affiche : Hello, Alice!
# 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

Les multi-affectations en Ruby permettent d'assigner plusieurs valeurs à plusieurs variables en une seule ligne, ce qui rend le code plus concis et lisible.

Multi-affectation en Ruby

Syntaxe de base

a, b, c = 1, 2, 3

Dans cet exemple :

  • a reçoit la valeur 1
  • b reçoit la valeur 2
  • c reçoit la valeur 3

Fonctionnement

  • Ruby associe chaque variable à la valeur correspondante dans la liste.
  • Si le nombre de variables est inférieur au nombre de valeurs, les valeurs restantes sont ignorées.
  • Si le nombre de variables est supérieur au nombre de valeurs, les variables non assignées prennent la valeur nil.

Cas particuliers

Affectation avec une seule valeur

x, y = 10
  • x reçoit 10
  • y reçoit nil

Affectation avec une seule variable

a, = 1
  • a reçoit 1

Échange de valeurs

Une utilisation courante est l’échange de valeurs entre deux variables :

x = 5
y = 10
x, y = y, x
# Maintenant, x vaut 10, y vaut 5

Utilisation avec des tableaux

Vous pouvez également faire de la décomposition de tableaux :

coordinates = [10, 20]
x, y = coordinates
# x vaut 10, y vaut 20

Exemple complet

# Affectation multiple
name, age, city = "Alice", 30, "Paris"

# Échange de valeurs
a, b = 1, 2
a, b = b, a

# Décomposition d'un tableau
point = [5, 10]
x, y = point

puts "Nom: #{name}, Age: #{age}, Ville: #{city}"
puts "a: #{a}, b: #{b}"
puts "x: #{x}, y: #{y}"

Résumé

Cas Exemple Résultat
Affectation simple a, b = 1, 2 a=1, b=2
Affectation avec moins de valeurs a, b = 1 a=1, b=nil
Échange de valeurs a, b = b, a échange des valeurs
Décomposition tableau x, y = [10, 20] x=10, y=20

Si vous souhaitez des exemples plus avancés ou des cas spécifiques, je peux vous aider !

Types de données

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.to_f / 3 
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

Voici une explication détaillée des symboles en Ruby, en mettant en évidence leur différence avec les chaînes de caractères (strings), leur usage, leur relation avec les mots-clés, etc.

Les Symboles en Ruby

Qu'est-ce qu'un symbole ?

  • Un symbole en Ruby est une identification immuable et unique, représentée par un nom précédé de deux points (:).
  • Exemple : :nom, :age, :admin

Caractéristiques principales

  • Immuables : une fois créés, ils ne changent pas.
  • Uniqueness : chaque symbole est unique dans tout le programme. Si vous utilisez :nom plusieurs fois, Ruby ne crée qu'une seule instance en mémoire.
  • Rapides : leur utilisation est plus performante que celle des strings pour des identifiants ou des clés, car ils sont stockés une seule fois.

Différence avec les strings

Aspect String ("texte") Symbole (:nom)
Mutabilité Modifiable (peut changer) Immuable
Création Peut être créé à chaque utilisation Créé une seule fois, partagé
Usage principal Contenu textuel, affichage, manipulation Identifiants, clés, métadonnées
Comparaison Comparé par valeur (==) Comparé par identité (object_id)

Exemple :

str1 = "hello"
str2 = "hello"
sym1 = :hello
sym2 = :hello

puts str1 == str2        # true (valeur identique)
puts sym1 == sym2        # true (valeur identique)
puts str1.object_id == str2.object_id  # false (différentes instances)
puts sym1.object_id == sym2.object_id  # true (même instance)

Usage courant des symboles

1. Clés dans les Hashes

person = { name: "Alice", age: 30 }
# ou
person = { :name => "Alice", :age => 30 }

2. Méthodes et options

def greet(name:, age:)
  puts "Bonjour #{name}, vous avez #{age} ans."
end
greet(name: "Bob", age: 25)

3. Méta-programmation et métadonnées

Les symboles sont souvent utilisés pour représenter des noms de méthodes, des attributs, ou des options.

Les symboles et les mots-clés

  • Les mots-clés en Ruby (comme if, while, def, etc.) sont réservés par le langage.
  • Les symboles sont des identifiants que vous pouvez définir vous-même, mais ils ne sont pas des mots-clés réservés.
  • Exemple : :if est un symbole, mais if est un mot-clé.

Résumé

Aspect Symbole (:nom) String ("texte")
Immuable Oui Non
Créé une seule fois Oui Non
Usage principal Identifiants, clés, métadonnées Contenu textuel, affichage
Comparaison Par identité (object_id) Par valeur (==)

Les Ranges (Intervalles)

Un Range en Ruby représente un intervalle entre deux valeurs. C'est un concept puissant qui permet de manipuler des séquences de valeurs de manière élégante.

Syntaxe des Ranges

# Range inclusif (inclut la dernière valeur)
1..5    # représente les nombres 1, 2, 3, 4, 5

# Range exclusif (exclut la dernière valeur)
1...5   # représente les nombres 1, 2, 3, 4

Contextes d'usage des Ranges

1.Itération

(1..5).each { |n| puts n }  # Affiche les nombres de 1 à 5

# Avec un pas spécifique
(1..10).step(2) { |n| puts n }  # Affiche 1, 3, 5, 7, 9

2. Sélection dans les collections

array = [10, 20, 30, 40, 50]
array[1..3]  # => [20, 30, 40]

3. Tests d'appartenance

age = 25
if (18..65).include?(age)
  puts "Âge actif"
end

4. Conditions case

case score
when 0..59
  puts "Échec"
when 60..69
  puts "Passable"
when 70..79
  puts "Bien"
when 80..100
  puts "Excellent"
end

voir : sujet sur case en Ruby

5. Génération de séquences

letters = ('a'..'e').to_a  # => ["a", "b", "c", "d", "e"]

Les 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
fruits.map! { |fruit| fruit.upcase } # altère le tableau
fruits.map { |fruit| fruit.upcase! } # !! altère le tableau

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

Struct

La classe Struct permet de créer rapidement des structures de données avec des attributs nommés.

# Définition
Person = Struct.new(:name, :age, :city)

# Utilisation
alice = Person.new("Alice", 30, "Paris")
puts alice.name  # => "Alice"
alice.age = 31  # Modification

OpenStruct

Similaire à Struct mais plus flexible (nécessite require 'ostruct').

require 'ostruct'

person = OpenStruct.new
person.name = "Bob"
person.age = 42
puts person.name  # => "Bob"

Les structures de contrôle

Les Structures Conditionnelles

Elles permettent d'exécuter différents blocs de code en fonction de la vérité ou de la fausseté d'une ou plusieurs conditions.

if / elsif / else

La structure conditionnelle la plus courante.

Exemple :

age = 20

if age >= 18
  puts "Vous êtes majeur."
elsif age >= 13
  puts "Vous êtes un adolescent."
else
  puts "Vous êtes un enfant."
end

Syntaxe :

if condition_1
  # Code à exécuter si condition_1 est vraie
elsif condition_2
  # Code à exécuter si condition_1 est fausse ET condition_2 est vraie
else
  # Code à exécuter si toutes les conditions précédentes sont fausses
end

unless / else

L'inverse de if. Le bloc de code est exécuté si la condition est fausse. Souvent utilisé pour améliorer la lisibilité lorsque la condition est naturellement exprimée de manière négative.

Exemple :

est_connecte = false

unless est_connecte
  puts "Veuillez vous connecter."
else
  puts "Bienvenue !"
end

Syntaxe :

unless condition
  # Code à exécuter si la condition est fausse
else
  # Code à exécuter si la condition est vraie (le 'else' est optionnel)
end

case / when / else

Utilisé pour gérer plusieurs conditions basées sur la valeur d'une seule expression. Plus propre que de multiples elsif imbriqués.

Exemple :

jour_semaine = "Mardi"

case jour_semaine
when "Lundi"
  puts "Début de semaine, courage !"
when "Mardi", "Mercredi", "Jeudi"
  puts "Milieu de semaine."
when "Vendredi"
  puts "C'est le week-end bientôt !"
else
  puts "C'est le week-end !"
end

Syntaxe :

case expression
when valeur_1
  # Code si expression == valeur_1
when valeur_2, valeur_3
  # Code si expression == valeur_2 OU expression == valeur_3
when (valeur_min..valeur_max)
  # Code si expression est dans la plage
else
  # Code si aucune des conditions 'when' n'est remplie
end

Formes Modificatrices (Modifier Forms)

Ruby permet une syntaxe plus concise pour les conditions simples sur une seule ligne.

Exemple :

puts "Accès autorisé." if age >= 18
puts "Accès refusé." unless age >= 18

Syntaxe :

instruction if condition
instruction unless condition

Les Structures de Boucle (Itération)

Elles permettent de répéter un bloc de code plusieurs fois.

while

Exécute un bloc de code tant qu'une condition est vraie.

Exemple :

compteur = 0
while compteur < 5
  puts "Compteur : #{compteur}"
  compteur += 1
end

Syntaxe :

until

Exécute un bloc de code tant qu'une condition est fausse (l'inverse de while).

Exemple :

compteur = 0
until compteur == 5
  puts "Compteur : #{compteur}"
  compteur += 1
end

Syntaxe :

until condition
  # Code à répéter
end

for (et pourquoi each est souvent préféré)

Itère sur les éléments d'une collection.

  • Note Importante : En Ruby, for est moins idiomatique que les méthodes d'itération des Enumerable (comme each). La principale raison est que for ne crée pas un nouveau scope pour la variable d'itération, ce qui peut entraîner des fuites de variables.

Exemple :

nombres = [1, 2, 3]
for n in nombres
  puts n
end

Syntaxe :

for element in collection
  # Code à exécuter pour chaque element
end

each et autres méthodes d'énumération

La manière la plus courante et idiomatique d'itérer en Ruby. Ces méthodes sont disponibles sur les objets qui incluent le module Enumerable (Arrays, Hashes, Ranges, etc.).
  • Autres méthodes utiles :
    • map (ou collect) : Transforme chaque élément et retourne un nouveau tableau.
    • select (ou filter) : Sélectionne les éléments qui satisfont une condition.
    • reject : Rejette les éléments qui satisfont une condition.
    • reduce (ou inject) : Combine tous les éléments d'une collection en un seul résultat.

each : Itère sur chaque élément.

nombres = [1, 2, 3]
nombres.each do |n|
  puts n
end

# Avec un hash
personne = { nom: "Alice", age: 30 }
personne.each do |cle, valeur|
  puts "#{cle}: #{valeur}"
end

loop do

Crée une boucle infinie qui doit être explicitement arrêtée avec break. Utile pour des boucles d'événements ou des processus continus.

Exemple :

reponse = ""
loop do
  puts "Entrez 'quitter' pour sortir :"
  reponse = gets.chomp
  break if reponse == "quitter"
  puts "Vous avez entré : #{reponse}"
end
puts "Boucle terminée."

Syntaxe :

loop do
  # Code à répéter indéfiniment
  # ...
  break if condition_de_sortie
end

Méthodes d'itération numérique

Des méthodes pratiques pour itérer un nombre spécifique de fois ou sur une plage de nombres.

step : Itère avec un pas spécifique.

0.step(10, 2) do |n|
  puts n # Affiche 0, 2, 4, 6, 8, 10
end

downto : Itère d'un nombre de départ jusqu'à un nombre de fin (décroissant).

3.downto(1) do |n|
  puts n # Affiche 3, 2, 1
end

upto : Itère d'un nombre de départ jusqu'à un nombre de fin.

1.upto(3) do |n|
  puts n # Affiche 1, 2, 3
end

times : Exécute un bloc un nombre de fois donné.

5.times do |i|
  puts "Itération #{i}" # i va de 0 à 4
end

Les Modificateurs de Boucle

Ces mots-clés permettent de contrôler le flux d'exécution à l'intérieur d'une boucle.

break

Sort immédiatement de la boucle en cours.

Exemple :

nombres = [1, 2, 3, 4, 5]
nombres.each do |n|
  break if n == 3
  puts n
end
# Affiche 1, 2

next

Passe à l'itération suivante de la boucle, en ignorant le reste du code dans l'itération actuelle.

Exemple :

nombres = [1, 2, 3, 4, 5]
nombres.each do |n|
  next if n % 2 == 0 # Passe les nombres pairs
  puts n
end
# Affiche 1, 3, 5

redo

Redémarre l'itération actuelle de la boucle sans réévaluer la condition de la boucle. Moins courant et souvent un signe de logique complexe.

Exemple :

i = 0
while i < 3
  puts "i est #{i}"
  i += 1
  redo if i == 2 # Redémarre l'itération quand i est 2
end
# Affiche:
# i est 0
# i est 1
# i est 2
# i est 2 (à cause du redo)
# i est 3 (quitte la boucle)

retry

Utilisé principalement dans les blocs rescue pour retenter l'exécution du bloc begin depuis le début.

Exemple (voir section 5 pour le contexte begin/rescue) :

tentatives = 0
begin
  puts "Tentative ##{tentatives + 1}"
  raise "Erreur simulée" if tentatives < 2
  puts "Opération réussie !"
rescue
  tentatives += 1
  puts "Erreur détectée. Nouvelle tentative..."
  retry if tentatives < 3
  puts "Échec après plusieurs tentatives."
end

La Gestion des Erreurs (Exceptions)

Ruby utilise un mécanisme d'exceptions pour gérer les erreurs et les situations imprévues.

begin / rescue / else / ensure

  • begin : Le bloc de code où une exception pourrait se produire.
  • rescue : Le bloc de code exécuté si une exception se produit dans le bloc begin. Vous pouvez spécifier le type d'exception à intercepter.
  • else : Le bloc de code exécuté si aucune exception ne se produit dans le bloc begin.
  • ensure : Le bloc de code qui est toujours exécuté, qu'une exception se produise ou non. Idéal pour le nettoyage (fermeture de fichiers, connexions, etc.).

Exemple :

def diviser(a, b)
  begin
    resultat = a / b
    puts "Le résultat est : #{resultat}"
  rescue ZeroDivisionError
    puts "Erreur : Division par zéro impossible !"
  rescue TypeError => e
    puts "Erreur de type : #{e.message}. Veuillez utiliser des nombres."
  else
    puts "La division s'est déroulée sans problème."
  ensure
    puts "Fin de l'opération de division."
  end
end

diviser(10, 2)
puts "---"
diviser(10, 0)
puts "---"
diviser(10, "deux")

Syntaxe :

begin
  # Code qui pourrait lever une exception
  # ...
rescue TypeErreur1 => e
  # Gérer TypeErreur1
  puts "Erreur de type 1 : #{e.message}"
rescue TypeErreur2
  # Gérer TypeErreur2
  puts "Erreur de type 2"
rescue => e # Intercepte toute autre exception (StandardError par défaut)
  puts "Une erreur inattendue s'est produite : #{e.class} - #{e.message}"
else
  # Exécuté si AUCUNE exception n'est levée dans le bloc 'begin'
  puts "Opération réussie sans erreur."
ensure
  # Exécuté TOUJOURS, que l'exception soit levée ou non
  puts "Nettoyage ou finalisation."
end

Récapitulatif

Nous avons exploré les structures de contrôle fondamentales en Ruby :

  • Conditionnelles : if, unless, case pour la prise de décision.
  • Boucles : while, until, each, loop do, times, etc., pour la répétition.
  • Modificateurs de Boucle : break, next, redo, retry pour un contrôle fin du flux.
  • Gestion des Erreurs : begin/rescue/else/ensure pour la robustesse du programme.

Bonnes Pratiques

  • Lisibilité : Choisissez la structure qui rend votre intention la plus claire. unless pour les négations, case pour de multiples elsif.
  • Idiomatique Ruby : Préférez each et les méthodes Enumerable à for pour l'itération.
  • Gestion des Erreurs : Interceptez les exceptions spécifiques plutôt que toutes les exceptions génériques (rescue => e) pour une meilleure gestion.
  • Simplicité : Évitez les boucles et conditions trop imbriquées ; refactorisez si nécessaire.

Maîtriser ces structures est essentiel pour écrire des programmes Ruby efficaces, lisibles et robustes.

Focus sur le statement case en Ruby

Le case est une structure de contrôle puissante en Ruby qui permet d'évaluer une expression selon plusieurs conditions. C'est une alternative élégante aux structures if/elsif/else multiples, particulièrement lorsque vous comparez une même variable à différentes valeurs.

Syntaxe de base

case expression
when valeur1
  # code exécuté si expression == valeur1
when valeur2
  # code exécuté si expression == valeur2
else
  # code exécuté si aucune condition n'est satisfaite
end

Fonctionnement détaillé

Lorsque Ruby évalue un statement case, il compare l'expression à chaque condition when en utilisant la méthode === (triple égal). Cette méthode a un comportement spécial selon le type de l'objet qui l'appelle.

Exemples d'utilisation

1. Comparaison simple avec des valeurs

day = "Lundi"

case day
when "Lundi"
  puts "Début de semaine"
when "Mercredi"
  puts "Milieu de semaine"
when "Vendredi"
  puts "Fin de semaine"
else
  puts "Autre jour"
end
# Affiche: "Début de semaine"

2. Utilisation avec des Ranges

note = 85

case note
when 0..49
  puts "Échec"
when 50..64
  puts "Passable"
when 65..74
  puts "Bien"
when 75..89
  puts "Très bien"
when 90..100
  puts "Excellent"
else
  puts "Note invalide"
end
# Affiche: "Très bien"

3. Conditions multiples par clause when

jour = "Samedi"

case jour
when "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"
  puts "Jour ouvrable"
when "Samedi", "Dimanche"
  puts "Week-end"
end
# Affiche: "Week-end"

4. Utilisation avec des Regexp (expressions régulières)

fruit = "pomme"

case fruit
when /^p/
  puts "Commence par p"
when /^b/
  puts "Commence par b"
else
  puts "Commence par une autre lettre"
end
# Affiche: "Commence par p"

5. Utilisation avec des Classes

objet = [1, 2, 3]

case objet
when String
  puts "C'est une chaîne"
when Array
  puts "C'est un tableau"
when Hash
  puts "C'est un hash"
else
  puts "C'est autre chose"
end
# Affiche: "C'est un tableau"

6. Case sans expression (pattern matching implicite)

case
when Time.now.hour < 12
  puts "Bon matin"
when Time.now.hour < 18
  puts "Bon après-midi"
else
  puts "Bonsoir"
end
# Affiche selon l'heure actuelle

7. Pattern matching (Ruby 2.7+)

resultat = [200, "OK", {id: 123}]

case resultat
when [200, _, hash] if hash[:id]
  puts "Succès avec ID: #{hash[:id]}"
when [404, *]
  puts "Non trouvé"
when [500..599, *]
  puts "Erreur serveur"
else
  puts "Autre réponse"
end
# Affiche: "Succès avec ID: 123"

Particularités importantes

  1. Retour de valeur : Comme la plupart des structures en Ruby, case retourne la valeur de la dernière expression évaluée.
statut = case niveau
         when 1 then "Débutant"
         when 2 then "Intermédiaire"
         when 3 then "Avancé"
         else "Inconnu"
         end
  1. Opérateur === : Le case utilise l'opérateur === pour les comparaisons, ce qui permet des comportements spéciaux:
    • Range === valeur vérifie si la valeur est dans l'intervalle
    • Regexp === string vérifie si la chaîne correspond à l'expression régulière
    • Class === objet vérifie si l'objet est une instance de la classe
  2. Évaluation séquentielle : Ruby évalue les conditions when dans l'ordre et s'arrête à la première correspondance.

Avantages du case par rapport aux if/elsif/else

  • Lisibilité : Plus clair quand on compare une même variable à plusieurs valeurs
  • Expressivité : Exploite la puissance de l'opérateur === pour des comparaisons sophistiquées
  • Concision : Permet d'écrire du code plus compact, surtout avec la syntaxe then
Le statement case est l'une des structures qui illustre parfaitement la philosophie de Ruby : offrir aux développeurs des outils élégants et expressifs pour écrire du code lisible et maintenable.

Les 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)
  greetings  = { fr:  "Bonjour", en: "Hello" }
  puts "#{greeting[language]}, #{name}!"
  end
end

# Avec paramètres et keywords
def display(data, upcase: false)
  data.up
  puts  (upcase)? data : data.upcase 
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("Alice")     # Bonjour, Alice!
greet_with_language("Bob", :en)  # Hello, Bob!


sum = add(5, 3)                  # 8

product = multiply(4, 2)         # 8


display("Hello")                 # Hello
display("Bonjour", upcase: true) # BONJOUR

Blocs

# bloc type en ruby avec les contrôle d'exceptions
begin
  # Ouverture du fichier en mode lecture
  File.open('mon_fichier.txt', 'r') do |fichier|
    # Lecture et affichage du contenu
    puts "Contenu du fichier :"
    fichier.each_line do |ligne|
      puts ligne
    end
  end
rescue Errno::ENOENT
  # Gestion de l'erreur si le fichier n'existe pas
  puts "Erreur : Le fichier n'existe pas."
rescue Errno::EACCES
  # Gestion de l'erreur si le fichier n'est pas accessible en lecture
  puts "Erreur : Impossible d'accéder au fichier (problème de permissions)."
rescue IOError
  # Gestion des erreurs d'entrée/sortie
  puts "Erreur : Problème lors de la lecture du fichier."
rescue StandardError => e
  # Capture toute autre erreur standard
  puts "Erreur inattendue : #{e.message}"
  puts e.backtrace
ensure
  # Ce bloc sera toujours exécuté, que des erreurs se produisent ou non
  puts "Opération terminée."
end


# Bloc avec do...end en closure
[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" }

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

Différence entre Proc et Lambda en Ruby

Les Proc et Lambda sont deux types de closures en Ruby, mais ils présentent plusieurs différences importantes :

Vérification du nombre d'arguments

# Lambda - strict sur le nombre d'arguments
my_lambda = lambda { |x, y| puts "#{x} et #{y}" }
# my_lambda.call(1)      # Erreur: ArgumentError (wrong number of arguments)
# my_lambda.call(1, 2, 3) # Erreur: ArgumentError (wrong number of arguments)
my_lambda.call(1, 2)     # Fonctionne: "1 et 2"

# Proc - flexible sur le nombre d'arguments
my_proc = Proc.new { |x, y| puts "#{x} et #{y}" }
my_proc.call(1)          # Fonctionne: "1 et nil"
my_proc.call(1, 2, 3)    # Fonctionne: "1 et 2" (ignore le 3ème argument)

Comportement du return

def test_lambda
  my_lambda = -> { return "Retour du lambda" }
  result = my_lambda.call
  return "Retour de la méthode: #{result}"
end

def test_proc
  my_proc = Proc.new { return "Retour du proc" }
  result = my_proc.call
  return "Cette ligne ne sera jamais exécutée"
end

puts test_lambda  # Affiche: "Retour de la méthode: Retour du lambda"
puts test_proc    # Affiche: "Retour du proc"

Syntaxe de création

# Création d'un Lambda
lambda1 = lambda { |x| x * 2 }
lambda2 = -> (x) { x * 2 }  # Syntaxe alternative (Ruby 1.9+)

# Création d'un Proc
proc1 = Proc.new { |x| x * 2 }
proc2 = proc { |x| x * 2 }  # Syntaxe alternative

Méthode lambda?

my_lambda = -> { puts "Lambda" }
my_proc = Proc.new { puts "Proc" }

puts my_lambda.lambda?  # true
puts my_proc.lambda?    # false

Résumé des différences

Caractéristique Lambda Proc
Vérification des arguments Stricte (comme les méthodes) Flexible (arguments manquants = nil, excédentaires ignorés)
Comportement du return Retourne au contexte appelant Retourne de la méthode englobante
Syntaxe lambda { } ou -> { } Proc.new { } ou proc { }
Identité lambda? == true lambda? == false
En général, les lambdas sont plus proches des méthodes normales en termes de comportement, tandis que les Procs offrent plus de flexibilité mais peuvent avoir des effets de bord inattendus avec 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.

En Ruby même ce qui ressemble à des opérateurs ne l'est pas !
# Ceci...
p 1 + 2

# est équivalent à...
p 1.+(2)

# qui est la même chose que :
p 1.send "+", 2

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]

Le Splat Operator en Ruby

Le splat operator (opérateur d'éclatement) est représenté par l'astérisque * en Ruby. C'est un outil puissant qui permet de manipuler des collections d'éléments de différentes façons.

Splat simple (*)

Conversion d'une collection en arguments séparés

def addition(a, b, c)
  a + b + c
end

nombres = [1, 2, 3]
puts addition(*nombres)  # Équivalent à addition(1, 2, 3)

Capture d'un nombre variable d'arguments

def afficher_tous(*elements)
  elements.each { |e| puts e }
end

afficher_tous("pomme", "orange", "banane")  
      # Capture tous les arguments dans un tableau

Décomposition d'un tableau

premier, *milieu, dernier = [1, 2, 3, 4, 5]
puts "Premier: #{premier}"    # 1
puts "Milieu: #{milieu}"      # [2, 3, 4]
puts "Dernier: #{dernier}"    # 5

Fusion de tableaux

array1 = [1, 2, 3]
array2 = [4, 5]
combined = [*array1, *array2]  # [1, 2, 3, 4, 5]

Double splat (**)

Le double splat s'applique aux hashes et permet de manipuler des paires clé-valeur.

Conversion d'un hash en arguments nommés

def profil(nom:, age:, ville:)
  puts "#{nom}, #{age} ans, habite à #{ville}"
end

info = { nom: "Alice", age: 30, ville: "Paris" }
profil(**info)  # Équivalent à profil(nom: "Alice", age: 30, ville: "Paris")

Capture d'arguments nommés variables

def options(obligatoire:, **autres)
  puts "Option obligatoire: #{obligatoire}"
  puts "Autres options: #{autres}"
end

options(obligatoire: "valeur", optionnel1: "test", optionnel2: 123)
# Autres options: {:optionnel1=>"test", :optionnel2=>123}

Fusion de hashes

hash1 = { a: 1, b: 2 }
hash2 = { c: 3, d: 4 }
combined = { **hash1, **hash2 }  # { a: 1, b: 2, c: 3, d: 4 }

Cas d'utilisation avancés

Combinaison des deux types de splat

def methode(*args, **kwargs)
  puts "Arguments positionnels: #{args}"
  puts "Arguments nommés: #{kwargs}"
end

methode(1, 2, 3, a: "A", b: "B")
# Arguments positionnels: [1, 2, 3]
# Arguments nommés: {:a=>"A", :b=>"B"}

Transfert d'arguments à une autre méthode

def wrapper(*args, **kwargs)
  puts "Avant l'appel"
  autre_methode(*args, **kwargs)
  puts "Après l'appel"
end

Utilisation avec des opérateurs de décomposition

range = (1..5)
puts [*range]  # [1, 2, 3, 4, 5]

hash = { a: 1, b: 2 }
nouvelles_options = { c: 3, **hash }  # { c: 3, a: 1, b: 2 }
Le splat operator est un outil essentiel en Ruby qui rend le code plus flexible et expressif, particulièrement utile pour créer des API élégantes et des méthodes à arguments variables.

Programmation full Objet

L'aspect "Full Object" de Ruby : Une Explication Détaillée

Qu'est-ce que "Full Object" en Ruby ?

En Ruby, "Full Object" (ou "Tout est objet") signifie que pratiquement tout élément du langage est un objet, y compris les types primitifs comme les nombres, les booléens, et même nil (l'équivalent de null dans d'autres langages). Chaque valeur peut recevoir des messages (appels de méthodes) et possède une identité propre.

Origines de cette approche

Influences historiques

Ruby a été créé par Yukihiro "Matz" Matsumoto en 1995, qui s'est inspiré de plusieurs langages :

  • Smalltalk : Principal inspirateur pour le paradigme "tout est objet"
  • Lisp : Pour certains aspects fonctionnels
  • Perl : Pour la flexibilité et les expressions régulières
  • Python : Pour la syntaxe claire

Matz voulait créer un langage qui mettrait l'accent sur la programmation orientée objet pure, tout en restant agréable à utiliser.

Philosophie de conception

La philosophie de Ruby est centrée sur le "Principe de moindre surprise" (Principle of Least Surprise). Matz a conçu Ruby pour que le langage fonctionne comme les programmeurs s'y attendraient intuitivement, en privilégiant la lisibilité et l'élégance du code.

Comment fonctionne l'approche "Full Object"

Tout est vraiment un objet

# Même les nombres sont des objets
42.class          # => Integer
42.methods.count  # => retourne un nombre important de méthodes

# Les chaînes de caractères aussi
"hello".upcase    # => "HELLO"

# Même true, false et nil sont des objets
true.class        # => TrueClass
false.class       # => FalseClass
nil.class         # => NilClass

Classes et métaclasses

Ruby implémente son système d'objets via une hiérarchie sophistiquée :

  1. Chaque objet est une instance d'une classe
  2. Chaque classe est elle-même un objet, instance de la classe Class
  3. Chaque classe a une "métaclasse" (ou "singleton class") qui stocke ses méthodes de classe
# Définition d'une classe
class Person
  def initialize(name)
    @name = name
  end
  
  def greet
    "Hello, I'm #{@name}"
  end
end

# La classe est elle-même un objet
Person.class  # => Class
Person.is_a?(Object)  # => true

Rappel : Méthodes et messages

En Ruby, appeler une méthode est conceptuellement "envoyer un message" à un objet :

# Ces deux expressions sont équivalentes
"hello".upcase
"hello".send(:upcase)

Avantages de l'approche "Full Object"

  1. Cohérence : Pas d'exceptions à la règle "tout est objet"
  2. Expressivité : Permet d'écrire du code plus élégant et concis
  3. Métaprogrammation facilitée : Modification dynamique des classes et objets
  4. Extensibilité : Possibilité d'étendre même les types de base

Implications pratiques

Métaprogrammation

# Ajout d'une méthode à une classe existante
class String
  def shout
    self.upcase + "!"
  end
end

"hello".shout  # => "HELLO!"

Duck Typing

Ruby favorise le "duck typing" : si un objet répond aux méthodes attendues, son type exact importe peu.

def print_length(object)
  puts object.length
end

print_length("hello")     # => 5
print_length([1, 2, 3])   # => 3
print_length({a: 1, b: 2}) # => 2

L'approche "Full Object" de Ruby est fondamentale à son identité. Elle offre une cohérence conceptuelle et une grande flexibilité, permettant aux développeurs d'écrire du code expressif et élégant. Cette philosophie a influencé de nombreux langages modernes et reste l'un des aspects les plus appréciés de Ruby.

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

  def sex 
     @sex
  end

  def sex=(sex)
     @sex = sex
  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
  attr_accessor :carnivorous, :coat
  def initialize
     @carnivorous = false
     @coat = true
  end
  
  def speak
    "Je suis un animal"
  end
end

class Dog < Animal
  def initialize
    super
    @carnivorous = true
  end

  def speak
    "Woof !"
  end
end

class Fish < Animal
 def initialize
    super
    @coat = false
  end

  def speak
    "Blup !"
  end
end

puts Animal.new.speak   # Je suis un animal
dingo =  Dog.new ; nemo = Fish::new
puts dingo.speak        # Woof!
p dingo.carnivorous     # true    
p dingo.coat            # true 
puts nemo.speak         # Meow!
p nemo.carnivorous      # false    
p nemo.coat             # false 

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

En Ruby, les protections de méthode (ou niveaux de visibilité) permettent de contrôler l'accès aux méthodes d'une classe. Il existe trois niveaux de protection :

Public (par défaut)

Les méthodes publiques sont accessibles partout, sans restriction.

class Voiture
  def demarrer  # méthode publique par défaut
    puts "Vroum vroum!"
  end
end

ma_voiture = Voiture.new
ma_voiture.demarrer  # Fonctionne sans problème

Protected

Les méthodes protégées sont accessibles :

  • À l'intérieur de la classe qui les définit
  • Dans les classes héritées
  • Par d'autres instances de la même classe
class Personne
  attr_reader :nom
  
  def initialize(nom, age)
    @nom = nom
    @age = age
  end
  
  def plus_age_que?(autre_personne)
    @age > autre_personne.age_actuel
  end
  
  protected
  
  def age_actuel  # méthode protégée
    @age
  end
end

alice = Personne.new("Alice", 30)
bob = Personne.new("Bob", 25)

puts alice.plus_age_que?(bob)  # true
# puts alice.age_actuel  # Erreur! Méthode protégée

Private

Les méthodes privées sont les plus restrictives :

  • Accessibles uniquement à l'intérieur de la classe qui les définit
  • Ne peuvent pas être appelées avec un receveur explicite (même self)
class CompteBancaire
  def initialize(solde_initial)
    @solde = solde_initial
  end
  
  def deposer(montant)
    @solde += montant
    actualiser_historique("Dépôt", montant)
    afficher_solde
  end
  
  def retirer(montant)
    if montant <= @solde
      @solde -= montant
      actualiser_historique("Retrait", montant)
      afficher_solde
    else
      puts "Fonds insuffisants!"
    end
  end
  
  private
  
  def actualiser_historique(type, montant)
    puts "#{type} de #{montant}€ effectué"
  end
  
  def afficher_solde
    puts "Solde actuel: #{@solde}€"
  end
end

compte = CompteBancaire.new(1000)
compte.deposer(500)   # Fonctionne et appelle des méthodes privées en interne
# compte.afficher_solde  # Erreur! Méthode privée
Ces protections aident à implémenter l'encapsulation, un principe fondamental de la programmation orientée objet, en cachant les détails d'implémentation et en exposant uniquement les interfaces nécessaires.

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
Librairie cliente HTTP pour faire des requêtes sur un web service, dans notre exemple l'API Github.

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

Installation de bundle pour gérer les dépendances
# gem install bundler

Initialisation d'un bundle

$ bundle init 
  => Writing new Gemfile to /Users/romain/Gemfile
Cette commande créée un fichier Gemfile qui va lister les dépendances :
# frozen_string_literal: true

source "https://rubygems.org"

# gem "rails"

Ajout de la dépendances

$ bundle add httparty
  => Fetching gem metadata from https://rubygems.org/...........

Pour installer les dépendances on peut faire un :

$ bundle update
  => Resolving dependencies...
  => Bundle updated!
  

Exemple pratique avec HTTParty

require 'httparty'
require 'json'

class GithubClient
  include HTTParty
  base_uri 'https://api.github.com'

  def initialize(token = nil)
    @headers = {
      'Accept' => 'application/vnd.github+json',
      'X-GitHub-Api-Version' => '2022-11-28',
      '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('lecid')
puts "Nom: #{user['name']}"
puts "Bio: #{user['bio']}"

pour executer le script standalone

$ bundle exec ruby test.rb 
  => Nom: Romain GEORGES
  => Bio: Ruby enthousiast
  => Open source evangelist

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

ou l'ajouter dans les Gemspec ou dans le Gemfile

Remarque : Un fichier gemspec sert à "builder" un gem et dans un gemfile on peut faire une référence des dépendances depuis le fichier Gemspec, tel que dans le Gemfile, on a :
# frozen_string_literal: true

source 'https://rubygems.org'

gemspec

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

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

Qu'est-ce que Rake ?

Rake est l'acronyme de "Ruby Make". C'est un utilitaire de construction et un gestionnaire de tâches écrit en Ruby. Son objectif principal est d'automatiser les tâches répétitives et complexes dans un projet Ruby (ou même d'autres types de projets).

Contrairement à des outils comme Make (souvent utilisé en C/C++), Rake utilise la syntaxe Ruby pour définir ses tâches, ce qui le rend très flexible et puissant, car vous pouvez utiliser toute la puissance du langage Ruby à l'intérieur de vos tâches.

Pourquoi utiliser Rake ?

  • Automatisation : Exécuter des séquences d'actions complexes avec une seule commande.
  • Cohérence : Assurer que les tâches sont toujours exécutées de la même manière, réduisant les erreurs humaines.
  • Lisibilité : Les tâches sont définies en Ruby, ce qui les rend souvent plus lisibles et maintenables que des scripts shell complexes.
  • Dépendances : Gérer facilement les dépendances entre les tâches, garantissant que les prérequis sont satisfaits avant l'exécution d'une tâche.
  • Portabilité : Étant basé sur Ruby, Rake est multiplateforme.

Concepts Clés de Rake

Le Rakefile

Toutes les tâches Rake sont définies dans un fichier nommé Rakefile (sans extension) à la racine de votre projet. C'est là que Rake va chercher les définitions de tâches.

Les Tâches (task)

Une tâche est l'unité fondamentale de Rake. Elle représente une action spécifique à exécuter.

# Rakefile

# Définition d'une tâche simple
task :hello do
  puts "Bonjour depuis Rake !"
end

Pour exécuter cette tâche, vous iriez dans votre terminal à la racine du projet et taperiez :

rake hello

Les Descriptions (desc)

Il est fortement recommandé d'ajouter une description à chaque tâche. Cela rend votre Rakefile auto-documenté et permet à Rake d'afficher une liste des tâches disponibles avec leurs descriptions.

# Rakefile

desc "Affiche un message de salutation"
task :hello do
  puts "Bonjour depuis Rake !"
end

Pour voir la liste des tâches avec leurs descriptions :

rake -T

Les Dépendances

Les tâches peuvent dépendre d'autres tâches. Cela signifie qu'une tâche ne sera exécutée qu'après que toutes ses dépendances aient été exécutées avec succès.

# Rakefile

desc "Prépare l'environnement (ex: crée des dossiers)"
task :prepare do
  puts "1. Préparation de l'environnement..."
  # Simuler une action
  sleep 0.5
end

desc "Compile le code source"
task :compile => :prepare do
  puts "2. Compilation du code..."
  # Simuler une action
  sleep 0.5
end

desc "Exécute les tests"
task :test => :compile do
  puts "3. Exécution des tests..."
  # Simuler une action
  sleep 0.5
end

desc "Déploie l'application"
task :deploy => :test do
  puts "4. Déploiement de l'application..."
  # Simuler une action
  sleep 0.5
  puts "Déploiement terminé !"
end

Si vous exécutez rake deploy, Rake s'assurera que test est exécuté, qui à son tour s'assurera que compile est exécuté, qui lui-même s'assurera que prepare est exécuté. L'ordre d'exécution sera donc : prepare, compile, test, deploy.

Les Espaces de Noms (namespace)

Pour organiser les tâches et éviter les conflits de noms, vous pouvez les regrouper dans des espaces de noms. C'est particulièrement utile dans les grands projets (comme Rails).

# Rakefile

namespace :db do
  desc "Migre la base de données"
  task :migrate do
    puts "Migration de la base de données..."
    # Logique de migration ici
  end

  desc "Remet la base de données à zéro (drop, create, migrate)"
  task :reset do
    puts "Réinitialisation de la base de données..."
    # Logique de réinitialisation ici
  end
end

namespace :app do
  desc "Démarre le serveur d'application"
  task :start do
    puts "Démarrage du serveur..."
  end
end

Pour exécuter ces tâches :

rake db:migrate
rake db:reset
rake app:start

Tâches avec Arguments

Les tâches peuvent accepter des arguments, ce qui les rend plus flexibles.

# Rakefile

desc "Salue une personne spécifique. Usage: rake greet[Nom]"
task :greet, [:name] do |t, args|
  name = args[:name] || "monde" # Valeur par défaut si l'argument n'est pas fourni
  puts "Bonjour, #{name} !"
end

Exécution :

rake greet[Alice]
# Output: Bonjour, Alice !

rake greet
# Output: Bonjour, monde !

Tâche par Défaut (task :default)

Vous pouvez définir une tâche par défaut qui sera exécutée si vous tapez simplement rake sans spécifier de nom de tâche.

# Rakefile

desc "Exécute la tâche par défaut (souvent 'test' ou 'build')"
task :default => :test do
  puts "Tâche par défaut terminée."
end

task :test do
  puts "Exécution des tests..."
end

Exécution :

rake
# Output:
# Exécution des tests...
# Tâche par défaut terminée.

Anatomie d'un Rakefile

Un Rakefile typique peut inclure :

  • require statements : Pour charger des bibliothèques Ruby externes ou des fichiers de votre propre projet qui contiennent la logique métier que vos tâches Rake vont utiliser.
  • Définitions de tâches : En utilisant task, desc, namespace.
  • Variables et constantes : Pour configurer le comportement des tâches.
# Rakefile

# Charger des bibliothèques ou des fichiers de votre projet
require 'fileutils' # Utile pour les opérations sur les fichiers
require_relative 'lib/my_app_logic' # Si vous avez de la logique dans lib/my_app_logic.rb

# Configuration
APP_NAME = "MyAwesomeApp"
BUILD_DIR = "build"

desc "Nettoie le répertoire de build"
task :clean do
  puts "Nettoyage du répertoire #{BUILD_DIR}..."
  FileUtils.rm_rf(BUILD_DIR) if File.directory?(BUILD_DIR)
  puts "Nettoyage terminé."
end

desc "Crée le répertoire de build"
task :create_build_dir do
  puts "Création du répertoire #{BUILD_DIR}..."
  FileUtils.mkdir_p(BUILD_DIR) unless File.directory?(BUILD_DIR)
  puts "Répertoire créé."
end

desc "Construit l'application"
task :build => [:clean, :create_build_dir] do
  puts "Construction de #{APP_NAME}..."
  # Simuler la copie de fichiers ou la compilation
  File.write("#{BUILD_DIR}/#{APP_NAME}.txt", "Contenu de l'application")
  puts "Construction terminée."
end

desc "Exécute l'application construite"
task :run_app => :build do
  puts "Exécution de l'application #{APP_NAME}..."
  content = File.read("#{BUILD_DIR}/#{APP_NAME}.txt")
  puts "Contenu de l'application : '#{content}'"
  MyAppLogic.do_something_important # Appel à une méthode de votre logique métier
end

namespace :docs do
  desc "Génère la documentation"
  task :generate do
    puts "Génération de la documentation..."
    # Commande pour générer la doc (ex: yard doc)
  end
end

desc "Tâche par défaut : construit et exécute"
task :default => :run_app

Commandes Rake Utiles

  • rake: Exécute la tâche par défaut.
  • rake <task_name>: Exécute une tâche spécifique.
  • rake -T ou rake --tasks: Liste toutes les tâches disponibles avec leurs descriptions.
  • rake -D <task_name> ou rake --describe <task_name>: Affiche la description détaillée d'une tâche spécifique.
  • rake -P ou rake --prereqs: Affiche les dépendances de chaque tâche.
  • rake -f <Rakefile>: Spécifie un Rakefile différent à utiliser.

Cas d'Usages Courants de Rake

Rake est omniprésent dans les projets Ruby, en particulier avec Ruby on Rails.

  • Développement Web (Rails) :
    • rake db:migrate: Appliquer les migrations de base de données.
    • rake db:seed: Remplir la base de données avec des données initiales.
    • rake test: Exécuter les tests unitaires et d'intégration.
    • rake assets:precompile: Précompiler les assets pour la production.
    • rake routes: Afficher toutes les routes de l'application.
    • rake log:clear: Nettoyer les fichiers de log.
  • Automatisation de Build : Compiler du code, copier des fichiers, créer des archives (ZIP/TAR).
  • Déploiement : Préparer l'environnement, déployer le code, redémarrer les services.
  • Maintenance : Nettoyer les fichiers temporaires, sauvegarder des données, générer des rapports.
  • Génération de Code : Créer des squelettes de fichiers, des modules, etc.
  • Tests : Exécuter différentes suites de tests, générer des rapports de couverture.

Bonnes Pratiques avec Rake

  1. Tâches Atomiques et Focalisées : Chaque tâche devrait faire une seule chose bien définie. Si une tâche devient trop complexe, divisez-la en sous-tâches et utilisez les dépendances.
  2. Utilisez des Descriptions (desc) : C'est crucial pour la maintenabilité et la découverte des tâches. Un Rakefile sans descriptions est un Rakefile difficile à utiliser.
  3. Organisez avec des Espaces de Noms (namespace) : Pour les projets de taille moyenne à grande, les namespaces améliorent grandement l'organisation et la lisibilité.
  4. Déléguez la Logique Complexe : Évitez de mettre des blocs de code Ruby très longs et complexes directement dans votre Rakefile. Si une tâche nécessite une logique métier significative, encapsulez cette logique dans des classes ou modules Ruby séparés (par exemple, dans le dossier lib/ de votre projet) et appelez ces classes depuis votre tâche Rake. Cela rend votre code plus testable et réutilisable.
  5. Gérez les Erreurs : Utilisez des blocs begin...rescue...end ou des vérifications conditionnelles pour gérer les erreurs potentielles dans vos tâches.
  6. Idempotence : Dans la mesure du possible, rendez vos tâches idempotentes. Cela signifie que l'exécution répétée d'une tâche devrait produire le même résultat que sa première exécution, sans effets secondaires indésirables. Par exemple, une tâche de création de dossier ne devrait pas échouer si le dossier existe déjà.
  7. Testez vos Tâches Rake : Pour les tâches Rake complexes, il peut être judicieux d'écrire des tests unitaires ou d'intégration pour s'assurer qu'elles fonctionnent comme prévu. Cela est facilité si vous déléguez la logique complexe à des classes Ruby séparées.
  8. Versionnez votre Rakefile : Le Rakefile fait partie intégrante de votre projet et doit être sous contrôle de version (Git, SVN, etc.).
  9. Utilisez des Variables d'Environnement : Pour des configurations qui varient entre les environnements (développement, staging, production), utilisez des variables d'environnement plutôt que de coder en dur les valeurs dans le Rakefile.

En résumé, Rake est un outil incroyablement puissant et flexible pour automatiser presque toutes les tâches imaginables dans un projet Ruby. En maîtrisant ses concepts clés et en suivant les bonnes pratiques, vous pouvez améliorer considérablement l'efficacité, la cohérence et la maintenabilité de vos workflows de développement.

Tests avec RSpec

Qu'est-ce que RSpec ?

RSpec est un framework de test pour le langage de programmation Ruby. Il est conçu pour faciliter le développement piloté par le comportement (Behavior-Driven Development - BDD). Contrairement aux frameworks de test traditionnels qui se concentrent sur la vérification des fonctions, RSpec met l'accent sur la spécification du comportement attendu d'une application.

Les tests RSpec sont écrits sous forme de "spécifications exécutables", qui décrivent ce qu'une partie du code est censée faire, dans un langage proche du langage naturel.

Pourquoi utiliser RSpec ?

  • Clarté et Expressivité : La syntaxe de RSpec est très lisible et ressemble à des phrases en anglais, ce qui rend les spécifications faciles à comprendre pour les développeurs, les chefs de projet et même les clients.
  • BDD (Behavior-Driven Development) : RSpec encourage une approche BDD, où les tests sont écrits avant le code de production, servant de documentation vivante et de guide pour le développement.
  • Collaboration : Les spécifications claires facilitent la communication et la collaboration au sein des équipes.
  • Confiance et Sécurité : Des tests robustes donnent confiance lors des refactorisations et des ajouts de nouvelles fonctionnalités, en s'assurant que les changements n'introduisent pas de régressions.
  • Détection Précoce des Bugs : En écrivant des tests avant le code, les problèmes de conception ou les bugs sont souvent identifiés plus tôt dans le cycle de développement.

Principes du BDD

Le BDD est une extension du Test-Driven Development (TDD) qui se concentre sur le comportement du système plutôt que sur les détails d'implémentation. Il utilise un langage plus accessible pour décrire les exigences et les tests.

Les spécifications BDD suivent souvent le format "Given-When-Then" :

  • Given (Étant donné) : Un état initial ou un contexte.
  • When (Quand) : Une action ou un événement se produit.
  • Then (Alors) : Un résultat ou un comportement attendu est observé.

RSpec traduit ces principes en code avec describe (contexte), context (sous-contexte), et it (l'action et le résultat attendu).

Installation et Configuration

Pour une application Ruby pure

  1. Exécutez bundle install.

Initialisez RSpec dans votre projet :

bundle exec rspec --init

Cela créera un dossier spec/ et un fichier spec/spec_helper.rb.

Ajoutez RSpec à votre Gemfile :

# Gemfile
group :development, :test do
  gem 'rspec'
end

Pour une application Rails

  1. Exécutez bundle install.

Générez la configuration RSpec pour Rails :

rails generate rspec:install

Cela créera un dossier spec/ avec spec/spec_helper.rb et spec/rails_helper.rb.

Ajoutez rspec-rails à votre Gemfile :

# Gemfile
group :development, :test do
  gem 'rspec-rails'
end

Structure des fichiers de test

Par convention, les fichiers de spécification RSpec sont placés dans le répertoire spec/ et se terminent par _spec.rb.

Exemples :

  • spec/models/user_spec.rb (pour le modèle User)
  • spec/lib/calculator_spec.rb (pour une classe Calculator dans lib/)
  • spec/requests/users_spec.rb (pour les requêtes HTTP liées aux utilisateurs)

Concepts Fondamentaux de RSpec

describe et context : Groupement des exemples

context : Utilisé pour regrouper des exemples sous une condition ou un état spécifique. Il est souvent imbriqué dans un describe pour affiner le contexte du test.

describe Calculator do
  describe '#add' do # Teste la méthode 'add'
    context 'when given positive numbers' do
      # ... exemples pour l'addition de nombres positifs
    end

    context 'when given negative numbers' do
      # ... exemples pour l'addition de nombres négatifs
    end
  end
end

describe : Utilisé pour regrouper des exemples (tests) liés à une entité spécifique (une classe, un module, une méthode). C'est le bloc de plus haut niveau dans un fichier de spécification.

# spec/calculator_spec.rb
describe Calculator do
  # ... exemples pour la classe Calculator
end

it : L'exemple (la spécification)

Le bloc it contient un seul exemple ou une seule spécification. La chaîne de caractères passée à it doit décrire le comportement attendu de manière claire et concise.

describe Calculator do
  describe '#add' do
    context 'when given positive numbers' do
      it 'returns the sum of the numbers' do
        calculator = Calculator.new
        expect(calculator.add(2, 3)).to eq(5)
      end
    end
  end
end

expect et les Matchers : Les assertions

expect est la base des assertions dans RSpec. Il prend une valeur ou un bloc de code en argument, et est suivi d'un "matcher" qui définit la condition attendue.

expect(valeur_actuelle).to matcher(valeur_attendue)
expect(valeur_actuelle).not_to matcher(valeur_attendue) # Pour l'inverse

Matchers courants :

  • Égalité :
    • eq(expected) : Vérifie l'égalité de valeur (==).
    • be(expected) : Vérifie l'égalité d'objet (même instance).
  • Vérification de type/classe :
    • be_an_instance_of(Class)
    • be_a_kind_of(Class)
  • Valeurs booléennes/nil :
    • be_nil
    • be_truthy (tout ce qui n'est pas false ou nil)
    • be_falsey (false ou nil)
  • Collections :
    • be_empty
    • include(item1, item2, ...)
    • have_key(:key) / have_value('value') (pour les hashes)
  • Comparaison numérique :
    • be > (value)
    • be >= (value)
    • be < (value)
    • be <= (value)
    • be_between(min, max).inclusive / .exclusive
  • Exceptions :
    • raise_error(ErrorClass)
    • raise_error(ErrorClass, 'message')
  • Changement d'état :
    • change(object, :method).from(old_value).to(new_value)
    • change(object, :method).by(delta)
    • change { block_of_code }.from(old_value).to(new_value)
  • Messages (pour les Test Doubles) :
    • receive(:method_name) (utilisé avec allow ou expect)
    • have_received(:method_name) (pour vérifier qu'un message a été reçu)

Exemples :

expect(5).to eq(5)
expect([1, 2, 3]).to include(2)
expect(nil).to be_nil
expect(true).to be_truthy
expect { raise 'Error!' }.to raise_error(RuntimeError)

user = User.new(name: 'Alice')
expect { user.save }.to change(User, :count).by(1)

Exécution des tests

Pour exécuter tous les tests :

bundle exec rspec

Pour exécuter un fichier spécifique :

bundle exec rspec spec/models/user_spec.rb

Pour exécuter un test spécifique (en utilisant le numéro de ligne) :

bundle exec rspec spec/models/user_spec.rb:10

Pour exécuter les tests qui ont échoué lors de la dernière exécution :

bundle exec rspec --only-failures

Concepts Avancés et Bonnes Pratiques

Hooks (before, after, around)

Les hooks permettent d'exécuter du code avant ou après les exemples.

  • before(:each) (ou before) : Exécuté avant chaque exemple (it) dans le bloc describe/context courant et ses enfants. C'est le plus couramment utilisé pour la configuration de l'état initial.
  • before(:all) : Exécuté une seule fois avant tous les exemples dans le bloc describe/context courant. À utiliser avec prudence car il peut introduire des dépendances entre les tests si l'état n'est pas correctement nettoyé.
  • after(:each) (ou after) : Exécuté après chaque exemple. Utile pour le nettoyage.
  • after(:all) : Exécuté une seule fois après tous les exemples.
  • around(:each) : Prend un bloc qui encapsule l'exécution de l'exemple. Utile pour des configurations plus complexes comme la gestion de transactions de base de données.
describe User do
  let(:user) { User.create(name: 'Test User') } # Sera créé avant chaque test

  before(:each) do
    # Ce code s'exécute avant chaque 'it'
    # Par exemple, réinitialiser une variable
    @admin_user = User.create(admin: true)
  end

  after(:each) do
    # Ce code s'exécute après chaque 'it'
    # Par exemple, nettoyer des fichiers temporaires
  end

  context 'when user is admin' do
    before(:all) do
      # Ce code s'exécute une seule fois avant tous les 'it' de ce context
      # À utiliser avec précaution !
      @global_resource = create_expensive_resource
    end

    it 'can manage other users' do
      expect(@admin_user.can_manage?(user)).to be_truthy
    end
  end
end

let et let! : Variables paresseuses et immédiates

Ces méthodes sont essentielles pour rendre vos tests DRY (Don't Repeat Yourself) et lisibles.

let!(:name) { value } : Similaire à let, mais la valeur est évaluée immédiatement avant chaque exemple (it), comme si elle était dans un before(:each). Utile lorsque la création de l'objet a des effets secondaires que vous voulez garantir avant chaque test.

describe User do
  let!(:user) { User.create(email: 'test@example.com') } # 'user' est créé avant chaque 'it'

  it 'increments user count' do
    expect(User.count).to eq(1) # Le user est déjà là
  end
end

let(:name) { value } : Définit une méthode qui retourne value. La valeur est mémoisée (calculée une seule fois) et évaluée paresseusement (seulement la première fois qu'elle est appelée dans un exemple).

describe Article do
  let(:article) { Article.create(title: 'My Article') }

  it 'has a title' do
    expect(article.title).to eq('My Article') # 'article' est créé ici
  end

  it 'is not published by default' do
    expect(article).not_to be_published # 'article' est créé ici
  end
end

subject : Le sujet implicite ou explicite

subject permet de définir l'objet principal que vous testez dans un bloc describe.

Sujet explicite : Vous pouvez définir votre propre subject en utilisant subject { ... }.

describe User do
  subject { User.new(name: 'Alice') } # subject est cette instance spécifique

  it 'has a name' do
    expect(subject.name).to eq('Alice')
  end
end

Vous pouvez également lui donner un nom : subject(:user) { User.new(...) }, ce qui vous permet d'utiliser user au lieu de subject.

Sujet implicite : Si votre describe est sur une classe, RSpec crée automatiquement un subject qui est une nouvelle instance de cette classe.

describe Calculator do # subject est une instance de Calculator
  it 'adds two numbers' do
    expect(subject.add(2, 3)).to eq(5) # subject est Calculator.new
  end
end

Test Doubles (Mocks et Stubs)

Les "test doubles" sont des objets qui imitent le comportement d'objets réels dans un test. Ils sont utilisés pour isoler l'unité de code testée des dépendances externes (base de données, API externes, etc.), rendant les tests plus rapides et plus fiables.

double : Crée un objet générique qui peut être utilisé comme un test double. Vous pouvez lui donner un nom pour une meilleure lisibilité des messages d'erreur.

user_double = double('User', name: 'Alice', email: 'alice@example.com')
expect(user_double.name).to eq('Alice')

Mocks (expect(...).to receive(...)) : Un mock est un stub qui inclut également une attente sur la façon dont il est censé être utilisé. Il vérifie si une méthode spécifique a été appelée avec des arguments spécifiques.

describe OrderProcessor do
  it 'sends a confirmation email after processing' do
    mailer = double('Mailer')
    expect(mailer).to receive(:send_confirmation).with('user@example.com', 'Order 123') # Attente

    processor = OrderProcessor.new(mailer: mailer)
    processor.process_and_confirm('user@example.com', 'Order 123')
  end
end

Stubs (allow(...).to receive(...)) : Un stub est un objet qui fournit des réponses prédéfinies à des appels de méthodes spécifiques. Il ne vérifie pas si la méthode a été appelée.

describe OrderProcessor do
  it 'processes the order using the payment gateway' do
    gateway = double('PaymentGateway') # Crée un double générique
    allow(gateway).to receive(:charge).and_return(true) # Stubbe la méthode charge

    processor = OrderProcessor.new(gateway: gateway)
    expect(processor.process(100)).to be_truthy
  end
end

Focus et Skip

Utiles pendant le développement pour se concentrer sur certains tests ou en ignorer d'autres temporairement.

xdescribe, xcontext, xit : Marque un bloc ou un exemple pour qu'il soit ignoré.

xdescribe 'My unfinished feature' do # Ce bloc sera ignoré
  # ...
end

it 'should do something else' do
  skip 'This test is not ready yet' # Ce test sera ignoré avec un message
  # ...
end

fdescribe, fcontext, fit : Marque un bloc ou un exemple pour qu'il soit le seul à être exécuté.

fdescribe 'My feature' do # Seuls les tests dans ce bloc seront exécutés
  fit 'should do something specific' do # Seul ce test sera exécuté
    # ...
  end
end

Attention : N'oubliez pas de retirer les f et x avant de commiter votre code !

Configuration de RSpec

Le fichier spec/spec_helper.rb (et spec/rails_helper.rb pour Rails) est l'endroit où vous configurez RSpec.

Exemples de configuration courante :

# spec/spec_helper.rb
RSpec.configure do |config|
  # Afficher la documentation des tests (chaînes de caractères des describe/it)
  config.formatter = :documentation

  # Ne pas autoriser les mocks/stubs non définis
  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  # Nettoyer la base de données entre les tests (souvent avec DatabaseCleaner)
  # config.before(:suite) do
  #   DatabaseCleaner.clean_all
  # end
  # config.before(:each) do
  #   DatabaseCleaner.strategy = :transaction
  # end
  # config.before(:each, js: true) do
  #   DatabaseCleaner.strategy = :truncation
  # end
  # config.before(:each) do
  #   DatabaseCleaner.start
  # end
  # config.after(:each) do
  #   DatabaseCleaner.clean
  # end

  # Inclure des modules d'aide
  # config.include FactoryBot::Syntax::Methods # Si vous utilisez FactoryBot
end

Cas d'Usage et Types de Tests

RSpec est polyvalent et peut être utilisé pour différents niveaux de tests :

Tests Unitaires (Modèles, Classes de service)

  • Objectif : Tester la plus petite unité de code isolément (une méthode, une classe).
  • Exemple : Tester la logique d'une méthode dans un modèle Rails, ou une classe de service qui effectue un calcul.
  • Bonne pratique : Utiliser des test doubles pour isoler les dépendances (base de données, API externes).
# spec/models/product_spec.rb
describe Product do
  describe '#price_with_tax' do
    it 'calculates the price including tax' do
      product = Product.new(price: 100, tax_rate: 0.20)
      expect(product.price_with_tax).to eq(120)
    end
  end
end

Tests d'Intégration (Interactions entre composants)

  • Objectif : Tester comment différentes unités de code interagissent entre elles.
  • Exemple : Tester qu'un contrôleur appelle correctement une méthode de service, ou qu'un modèle interagit correctement avec un autre modèle.
  • Bonne pratique : Moins de mocks que les tests unitaires, laisser les composants réels interagir.
# spec/services/order_creator_spec.rb
describe OrderCreator do
  let(:user) { create(:user) } # Utilise FactoryBot pour créer un vrai utilisateur
  let(:product) { create(:product, price: 50) }

  it 'creates an order and associates it with the user and products' do
    order = OrderCreator.new(user, [product]).call
    expect(order).to be_persisted
    expect(order.user).to eq(user)
    expect(order.products).to include(product)
    expect(order.total_amount).to eq(50)
  end
end

Tests Fonctionnels / Requêtes (API, Contrôleurs)

  • Objectif : Tester les points d'entrée de votre application (API REST, actions de contrôleur) en simulant des requêtes HTTP.
  • Exemple : Vérifier qu'une requête POST /users crée un utilisateur et retourne le bon statut HTTP.
  • Bonne pratique : Utiliser les helpers de Rails (comme get, post, json_response) et vérifier les statuts HTTP, les en-têtes et le corps de la réponse.
# spec/requests/users_spec.rb (pour une API REST)
describe 'Users API' do
  describe 'POST /users' do
    context 'with valid parameters' do
      it 'creates a new user' do
        expect {
          post '/users', params: { user: { email: 'test@example.com', password: 'password' } }
        }.to change(User, :count).by(1)
        expect(response).to have_http_status(:created)
        expect(json_response['email']).to eq('test@example.com')
      end
    end

    context 'with invalid parameters' do
      it 'does not create a user' do
        expect {
          post '/users', params: { user: { email: '', password: '123' } }
        }.not_to change(User, :count)
        expect(response).to have_http_status(:unprocessable_entity)
        expect(json_response['errors']['email']).to include("can't be blank")
      end
    end
  end
end

Tests de Feature / Système (avec Capybara)

  • Objectif : Tester l'application du point de vue de l'utilisateur final, en simulant les interactions du navigateur.
  • Exemple : Tester le processus d'inscription, de connexion, ou le parcours d'achat complet.
  • Bonne pratique : Utiliser Capybara pour simuler les clics, les saisies de texte, et vérifier le contenu de la page. Ces tests sont plus lents et doivent être moins nombreux que les tests unitaires.
# spec/features/user_signup_spec.rb
require 'rails_helper'

RSpec.feature 'User Signup', type: :feature do
  scenario 'a user can sign up successfully' do
    visit new_user_registration_path
    fill_in 'Email', with: 'newuser@example.com'
    fill_in 'Password', with: 'password123'
    fill_in 'Password confirmation', with: 'password123'
    click_button 'Sign up'

    expect(page).to have_content('Welcome! You have signed up successfully.')
    expect(User.last.email).to eq('newuser@example.com')
  end

  scenario 'a user cannot sign up with invalid email' do
    visit new_user_registration_path
    fill_in 'Email', with: 'invalid-email'
    fill_in 'Password', with: 'password123'
    fill_in 'Password confirmation', with: 'password123'
    click_button 'Sign up'

    expect(page).to have_content('Email is invalid')
    expect(User.count).to eq(0)
  end
end

Tests de Régression

  • Objectif : S'assurer que les bugs corrigés ne réapparaissent pas et que les nouvelles fonctionnalités n'introduisent pas de problèmes dans le code existant.
  • Bonne pratique : Chaque fois qu'un bug est découvert et corrigé, écrire un test qui reproduit ce bug. Ce test échouera avant la correction et passera après, garantissant que le bug ne réapparaîtra pas sans que le test ne le signale.

Bonnes Pratiques Générales

Lisibilité et Expressivité

  • Messages describe/context/it clairs : Ils doivent former des phrases lisibles qui décrivent le comportement attendu.
    • describe User
    • context 'when logged in'
    • it 'should display the dashboard'
  • Utiliser let et subject : Pour réduire la duplication et rendre les tests plus concis.

Isolation des Tests

  • Chaque test doit être indépendant : Un test ne doit pas dépendre de l'ordre d'exécution des autres tests ou de l'état laissé par un test précédent.
  • Nettoyage de l'état : Utiliser before(:each) pour configurer l'état et after(:each) (ou des outils comme DatabaseCleaner) pour le nettoyer.

Vitesse des Tests

  • Tests unitaires rapides : Ils devraient s'exécuter en millisecondes. Évitez les accès à la base de données ou aux systèmes de fichiers dans les tests unitaires. Utilisez des test doubles.
  • Tests d'intégration et de feature plus lents : C'est normal, mais minimisez leur nombre et leur complexité.

Éviter l'Over-Mocking

  • Ne moquez pas tout : Moquez uniquement les dépendances externes (API, base de données, services tiers) ou les objets complexes qui ralentiraient le test.
  • Testez le comportement, pas l'implémentation : Ne moquez pas des méthodes internes à la classe que vous testez. Si vous le faites, cela peut indiquer que votre classe a trop de responsabilités ou que votre test est trop lié aux détails d'implémentation.

Un seul expect par it (règle générale)

Bien que ce ne soit pas une règle stricte, avoir une seule assertion par it rend le test plus clair sur ce qu'il vérifie et facilite l'identification de la cause d'un échec. Si vous avez plusieurs expect, cela peut indiquer que votre it teste trop de choses.

Nommage des spécifications

  • Les noms des fichiers : nom_de_la_classe_spec.rb
  • Les blocs describe : describe NomDeLaClasse ou describe 'Nom de la fonctionnalité'
  • Les blocs context : context 'quand la condition X est vraie'
  • Les blocs it : it 'devrait faire Y' ou it 'ne devrait pas faire Z'

Refactorisation des tests

Les tests sont du code. Ils doivent être maintenus, refactorisés et être aussi propres que le code de production. La duplication dans les tests est un signe qu'une refactorisation est nécessaire (souvent avec let, before, ou des helpers).

Conclusion

RSpec est un outil formidable pour écrire des tests clairs, expressifs et maintenables en Ruby. En adoptant les principes du BDD et en suivant les bonnes pratiques, vous pouvez construire une suite de tests robuste qui vous donnera confiance dans votre code, facilitera la collaboration et accélérera le développement.

N'oubliez pas que l'apprentissage de RSpec est un processus continu. Pratiquez, lisez le code des autres, et explorez la documentation officielle de RSpec pour découvrir de nouvelles fonctionnalités et techniques.

Annexes & références

Convention syntaxiques

  • 2 espaces pour indenter, pas de tabs, (mineur)
  • préférer [] à Array::new ou Array.new (mineur)
  • préférer {} à Hash.new (mineur)éviter les variables globales
  • éviter les evals
  • éviter le camelCase, utiliser les_formes_lisibles => snake_case
  • écrire du code humainement lisible, ruby est fait pour ça
  • éviter les notations hongroise

voir http://github.com/chneukirchen/styleguide/raw/master/RUBY-STYLE

Règles syntaxiques

  • les commentaires commencent part # (sharp) jusqu'à EOL
  • un programme ruby est une séquence d'expressions fini par un point virgule (;) ou un retour chariot sauf si elle est incomplète\ en fin de ligne si incomplête.

mots reservés


alias and BEGIN begin break case class def defined?do else elsif END end ensure false for ifin module next nil not or redo rescue retryreturn self super then true undef unless until whenwhile yield

Types

les types de base sont :

  • numbers
  • strings
  • ranges
  • regexp
  • symbols
  • arrays
  • hashes

Numbers


  • 123
  • 1_234
  • 123.45
  • 1.2e-3
  • 0xffff # hex
  • 0b01011 # binaire
  • 0377 # octal

Strings & quoting

  • 'pas d\'interpolatio n\' echape que le \\'
  • "#{interpolation}, et backslashes\n"
  • %q(pas d\'interpolation)
  • %Q(interpolation et backslashes)
  • %(interpolation et backslashes)
  • `interpretation avec interpolation et backslashes`. => execute la commande
  • %x(interpretation avec interpolation et backslashes) => execute la commande


Backslashes


\t (tab), \n (newline), \r (carriage return), \f (form feed), \b(backspace), \a (bell), \e (escape), \s (whitespace), \nnn (octal),\xnn (hexadecimal), \cx (control x), \C-x (control x), \M-x (meta x),\M-\C-x (meta control x)

Documentation sur place

  • <<identifier - interpolation
  • <<"identifier" - pareil
  • <<'identifier' - pas d'interpolation
  • <<-identifier - accepte l'indentation préfixé par "-"

finir par le un retour chariot + identifier

Symbols

  • 1.8: les Symboles ne doivent pas contenir \0 ou être vide.
  • :symbol == :symbol
  • :'#{"sans"} interpolation' == :"#{"sans"} interpolation"
  • :"#{"avec"} interpolation" == :"avec interpolation"
  • %s(#{"sans"} interpolation) == :"#{"sans"} interpolation"
  • Ranges
    1..10
  • 1...10
  • 'a'..'z'
  • 'a'...'z'
  • (1..10) === 5 # true
  • (1..10) === 10 # true
  • (1...10) === 10 # false
  • (1..10) === 15 # false

Mots clés

Romain GEORGES

Open Source evangelist & Ruby enthousiast