1 | 2 | 3 | 4

Ruby en vingt minutes

Maintenant, créons un objet de la classe Greeter et animons-le :

irb(main):035:0> g = Greeter.new("Pat")
=> #<Greeter:0x16cac @name="Pat">
irb(main):036:0> g.say_hi
Hi Pat!
=> nil
irb(main):037:0> g.say_bye
Bye Pat, come back soon.
=> nil

Une fois l’objet g créé, il se souvient que le nom qui lui est lié est Pat, comme indiqué à sa création. Il serait d’ailleurs intéressant de pouvoir récupérer directement ce nom. Essayons :

irb(main):038:0> g.@name
SyntaxError: compile error
(irb):52: syntax error
        from (irb):52

Whoa, ça n’a pas l’air possible pour l’instant.

Dans la peau d’un objet

name est, comme nous l’avons vu, une variable d’instance. « Instance » est synonyme d’objet. Les variables d’instances sont donc cachées au cœur des objets que nous pouvons créer à loisir, en utilisant un modèle générique, la classe. Mais elles ne sont pas si bien cachées que ça. Pour commencer, vous pouvez les voir si vous inspectez un objet (avec la méthode inspect), et il existe des moyens d’y accéder. Mais Ruby fait en sorte de garder un peu d’ordre dans sa maison, et collant au plus près de l’approche orientée objet, y compris dans la gestion des données et variables qui les renferment.

Quelles sont les méthodes disponibles pour nos instances de la classe Greeter ?

irb(main):039:0> Greeter.instance_methods
=> ["method", "send", "object_id", "singleton_methods",
    "__send__", "equal?", "taint", "frozen?",
    "instance_variable_get", "kind_of?", "to_a",
    "instance_eval", "type", "protected_methods", "extend",
    "eql?", "display", "instance_variable_set", "hash",
    "is_a?", "to_s", "class", "tainted?", "private_methods",
    "untaint", "say_hi", "id", "inspect", "==", "===",
    "clone", "public_methods", "respond_to?", "freeze",
    "say_bye", "__id__", "=~", "methods", "nil?", "dup",
    "instance_variables", "instance_of?"]

Ouch. Voilà une sacré liste de méthodes. Et pourtant, nous n’en avons défini que deux… d’où sortent donc les autres ? En fait, il s’agit d’une liste exhaustive des méthodes appliquables aux objets de la classe Greeter, y compris celles définies dans les classes parentes de Greeter. Si nous voulons obtenir la listes des méthodes définies uniquement pour Greeter, il suffit de passer le paramètre false:

irb(main):040:0> Greeter.instance_methods(false)
=> ["say_bye", "say_hi"]

Ok, c’est déjà plus confortable. Et conforme. Vérifions que c’est vrai, en testant quelles méthodes reconnaissent effectivement les instances de Greeter:

irb(main):041:0> g.respond_to?("name")
=> false
irb(main):042:0> g.respond_to?("say_hi")
=> true
irb(main):043:0> g.respond_to?("to_s")
=> true

Une instance de Greeter connaît donc say_hi et to_s (une méthode qui transforme « quelque chose » en une chaîne de caractère et qui est disponible pour tout objet). Par contre, la méthode name est inconnue.

Modifier les classes a posteriori

Mais nous n’en démordrons pas : il nous faut un moyen de récupérer le nom lié à un objet. Comment faire ? Ruby propose un moyen très simple pour accéder aux variables d’instances :

irb(main):044:0> class Greeter
irb(main):045:1>   attr_accessor :name
irb(main):046:1> end
=> nil

Il semblerait que nous ayons défini une seconde fois la classe Greeter… mais il n’en est rien. Nous l’avons simplement « ré-ouverte » et modifiée. Et les changements ainsi définis sont immédiatement disponibles dans tout objet nouvellement créé, ainsi que dans ceux déjà existants ! Créons un nouvel objet et testons l’artifice :

irb(main):047:0> g = Greeter.new("Andy")
=> #<Greeter:0x3c9b0 @name="Andy">
irb(main):048:0> g.respond_to?("name")
=> true
irb(main):049:0> g.respond_to?("name=")
=> true
irb(main):050:0> g.say_hi
Hi Andy!
=> nil
irb(main):051:0> g.name="Betty"
=> "Betty"
irb(main):052:0> g
=> #<Greeter:0x3c9b0 @name="Betty">
irb(main):053:0> g.name
=> "Betty"
irb(main):054:0> g.say_hi
Hi Betty!
=> nil

Le fait d’écrire attr_accessor a implicitement défini deux nouvelles méthodes à peu de frais : name pour récupérer la valeur de la variable/objet name, et name= pour la définir et la modifier.

Accueillir tout et tout le monde !

Le problème avec notre programme actuel, c’est qu’il ne peut s’occuper que d’une seule personne à la fois. Il nous faut vraiment un… SuperProgramme, qui pourrait aussi bien s’occuper de tout le monde en une fois, que d’une seule personne ou de plusieurs personnes à la fois…

Comme notre programme commence à prendre de l’ampleur, arrêtons ici avec IRB, et continuons dans un véritable éditeur de texte. Nous allons enregistrer la suite dans un fichier. Pour quitter IRB, écrivez simplement quit, exit ou appuyez sur [Ctrl]+[D].

Notre fichier va se présenter comme suit :

#!/usr/bin/env ruby

class MegaGreeter
  attr_accessor :names

  # Création d'un objet
  def initialize(names = "World")
    @names = names
  end

  # Saluer tout le monde
  def say_hi
    if @names.nil?
      puts "..."
    elsif @names.respond_to?("each")
      # @names est une liste de noms : traitons-les uns par uns
      @names.each do |name|
        puts "Hello #{name}!"
      end
    else
      puts "Hello #{@names}!"
    end
  end

  # Dire au revoir à tout le monde
  def say_bye
    if @names.nil?
      puts "..."
    elsif @names.respond_to?("join")
      # Grouper les différents noms de la liste par des virgules
      puts "Goodbye #{@names.join(", ")}.  Come back soon!"
    else
      puts "Goodbye #{@names}.  Come back soon!"
    end
  end

end


if __FILE__ == $0
  mg = MegaGreeter.new
  mg.say_hi
  mg.say_bye

  # Modifier le nom en Zeke
  mg.names = "Zeke"
  mg.say_hi
  mg.say_bye

  # Changer le nom pour un tableau (une liste de noms)
  mg.names = ["Albert", "Brenda", "Charles",
    "Dave", "Engelbert"]
  mg.say_hi
  mg.say_bye

  # Maintenant, le nom n'est plus...
  mg.names = nil
  mg.say_hi
  mg.say_bye
end

Sauvegardez ce fichier, par exemple en tant que “ri20min.rb” (l’extension .rb, pour ruby, est importante sous Windows, d’usage sous les autres OS), et éxécutez-le en tapant “ruby ri20min.rb” dans votre terminal. Vous devriez y lire ceci :

Hello World!
Goodbye World.  Come back soon!
Hello Zeke!
Goodbye Zeke.  Come back soon!
Hello Albert!
Hello Brenda!
Hello Charles!
Hello Dave!
Hello Engelbert!
Goodbye Albert, Brenda, Charles, Dave, Engelbert.  Come
back soon!
...
...

Il y a un certain nombre de choses nouvelles dans ce fichier. Examinons-les en détails.