Programmation en Ruby
I. Installation et concepts de base
II. Premiers pas
II-A. Manipulation de variables simples
II-B. Tableaux
II-C. Hash / tableaux associatifs
II-D. Manipulation de chaînes, de tableaux et de hash
II-E. Conventions de nommage
II-F. Expressions régulières
III. Création de notre première classe
III-A. Définition de la classe et de ses méthodes, variables d'instance
III-B. Constructeur : méthode initialize
III-C. Variable de classe
III-D. Simplification de la classe grâce à Ruby
III-E. Et l'héritage alors ?
III-F. Redéfinir une méthode
IV. Et les modules, qu'est-ce que c'est ?
V. Les structures de contrôle
IV-A. Opérateurs de comparaison
IV-B. if ... else ... end
IV-C. case ... end
IV-D. unless ... end
IV-E. while ... end
IV-F. until ... end
IV-G. for ... end
IV-H. each { ... } et autres itérateurs
IV-I. Sortie de boucles
VI. Procédures
VII. Gestion d'erreurs
VII-A. begin ... rescue ... end
VII-B. ... retry ...
VII-C. ... raise ...
VII-D. .. ensure ...
VIII. Manipulation de fichiers
VIII-A. Création de fichiers et de dossiers
VIII-B. Afficher le contenu d'un fichier ligne par ligne avec le numéro de ligne
VIII-C. Ecrire dans un fichier
VIII-D. Renommer un fichier
VIII-E. Lister le contenu d'un répertoire et de ses sous répertoires trié par ordre alphabétique
IX. Bases de données - MySQL
IX-A. Installer MySQL pour Ruby
IX-B. Exemple d'utilisation
I. Installation et concepts de base
Voyons comment installer Ruby sous Windows.
Rien de très compliqué, il vous suffit de vous rendre sur la page de Ruby, puis de cliquer sur "Téléchargements", et de télécharger le One-Click Installer pour Windows. Il ne vous reste plus qu'à choisir votre répertoire d'installation, et à valider en attendant la fin de la procédure d'installation.
Ruby est livré par défaut avec deux éditeurs : SciTe et FreeRIDE.
Pour lancer FreeRIDE (intégralement écrit en Ruby), rendez-vous dans le répertoire d'installation de Ruby, puis dans le répertoire "freeride", et exécutez "run.bat".
Je préfère SciTe, mais cela ne dépend que de considérations personnelles. Pour le lancer, rendez-vous dans le répertoire d'installation de Ruby, puis dans le répertoire "scite", et exécutez "SciTE.exe".
En Ruby, tout est objet. Absolument tout. Un entier, une chaîne de caractères, et même les classes, aussi étonnant que cela puisse paraître.
II. Premiers pas
Nous allons apprendre par l'expérience. Pour cela, nous allons utiliser irb (interactive Ruby).
En utilisant irb, nous allons pouvoir tester la syntaxe de base de Ruby et en visualiser directement le résultat, sans avoir à créer un fichier contenant notre code (un .rb) et à le modifier à chaque fois. Il vous suffit de lancer une fenêtre DOS (démarrer -> exécuter -> cmd), puis de taper "irb" dans l'invite de commande, et de valider.
II-A. Manipulation de variables simples
L'invite de commande s'est modifiée. Vous pouvez désormais taper directement vos commandes. Essayez de taper les lignes suivantes :
- a = 2
- b = 3
- puts "a + b vaut " + (a+b).to_s
Voici ce que vous devriez obtenir à l'écran :
irb(main):001:0> a = 2 => 2 irb(main):002:0> b = 3 => 3 irb(main):003:0> puts "a + b vaut " + (a+b).to_s a + b vaut 5 => nil
|
Beaucoup de choses dans ce simple exemple. Nous venons de créer deux variables, a et b, et de leur affecter des valeurs. Ensuite, nous avons décidé d'afficher leur somme à l'aide de la méthode puts. Nous affichons donc une chaîne (la partie entre guillemets), que nous concaténons à une autre chaîne à l'aide de l'opérateur +.
Cette deuxième chaîne est en fait le résultat de la somme de nos deux variables (encore cette fois à l'aide de l'opérateur +, qui n'a donc pas la même fonction suivant le type avec lequel il est utilisé), que nous convertissons en chaîne à l'aide de la méthode to_s (pour rendre possible la concaténation, il nous faut deux types identiques).
Vous remarquerez que nulle part nous n'avons défini le type de a ou de b. Ruby se débrouille comme un grand : c'est le duck typing (ça ressemble à un canard, ça se comporte comme un canard, donc c'est un canard).
Les opérateurs sur les nombres restent très classiques : + pour l'addition, - pour la soustraction, * pour la multiplication, et / pour la division.
A noter tout de même ** pour la puissance : a**n signifie a à la puissance n. Ici, n peut être un réel, donc pour obtenir racine de 2, il est tout à fait possible de faire 2**0.5.
Reste la fonction modulo : %, qui renvoie le reste de la division entière.
|
Ici, nous avons écrit "a+b". Or, en Ruby, tout étant objet, les opérateurs ne sont que des méthodes.
Nous aurions donc pu écrire "a.+(b)" : l'appel à la méthode + de l'objet a avec en paramètre l'objet b.
Peu pratique à écrire, vous en conviendrez, mieux vaut utiliser la première notation. Ruby est bourré de ces sucres syntaxiques qui nous facilitent la vie. |
Dans la même veine que to_s, voici quelques fonctions utiles de manipulation de chaîne :
- to_i : convertit une chaîne en un entier, ou retourne la partie entière d'un réel
- to_f : idem, mais retourne un réel
- length : retourne la longueur d'une chaîne ou la longueur d'un tableau
- upcase : convertit une chaîne en majuscules
- downcase : convertit une chaîne en minuscules
- swapcase : intervertit les majuscules et les minuscules dans une chaîne
- capitalize : met en majuscule le premier caractère de la chaîne
- reverse : inverse la chaîne ou les éléments d'un tableau
Jouons encore un peu, et découvrons les chaînes.
Une chaîne peut être définie soit entre apostrophes, soit entre guillemets. Une chaîne définie entre apostrophes ne prendra pas en compte les caractères spéciaux (n par exemple), et n'évaluera pas non plus les expressions entourées par #{...} (que nous verrons un peu plus tard, irb ne le comprend pas).
Créons donc une chaîne simple :
Et observons la sortie :
irb(main):004:0> c = 'cou' => "cou" irb(main):005:0> puts c * 2 => "coucou"
|
Merveilleux, la multiplication répète notre chaîne !
Voyons maintenant comment demander à l'utilisateur une saisie clavier :
- saisie = gets
- puts saisie.chomp
- saisie2 = gets.chomp
irb(main):006:0> saisie = gets coucou => "coucoun" irb(main):007:0> saisie.chomp => "coucou" irb(main):008:0> saisie2 = gets.chomp tralala => "tralala"
|
Ici, gets permet de demander à l'utilisateur de saisir une valeur. En observant bien, on se rend compte que la frappe sur la touche Entrée est incluse dans la chaîne (le n). Pour l'enlever, rien de plus simple, il suffit d'utiliser la méthode chomp, qui renvoie notre chaîne sans le caractère de retour à la ligne.
|
Lorsque vous souhaitez effectuer le remplacement de variables dans une chaîne en Ruby, il faudra utiliser la syntaxe suivante (notez bien la présence de guillemets pour la chaîne où se produit la substitution) : |
nom = ' Pierre-Baptiste ' puts " Bonjour #{ nom}
|
Et voici la sortie :
Rappel : irb ne le comprend pas.
II-B. Tableaux
Nous allons maintenant créer un tableau :
- mon_tableau = ["a", 12, "b"]
irb(main):001:0> mon_tableau = ["a", 12, "b"] => ["a", 12, "b"]
|
Pour accéder aux différents éléments de notre tableau, il suffit de faire suivre le nom de notre variable de crochets, avec l'indice désiré entre les crochets. Le premier indice du tableau est 0 :
- puts mon_tableau[1]
- mon_tableau[1] = 42
- puts mon_tableau[1]
irb(main):002:0> puts mon_tableau[1] 12 => nil irb(main):003:0> mon_tableau[1] = 42 => 42 irb(main):004:0> puts mon_tableau[1] 42 => nil
|
Il existe de nombreuses autres méthodes qui s'appliquent aux tableaux : push, pop, last, etc.
irb(main):005:0> tablo = ['a', 'z', 'e', 'r', 't'] => ["a", "z", "e", "r", "t"] irb(main):006:0> tablo.push('y', 'u') => ["a", "z", "e", "r", "t", "y", "u"] irb(main):007:0> tablo.pop => "u" irb(main):008:0> tablo => ["a", "z", "e", "r", "t", "y"] irb(main):009:0> tablo.last => "y" irb(main):010:0> tablo => ["a", "z", "e", "r", "t", "y"]
|
Juste une petite méthode fort sympathique pour en finir avec les tableaux : la méthode "sort". Faites juste un "mon_tableau.sort", et vous verrez alors votre tableau trié
II-C. Hash / tableaux associatifs
Ruby gère également les tableaux associatifs. Il faut juste noter qu'en Ruby, ils sont appelés "hash".
- mon_hash = {:nom => "Naigeon", :prenom => "PB"}
irb(main):001:0> mon_hash = {:nom => "Naigeon", :prenom => "PB"} => {:nom => "Naigeon", :prenom => "PB"}
|
Accéder aux éléments n'est guère plus compliqué :
- puts mon_hash[:nom]
- puts mon_hash[:prenom]
- mon_hash[:prenom] = "Pierre-Baptiste"
- puts mon_hash[:prenom]
irb(main):002:0> puts mon_hash[:nom] Naigeon => nil irb(main):003:0> puts mon_hash[:prenom] PB => nil irb(main):004:0> mon_hash[:prenom] = "Pierre-Baptiste" =>"Pierre-Baptiste" irb(main):005:0> puts mon_hash[:prenom] Pierre-Baptiste => nil
|
II-D. Manipulation de chaînes, de tableaux et de hash
Il est possible d'accéder aux différentes parties de la chaîne comme suit :
irb(main):001:0> ma_var = 'azerty' => "azerty" irb(main):002:0> ma_var[0] => 97 irb(main):003:0> ma_var[0..0] => "a" irb(main):004:0> ma_var[0..3] => "azer" irb(main):005:0> ma_var[0,4] => "azer" irb(main):006:0> ma_var[-2,2] => "ty"
|
Dans le premier cas, nous obtenons le code ASCII de la première lettre (l'indice 0).
Ensuite, nous lui demandons d'afficher les lettres comprises entre l'indice 0 et l'indice 0 (les bornes sont inclues). Il n'y en a qu'une, il nous renvoie donc "a".
Puis nous lui demandons donc d'afficher les lettres comprises entre l'indice 0 et l'indice 3 (quatre lettres donc). Il nous retourne "azer".
Nous lui demandons ensuite de nous afficher à partir de l'indice 0 une chaîne de longueur 4. Il nous retourne donc encore "azer".
Pour finir, nous lui passons un indice négatif. Les indices négatifs partent de la fin du tableau (et commencent à 1, eux, puisqu'il est dur de différencier 0 et -0). Nous lui demandons donc d'afficher à partir du deuxième caractère avant la fin une chaîne de longueur 2 (les deux derniers caractères quoi !).
Il est possible de procéder de même avec les tableaux :
irb(main):007:0> mon_tabl = ['q', 'w', 'e', 'r', 't', 'y'] => ["q", "w", "e", "r", "t", "y"] irb(main):008:0> mon_tabl[0] => "q" irb(main):009:0> mon_tabl[0..0] => ["q"] irb(main):010:0> mon_tabl[0..3] => ["q", "w", "e", "r"] irb(main):011:0> mon_tabl[0,4] => ["q", "w", "e", "r"] irb(main):012:0> mon_tabl[-2,2] => ["t", "y"]
|
Cela fonctionne exactement comme précédemment. Faites juste attention au fait que mon_tabl[0] retourne une chaîne (dans le cas présent), alors que mon_tabl[0..0] retourne cette même chaîne, mais dans un tableau !
Encore deux petites fonctions extrêmement utiles avant de conclure cette section : split et join (équivalents de explode et implode en PHP).
irb(main):010:0> ma_date = '18/02/1981' => "18/02/1981" irb(main):011:0> mon_tableau = ma_date.split('/') => ["18", "02", "1981"] irb(main):012:0> ma_date2 = mon_tableau.join('-') => "18-02-1981"
|
Vous pouvez bien entendu spécifier le séparateur de votre choix.
Split vous permet donc de découper une chaîne dans un tableau en fonction d'un séparateur donné, et join vous permet de regrouper les éléments d'un tableau dans une chaîne, toujours avec un séparateur.
A vous maintenant la manipulation de chaînes
II-E. Conventions de nommage
Variables et méthodes
- nom_var
- nom_methode
- nom_methode? (une méthode qui renvoie true ou false : is_a?(Fixnum))
- nom_methode! (une méthode "destructrice", souvent qui modifie la variable : sort!)
- @nom_var_instance
- @@nom_var_classe
- $nom_var_globale (Attention, peut aussi s'écrire en majuscules : $NOM_VARIABLE_GLOBALE)
Classes
Constantes
- NOMCONSTANTE ou NomConstante
II-F. Expressions régulières
Ruby est tout à fait de taille à jouer avec les expressions régulières. En revanche, moi, je suis loin d'être de taille à jouer avec elles, c'est pourquoi vous ne disposerez ici que d'un petit exemple, à vous de faire la suite
chaine = ' la couleur #FF024D est-elle une couleur HTML ? '
test = ( chaine = ~ /#[0-9A-Fa-f]{6}/ )
if ( test) puts " la chaine #{ chaine[ test,7 ] } est un code couleur HTML valide ! "
else puts " Pas de code couleur HTML valide dans la chaine "
end
|
Nous nous contentons simplement de parcourir notre chaîne à la recherche du motif, et nous renvoyons sa position si le motif est trouvé.
Notez simplement l'opérateur "=~". C'est l'opérateur réservé à l'évaluation d'expressions régulières. Il retourne la position du premier caractère qui correspond dans la chaîne, nil si rien n'est trouvé.
III. Création de notre première classe
III-A. Définition de la classe et de ses méthodes, variables d'instance
Nous allons nous atteler à la création de classes. Désormais, nous coderons directement dans un fichier. Créez donc un nouveau fichier nommé par exemple animaux.rb.
Nous allons y placer le code suivant :
class Animal
end
cochon = Animal.new
|
Nous avons donc ici une définition de la classe "Animal", ainsi que la création d'une instance de cette classe, nommée "cochon".
Une classe se définit en utilisant le mot-clé "class", suivi par le nom de la classe. La fin de la définition est marquée par le mot-clé "end". Notre classe ici ne fait donc rien, elle se contente d'exister.
Il ne nous reste plus qu'à en créer une instance en utilisant la méthode "new".
Nous allons définir certaines méthodes (simplistes) pour la classe "Animal". Un animal pourra donc "parler" (à défaut d'un meilleur terme), et aura un âge donné.
Il nous va donc falloir écrire des méthodes permettant de définir des propriétés et de les lire.
Une méthode se définit en commencant par le mot clé def, suivi par le nom de la méthode, puis le code de la méthode, et se termine par le mot clé end.
Modifions donc notre fichier comme suit :
class Animal def set_parle( parole) @ parole = parole end def get_parle return @ parole end def set_age( age) @ age = age end def get_age return @ age end
end
cochon = Animal.new cochon.set_parle( " groin groin " ) cochon.set_age( 12 )
puts " le cochon fait #{ cochon.get_parle} " puts " le cochon a #{ cochon.get_age} ans "
|
Pour exécuter ce programme, ouvrez une fenêtre DOS, places-vous dans le répertoire où se situe votre fichier "animaux.rb", et tapez la ligne suivante :
Et voici ce que vous devriez obtenir en sortie :
le cochon fait groin groin le cochon a 12 ans
|
Beaucoup de modifications cette fois-ci.
Nous avons simplement défini quatre méthodes : définition de l'âge, définition de la "parole", récupération de l'âge et récupération de la "parole".
Les méthodes sont définies à l'aide du mot-clé "def" suivi du nom de la méthode, et se terminent par le mot-clé "end".
Mais pourquoi ces @ devant le nom des variables ? Tous simplement parce que ce sont des variables d'instance. Elles seront donc accessibles tout au long de la vie de notre objet, et de n'importe quel endroit de notre objet. Une fois que nous avons crée l'instance "cochon" de notre classe "Animal", nous définissons sa "parole" ainsi que son âge.
Ensuite, nous nous contentons d'afficher ces deux valeurs au sein d'une chaîne. Les balises #{...} permettent d'évaluer du code Ruby au sein d'une chaîne.
III-B. Constructeur : méthode initialize
Nous allons maintenant définir les comportements par défaut de notre objet lors de sa création. Placez le code suivant dans votre définition de classe :
def initialize @ parole = " . . . " @ age = 0 end
|
Et modifions la fin de notre fichier comme suit :
cochon = Animal.new
puts " le cochon fait #{ cochon.get_parle} " puts " le cochon a #{ cochon.get_age} ans "
cochon.set_parle( " groin groin " ) cochon.set_age( 12 )
puts " le cochon fait #{ cochon.get_parle} " puts " le cochon a #{ cochon.get_age} ans "
|
Il ne nous reste plus qu'à exécuter notre fichier, et à observer la sortie :
le cochon fait ... le cochon a 0 ans le cochon fait groin groin le cochon a 12 ans
|
En fait, la méthode initialize est une méthode spécifique, qui s'exécute automatiquement lors de l'instanciation d'un objet.
Ici donc, lors de la création de notre objet "cochon", nous définissons qu'il ne sait pas parler ("..."), et qu'il est âgé de 0 ans.
Nous affichons ces propriétés, puis nous les modifions et les ré-affichons. Rien de très compliqué ici.
C'est bien mignon, mais si il était possible de définir des valeurs directement lors de la création de l'objet, ça nous arrangerait.
Et bien, c'est possible ! Il suffit de modifier la méthode initialize :
def initialize( parole= " . . . " , age= 0 ) @ parole = parole @ age = age end
|
Nous pouvons donc désormais créer un objet comme suit :
cochon = Animal.new( " groin groin " , 12 )
|
Notre animal fraîchement crée saura donc immédiatement parler, et sera âgé de 12 ans.
Dans le cas où les paramètres auraient été oubliés, ils auraient pris la valeur par défaut définie dans la méthode initialize, à savoir "..." et 0.
III-C. Variable de classe
Nous désirons maintenant connaître à tout instant le nombre total d'animaux créés. Modifiez donc votre fichier comme suit :
class Animal @ @ total_cree = 0 def initialize( parole= " . . . " , age= 0 ) @ @ total_cree + = 1 @ parole = parole @ age = age end def self .total return @ @ total_cree end def set_parle( parole) @ parole = parole end def get_parle return @ parole end def set_age( age) @ age = age end def get_age return @ age end
end
puts " Nombre total d ' animaux crees : #{ Animal.total} "
cochon = Animal.new puts " Nombre total d ' animaux crees : #{ Animal.total} "
chien = Animal.new puts " Nombre total d ' animaux crees : #{ Animal.total} "
chat = Animal.new puts " Nombre total d ' animaux crees : #{ Animal.total} "
|
Exécutons le, et observons la sortie :
Nombre total d'animaux crees : 0 Nombre total d'animaux crees : 1 Nombre total d'animaux crees : 2 Nombre total d'animaux crees : 3
|
Mais qu'est-ce donc que ce double arobase ? Il s'agit tout simplement d'une variable de classe. Sitôt la classe définie, cette variable est accessible, puisque nous l'initialisons directement à 0 (directement dans la classe).
A chaque instanciation d'un objet, nous incrémentons cette variable dans la méthode "initialize".
Nous avons également crée une méthode "total", précédée du mot-clé "self". Ce mot-clé fait référence à la classe elle-même. Cette méthode se contente de renvoyer la valeur de la variable de classe.
Cette méthode étant une méthode de classe, il faut donc l'appeler depuis la classe, et non une instance de cette classe : "Animal.total".
III-D. Simplification de la classe grâce à Ruby
Nous allons maintenant profiter des possibilités de Ruby pour rendre la définition de notre classe plus lisible et plus fonctionnelle.
En effet, ne serait-il pas plus facile pour nous d'écrire :
cochon.parle = " groin groin " puts cochon.parle
|
Plutot que :
cochon.set_parle( " groin groin " ) puts cochon.get_parle
|
Je préfère nettement la première des deux versions
Et ça tombe bien, Ruby nous permet de mettre en place de telles méthodes. Voyons comment, en remplaçant les méthodes set_parle et get_parle par les deux méthodes ci-dessous :
def parle= ( parole) @ parole = parole end def parle return @ parole end
|
Et appelons ces deux méthodes après l'instanciation de notre objet cochon :
cochon.parle = " groin groin " puts cochon.parle
|
Magie ! Le résultat escompté est bien là, nous avons donc simplifié tant la lecture de notre définition de classe que l'utilisation des différentes méthodes.
Il s'agit simplement d'une preuve d'intelligence de Ruby. Si il rencontre la méthode parle, suivie d'une affectation (=), il sait tout seul quelle méthode appliquer.
Il ne nous reste plus qu'à faire la même chose avec nos méthodes "set_age" et "get_age".
def age= ( age) @ age = age end def age return @ age end
|
Nous allons à présent simplifier le code ci-dessus.
Pour commencer, les "return" présents au sein de nos différentes méthodes sont inutiles, puisque Ruby renvoie automatiquement la dernière expression évaluée.
Ainsi, "return @parole" peut devenir "@parole" seul.
Nous remarquons maintenant que les méthodes créées pour avoir accès en lecture et en écriture aux variables @age et @parole sont étrangement similaires et redondantes.
Pas de panique ! Ruby est là pour vous. Notre énorme fichier plein de lignes va se transformer ainsi :
class Animal @ @ total_cree = 0 attr_accessor :parle , :age def initialize( parle= " . . . " , age= 0 ) @ @ total_cree + = 1 @ parle = parle @ age = age end def self .total @ @ total_cree end
end
cochon = Animal.new cochon.age = 12 puts cochon.age puts cochon.parle cochon.parle = " groin groin " puts cochon.parle
|
Exécutons notre code, et observons la sortie :
Simplement en utilisant la méthode "attr_accessor", nous avons raccourci notre code de 12 lignes. Cette méthode permet de créer automatiquement des méthodes d'accès en lecture et en écriture aux variables d'instance passées en paramètre.
Je sais que ce point est un peu dur à saisir, mais Ruby va nous générer automatiquement les méthodes parle, parle=, age et age= qui vont nous permettre de lire ou de modifier les variables d'instance @age et @parle.
Nous avons donc au passage modifié notre méthode initialize pour remplacer @parole par @parle.
Si nous avions voulu accéder uniquement en lecture à nos variables, nous aurions pu utiliser "attr_reader", et "attr_writer" si nous avions voulu y accéder uniquement en écriture.
III-E. Et l'héritage alors ?
Nous allons nous essayer aux joies de l'héritage.
Pour cela, nous allons prendre un exemple parfaitement stupide : un animal est un être vivant, et un être vivant bat du cœur.
Nous allons donc définir une classe "EtreVivant", définir une méthode "coeur_bat", et en faire hériter notre classe "Animal".
class EtreVivant def coeur_bat puts " boum boum , j ' ai le coeur qui bat ! " end
end
|
Il ne nous reste plus qu'à gérer l'héritage proprement dit.
Modifiez donc la définition de la classe "Animal" comme suit :
Il ne vous reste plus qu'à instancier un objet, et à tester la méthode :
cochon = Animal.new cochon.coeur_bat
|
Et merveilleux, vous obtenez bien le texte "boum boum, j'ai le cœur qui bat !". La classe "Animal" a donc bien hérité des méthodes de la classe "EtreVivant".
III-F. Redéfinir une méthode
Nous allons à présent redéfinir une méthode existante. Prenez le code suivant :
class EtreVivant def coeur_bat puts " boum boum , j ' ai le coeur qui bat ! " end
end
class Animal< EtreVivant @ @ total_cree = 0 attr_accessor :parle , :age def initialize( parle= " . . . " , age= 0 ) @ @ total_cree + = 1 @ parle = parle @ age = age end def self .total @ @ total_cree end
end
cochon = Animal.new cochon.coeur_bat
class EtreVivant def coeur_bat puts " bim bim , j ' ai le coeur patraque ! " end
end
cochon.coeur_bat
|
Et vous obtenez en sortie :
boum boum, j'ai le coeur qui bat ! bim bim, j'ai le coeur patraque !
|
La méthode "coeur_bat" ne renvoie pas la même chose entre les deux appels. Elle a donc bien été redéfinie.
|
Vous pouvez vous en servir pour contrôler les évolutions de version de vos applications, redéfinir une méthode existante dans un contexte particulier, ...
Vous pouvez également la re-définir au sein d'une classe fille. La méthode aura donc un comportement global, mais sera spécifique dans une classe précise. Pour cela, il suffit de redéfinir la méthode dans la classe voulue. Dans l'exemple ci-dessus, il aurait fallu définir la méthode coeur_bat pour la classe Animal.
Ainsi, la méthode coeur_bat serait restée la même partout, sauf pour la classe Animal. C'est ce qu'on appelle la surcharge de méthodes. |
De la même façon, vous pouvez parfaitement étendre une classe existante, par exemple définir une méthode "je_parle_pas_clair" pour la classe String qui sera valable pour toutes les chaînes :
class String def je_parle_pas_clair temp = self .reverse end
end
texte = " Coucou les filles , ca va ? " puts texte.je_parle_pas_clair
|
Merveilleux non ?
Il y a également moyen de redéfinir une méthode pour un objet spécifique. C'est ce qu'on appellera une méthode Singleton.
Prenons cet exemple (parfaitement débile, pardon petit frère mais j'étais en manque d'idées) :
class BeauGosse def la_classe puts " j ' ai la classe " end
end
titou = BeauGosse.new titou.la_classe
petit_frere = BeauGosse.new
def petit_frere.la_classe puts " compare a mes grands freres , je vaut pas un clou ! "
end
petit_frere.la_classe
grand_frere = BeauGosse.new grand_frere.la_classe
|
Et observons la sortie :
j'ai la classe compare a mon grand frere, je vaut pas un clou ! j'ai la classe
|
Nous avons ici redéfini la méthode "la_classe" uniquement pour l'objet "petit_frere".
IV. Et les modules, qu'est-ce que c'est ?
Les modules sont de petites choses qui ressemblent énormément à des classes, mais qui ne peuvent pas être instanciés.
En revanche, ils peuvent tout à fait être intégrés dans une classe, pour faire bénéficier cette classe de l'ensemble des méthodes définies dans le module.
Voyons de suite un petit exemple (encore une fois ridicule et inutile, juste pour se rendre compte de la construction et des possibilités) :
module ModTest def mon_id puts self .object_id end
end
class ClassTest include ModTest
end
class ClassTest2 include ModTest
end
toto = ClassTest.new toto.mon_id
tata = ClassTest2.new tata.mon_id
|
Et là, miracle, nos deux classes héritent des méthodes de notre module.
Nous aurions pu envisager pour en arriver au même résultat d'ajouter la méthode à une classe "supérieure" dans la hiérarchie des classes, pourquoi pas la classe "Object" elle-même, mais toutes nos classes n'ont pas besoin d'avoir ces méthodes de définies.
Dans le cas présent, nos deux classes peuvent appartenir à deux branches très différentes, et bénéficier de la même méthode.
V. Les structures de contrôle
Pour les différentes structures de contrôle ci-dessous, je vous montrerai simplement un exemple d'utilisation, je ne pense pas qu'une explication soit nécessaire
D'une manière générale, les structures de contrôle s'appliquent à tout ce qui est compris entre ne nom de l'instruction (if, while, ...) et le mot-clé
end (parfois les instructions seront comprises entre accolades
{...
}).
IV-A. Opérateurs de comparaison
En Ruby, les opérateurs classiques de comparaison s'appliquent :
- a == b : a égal b (attention, contrairement à PHP par exemple, '42' == 42 renverra false)
- a != b : a différent de b
- a < b : a strictement inférieur à b
- a > b : a strictement supérieur à b
- a <= b : a inférieur ou égal à b
- a >= b : a supérieur ou égal à b
IV-B. if ... else ... end
puts " Merci de saisir un nombre : " chiffre = gets.chomp.to_i
if ( chiffre = = 42 ) puts " Bingo , LA réponse ! "
else puts " Essaie encore "
end
|
Notez simplement le to_i, qui transforme la valeur récupérée est un entier.
|
Dans le cas d'une suite de caractères alphanumériques, to_i a un comportement spécifique : |
irb(main):001:0> "42".to_i => 42 irb(main):002:0> "42abc".to_i => 42 irb(main):003:0> "abc42abc".to_i => 0 irb(main):004:0> "abc".to_i => 0
|
Tant que les caractères sont des chiffres, Ruby les prend en compte. Dès qu'il rencontre autre chose, il s'arrête. Dans le cas où le premier caractère de la chaîne ne correspond pas, il nous retourne 0.
IV-C. case ... end
Le case permet d'effectuer différentes actions en fonction de la valeur d'un paramètre sans avoir à imbriquer trop de if.
puts " Merci de saisir un nombre : " chiffre = gets.chomp.to_i
case chiffre when 0 .. 9 puts " Vous aimez les tout petits chiffres . " when 42 puts " Vous être un as ! " else puts " Vous auriez mieux pu choisir . . . ; ) "
end
|
La seule chose importante à noter ici est l'intervalle spécifié dans le "when 0..9".
IV-D. unless ... end
Un unless est simplement un if à l'envers : sauf si :
j = rand( 10 )
unless ( j <= 5 ) puts " j vaut plus de 5 "
end
|
IV-E. while ... end
puts " Je vais compter de 1 a 10 " i = 0
while ( i < 10 ) i + = 1 puts i
end
|
A noter la méthode d'incrémentation... inutile d'essayer i++, qui ne ferait que vous générer une erreur.
IV-F. until ... end
Le until est juste un while qui marche dans l'autre sens. En reprenant l'exemple précédent :
puts " Je vais compter de 1 a 10 " i = 0
until ( i > = 10 ) puts i i + = 1
end
|
IV-G. for ... end
La boucle for est un peu particulière, puisqu'elle s'applique à des éléments d'une collection :
puts " compte de 1 a 10 "
for elt1 in ( 1 .. 10 ) puts elt1
end
|
tableau = [ 1 , 2 , 3 , 4 ]
for elt2 in tableau puts elt2 + 1
end
|
IV-H. each { ... } et autres itérateurs
Il est également possible de coder les deux exemples ci-dessus comme suit :
puts " compte de 1 a 10 "
( 1 .. 10 ) .each { | elt1| puts elt1
}
|
tableau = [ 1 , 2 , 3 , 4 ] tableau.each { | elt2| puts elt2 + 1
}
|
Je préfère cette dernière syntaxe, mais ce n'est qu'une question de goût personnel. Choisissez donc celle qui vous convient le mieux.
Notez simplement que le dernier exemple pourrait s'écrire ainsi :
tableau = [ 1 , 2 , 3 , 4 ] tableau.each do | elt2| puts elt2 + 1
end
|
Il est également possible de parser un à un les caractères d'une chaîne à l'aide de each_byte :
ma_chaine = " ayu " ma_chaine.each_byte { | carac| ascii_sup = carac+ 1 puts ascii_sup.chr
}
|
Merveilleux, nous avons augmenté notre code ASCII de 1, puis nous avons affiché les lettres correspondantes.
Notez simplement la méthode "chr" appelée à l'affichage, pour afficher le caractère et non le code ASCII.
Il est également possible de parser ligne par ligne. Je suis sûr que vous avez déjà deviné le nom de l'itérateur... Bingo, each_line !
Affichons donc le numéro de la ligne avant la ligne elle-même :
ma_chaine = " totontitintata " i = 1 ma_chaine.each_line { | ligne| puts " #{ i} : #{ ligne} " i + = 1
}
|
Un petit moyen simple de répéter une opération X fois :
2000 .times do puts " Moi , j ' adore K2000 ! "
end
|
IV-I. Sortie de boucles
Il existe différents moyens de sortir d'une boucle : "return", "break", "next" et "redo". Voyons les spécificités de chacun :
- return : sort complètement de la boucle et de la méthode qui contient la boucle.
- break : sort de la boucle, et continue le code juste après.
- next : passe directement à la prochaine itération de la boucle
- redo : re-passe l'itération courante de la boucle
|
Il est facile à l'aide de ces commandes de réaliser des boucles infinies. Prenez donc le temps de bien regarder comment elles fonctionnent. |
VI. Procédures
Il est possible en Ruby de stocker des blocs de code dans des objets, pour mieux les exécuter plus tard à la demande :
quel_temps = Proc.new { | mon_param| puts " il fait #{ mon_param} aujourd ' hui "
} quel_temps.call " gris " quel_temps.call " beau "
|
Merveilleux, notre code est exécuté. Ici, il ne s'agit que d'un simple affichage, mais il est possible d'avoir le code que l'on veut dans une procédure.
Vous me direz, assez peu d'intérêt dans l'état. Mais là ou ça devient sympathique, c'est que l'on peut passer une procédure en paramètre à une méthode :
class Personne def initialize( age, sexe) @ age = age @ sexe = sexe end def infos affiche_age puts " Je suis de sexe " + @ sexe affiche_age.call @ age end
end
monAgeH = Proc.new do | age| puts " J ' ai " + age.to_s+ " ans "
end monAgeF = Proc.new do | age| puts " On ne demande pas son age a une dame ! "
end
paul = Personne.new( 42 , ' masculin ' ) virginie = Personne.new( 32 , ' feminin ' )
puts paul.infos( monAgeH) puts virginie.infos( monAgeF)
|
Ce qui va nous afficher :
Je suis de sexe masculin J'ai 42 ans nil Je suis de sexe feminin On ne demande pas son age a une dame ! nil
|
Nous avons donc passé un bloc de code en paramètre à une méthode. Ce bloc peut varier du tout au tout, il ne sera pas utile de modifier la méthode, ou de créer une deuxième classe similaire à la première.
Plutôt avantageux non ?
VII. Gestion d'erreurs
Indispensable au développeur, la gestion d'erreurs en Ruby est extrêmement simple à mettre en place.
VII-A. begin ... rescue ... end
Vous voulez ouvrir un fichier qui n'existe pas, créer une table qui existe déjà ? Vous haïssez cordialement les messages d'erreurs et les interruptions impromptues ?
Aucun soucis, dorénavant, vous serez capable de gérer ce genre de cas.
def ouvre_fichier( nom_fichier) begin fichier = File.open( nom_fichier, " r " ) rescue puts " Le fichier n ' a pas pu être ouvert " end
end
ouvre_fichier( " test . txt " )
|
Ne vous intéressez pas pour le moment à la manipulation de fichiers, cela fait l'objet de la section suivante.
Dans l'exemple ci-dessus, dans le cas ou le fichier "test.txt" ne peut être ouvert, Ruby lève une exception, qui arrête l'exécution du programme.
Mais le fait d'avoir mis l'instruction d'ouverture dans un bloc "begin ... rescue ... end" permet de capturer cette erreur, d'afficher un message (ou toute autre opération), puis de continuer l'exécution du programme. Nous pourrions par exemple, au sein du "rescue", lui demander d'ouvrir un fichier par défaut.
VII-B. ... retry ...
Grâce à l'instruction "retry", nous pouvons également relancer notre bloc, en modifiant les paramètres par exemple :
def ouvre_fichier( nom_fichier) begin fichier = File.open( nom_fichier, " r " ) rescue puts " Le fichier n ' a pas pu être ouvert , merci de saisir un nom correct " nom_fichier = gets.chomp retry end
end
ouvre_fichier( " test . txt " )
|
Attention tout de même, dans ce cas précis, nous bouclerons tant que l'utilisateur n'aura pas saisi un nom de fichier que Ruby est capable d'ouvrir.
Pensez donc à gérer ce cas
VII-C. ... raise ...
Utiliser raise vous permet de lever vos propres erreurs, et de les traiter à l'aide d'un rescue.
Voici un exemple parfaitement stupide, mais qui donne une idée de l'utilisation qui peut en être faite :
begin puts " Saisissez LE nombre : " nombre = gets.chomp.to_i if ( nombre ! = 42 ) raise " L ' utilisateur ne connait pas ses classiques " end
rescue puts " Erreur : #{ $! } "
end
|
La seule chose à noter ici est l'utilisation de "$!", qui retourne le dernier message d'erreur. Il s'agit d'une variable globale spécifique (il y en a quelques autres dans le même genre).
VII-D. .. ensure ...
Afin de s'assurer que dans tout les cas, une instruction s'exécutera, il suffit d'utiliser "ensure" :
VIII. Manipulation de fichiers
Les codes suivants vont être moins détaillés, l'idée est plutôt de voir différentes manières d'accéder aux dossiers et aux fichiers.
VIII-A. Création de fichiers et de dossiers
Rien de très compliqué dans cet exemple. Nous allons nous contenter de créer un dossier nommé "test" (avec les droits complets pour tout le monde), et de créer dans ce dossier un fichier vide nommé "test.txt" :
Dir:: mkdir( " test " , 0777 ) File:: new( " test / test . txt " , " w + " )
|
Comme nous n'avons pas l'intention de travailler plus avant ni sur le dossier, ni sur le fichier, inutile de les instancier, nous allons directement faire appel à la classe.
VIII-B. Afficher le contenu d'un fichier ligne par ligne avec le numéro de ligne
fichier = File.open( " test . txt " , " r " ) i = 1 fichier.each_line { | ligne| puts " #{ i} - #{ ligne} " i + = 1
} fichier.close
|
Notez simplement l'utilisation de "each_line", qui se tape tout le boulot à notre place, pour notre plus grand plaisir
La classe "File" héritant de la classe "IO", nous aurions pu faire la même chose comme suit :
tableau = File.readlines( " test . txt " ) i = 1 tableau.each { | ligne| puts " #{ i} - #{ ligne} " i + = 1
}
|
Pas de grande différence, mais uniquement pour bien se rendre compte qu'il existe différents moyens de faire une même chose ("IO.readlines" aurait également convenu).
VIII-C. Ecrire dans un fichier
Il est très facile d'écrire dans un fichier en Ruby :
mon_tabl = [ " Bonjour , " , " Content de vous rencontrer . " , " ça va bien ? " ]
mon_fichier = File.open( " test . txt " , " w " ) mon_tabl.each { | element| mon_fichier.write element+ " n "
} mon_fichier.close
|
Bien entendu, l'effet produit va dépendre des différents modes d'ouverture du fichier utilisés.
La ligne commentée n'est là que pour montrer quelle solution aurait également pu être utilisée. Libre à vous de choisir celle qui vous convient le mieux.
VIII-D. Renommer un fichier
C'est vraiment pour dire, mais rien de bien compliqué. Renommons notre fichier "test.txt" en "toto.txt" :
File:: rename( " test . txt " , " toto . txt " )
|
Vous étiez prévenus, c'est vraiment pour faire joli
Mais vous pouvez également vous servir de cette commande pour déplacer un fichier.
VIII-E. Lister le contenu d'un répertoire et de ses sous répertoires trié par ordre alphabétique
Un bout de code légèrement plus complexe, mais qui peut servir : nous allons ajouter à la classe "String" une méthode "liste_rep" qui lorsqu'elle recevra un path en paramètre listera celui-ci et tout ses sous-répertoires :
class String def liste_rep( espacement = " " )
liste_exclus = [ " . " , " . . " ] d = Dir.open( self )
liste_dir = d.sort - liste_exclus
liste_dir.each { | fichier| case File.ftype( self + fichier) when " directory " puts " #{ espacement} + #{ fichier} / " espacement + = " " ( self + fichier + " / " ) .liste_rep( espacement) espacement = espacement[ 0 , espacement.length- 4 ] when " file " puts " #{ espacement} - #{ fichier} " end } end
end
" . / " .liste_rep
|
Plusieurs points importants ici :
L'utilisation de "self" permet de savoir à tout moment quel est l'objet traité, puisqu'il s'agit de l'objet appelant la méthode. Cela permet de plus d'éviter de passer un paramètre optionnel dans notre méthode.
L'opération de soustraction sur les tableaux est autorisée. Ici, nous nous en servons pour exclure de la liste des fichiers à parcourir les dossiers "." et "..", histoire d'éviter de nous retrouver dans des boucles bizarres
Nous nous contentons ensuite de vérifier le type du fichier. Si c'est un dossier, on l'affiche, on augmente l'indentation, on fait un appel récursif sur la fonction, ... puis on décrémente l'indentation
Si c'est un fichier normal, on se contente de l'afficher avant de passer à l'élément suivant.
IX. Bases de données - MySQL
Dans cet exemple, nous allons nous pencher exclusivement sur MySQL, mais sachez que Ruby est capable d'attaquer tout type de bases de données.
IX-A. Installer MySQL pour Ruby
Commencez par télécharger et installer MySQL en vous rendant sur la page de téléchargement MySQL.
Une fois installé et configuré, il ne vous reste plus qu'à installer le module MySQL pour Ruby. Rien de plus simple, grâce à un système de packages très bien conçu : Gem.
Ouvrez une invite de commande, et tapez "gem install mysql".
Après une mise à jour des packages disponibles, il vous proposera une liste vous permettant de choisir quelle version du module vous souhaitez installer. J'ai opté pour celle-ci : "1. mysql 2.7.1 (mswin32)". Il vous suffit de taper le numéro correspondant, et de valider avec la touche "Entrée".
Il va se charger de tout installer, il ne vous reste plus qu'à vérifier que cela fonctionne, en lançant irb, et en tapant :
require "mysql". Si il vous renvoie "true", bingo c'est gagné, nous pouvons donc nous attaquer au code
IX-B. Exemple d'utilisation
Cette fois encore, le code sera plus parlant qu'un long discours :
require " mysql "
db_host = " localhost " db_user = " root " db_pass = " mon_pass " db_name = " base_test "
connex = Mysql.new( db_host, db_user, db_pass)
strSQL = " CREATE DATABASE #{ db_name} "
begin connex.query( strSQL)
rescue puts " # # # Impossible de creer la base # # # "
end
connex.select_db( db_name)
strSQL = " CREATE TABLE liste_livres
(
id_livre INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
titre VARCHAR ( 255 ) NOT NULL ,
auteur VARCHAR ( 50 ) NOT NULL ,
editeur VARCHAR ( 50 ) NOT NULL
) "
begin connex.query( strSQL)
rescue puts " # # # Impossible de creer la table # # # "
end
strSQL = [ " INSERT INTO liste_livres ( titre , auteur , editeur )
VALUES ( ' Le guide du voyageur galactique ' , ' Douglas Adams ' , ' Gallimard ' ) " , " INSERT INTO liste_livres ( titre , auteur , editeur )
VALUES ( ' Dune ' , ' Frank Herbert ' , ' Robert Laffont ' ) " , " INSERT INTO liste_livres ( titre , auteur , editeur )
VALUES ( ' Fondation ' , ' Isaac Asimov ' , ' Gallimard ' ) " , " INSERT INTO liste_livres ( titre , auteur , editeur )
VALUES ( ' La pierre et le sabre ' , ' Eiji Yoshikawa ' , ' J ' ai Lu ' ) " , " INSERT INTO liste_livres ( titre , auteur , editeur )
VALUES ( ' La parfaite lumière ' , ' Eiji Yoshikawa ' , ' J ' ai Lu ' ) " ]
begin strSQL.each { | ligne| connex.query( ligne) }
rescue puts " # # # Impossible d ' inserer les donnees dans la base # # # "
end
strSQL = " SELECT * FROM liste_livres ORDER BY titre " result = connex.query( strSQL) result.each_hash do | ligne| puts " Titre : #{ ligne[ ' titre ' ] } " puts " Auteur : #{ ligne[ ' auteur ' ] } " puts " Editeur : #{ ligne[ ' editeur ' ] } " puts " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
end
connex.close
|
Voici les choses importantes à retenir ici :
- require "mysql" : charge le module MySQL, et permet d'utiliser les classes et méthodes liées.
- Mysql#new : crée la connexion à la base de données.
- Mysql#select_db : définit la base sur laquelle travailler.
- Mysql#query : envoie une chaîne SQL à la base.
- Mysql#close : ferme la connexion à la base.
Notez simplement l'utilisation du backslash ("") en fin de ligne lors du remplissage de la base, qui me permet d'éviter des sauts de ligne intempestifs (et puis, c'est nettement plus facile à lire que tout à la suite).
Je vous laisse vous amuser un peu avec les bases, mais rien de très compliqué ici.