Retour au cours

méthodes magiques : to_s, inspect et autres conventions

Objectifs

  • Comprendre le rôle des méthodes conventionnelles que Ruby appelle implicitement pour vous.
  • Personnaliser la représentation textuelle d’un objet pour l’affichage avec to_s.
  • Personnaliser la représentation de débogage d’un objet avec inspect.
  • Découvrir comment les opérateurs (+, ==, []) sont aussi des méthodes que l’on peut redéfinir.

En Ruby, il existe une série de méthodes que vous n’appelez pas toujours directement, mais que l’interpréteur utilise “sous le capot” dans certaines situations. En définissant ces méthodes sur vos propres classes, vous donnez à vos objets des super-pouvoirs et vous les intégrez de manière transparente au reste du langage.

to_s : La représentation pour les humains

La méthode to_s (to string) doit retourner une représentation textuelle et lisible de votre objet, destinée à l’affichage final. C’est cette méthode qui est appelée lorsque vous :

  • Utilisez puts sur votre objet.
  • L’insérez dans une chaîne avec l’interpolation ("mon objet : #{mon_objet}").
class Livre
  attr_reader :titre, :auteur

  def initialize(titre, auteur)
    @titre = titre
    @auteur = auteur
  end

  # On redéfinit to_s
  def to_s
    "'#{@titre}' par #{@auteur}"
  end
end

mon_livre = Livre.new("Le Guide du voyageur galactique", "Douglas Adams")

puts mon_livre # Appelle mon_livre.to_s automatiquement
# => 'Le Guide du voyageur galactique' par Douglas Adams

inspect : La représentation pour les développeurs

La méthode inspect doit retourner une chaîne de caractères qui représente l’objet de manière détaillée et non ambiguë, destinée au débogage. C’est cette méthode qui est appelée par :

  • p mon_objet (qui est un raccourci pour puts mon_objet.inspect).
  • La console irb pour afficher le résultat d’une expression.
class Livre
  # ... (initialize et to_s) ...

  # On redéfinit inspect
  def inspect
    "#<Livre titre=\"#{@titre}\" auteur=\"#{@auteur}">"
  end
end

mon_livre = Livre.new("Le Guide...", "Douglas Adams")
p mon_livre # Appelle mon_livre.inspect

# => #<Livre titre="Le Guide..." auteur="Douglas Adams">
# C'est beaucoup plus informatif pour un développeur.

Les opérateurs sont des méthodes

En Ruby, la plupart des opérateurs sont en réalité des “sucres syntaxiques” pour des appels de méthode.

SyntaxeÉquivalent en méthode
a + ba.+(b)
a == ba.==(b)
mon_tableau[0]mon_tableau.[](0)
mon_tableau[0] = "val"mon_tableau.[]=(0, "val")

Cela signifie que vous pouvez redéfinir ces opérateurs dans vos propres classes pour leur donner un comportement spécifique.

class Panier
  def initialize
    @pommes = 0
  end

  def add(nombre)
    @pommes += nombre
  end

  # On redéfinit l'opérateur +
  def +(autre_panier)
    # ... logique pour fusionner deux paniers
  end
end

Bonnes pratiques

  • Définissez toujours une méthode to_s sur vos classes pour leur donner une représentation textuelle propre.
  • Définissez toujours une méthode inspect pour faciliter grandement le débogage de vos objets dans irb ou avec p.
  • La surcharge d’opérateurs est puissante mais doit être utilisée avec parcimonie et de manière intuitive. Redéfinir + pour faire une soustraction serait une très mauvaise idée !

Exercices

  1. Représentation d’un utilisateur :

    • Créez une classe Utilisateur avec un nom et un email.
    • Définissez la méthode to_s pour qu’elle retourne uniquement le nom.
    • Définissez la méthode inspect pour qu’elle retourne une chaîne détaillée comme #<Utilisateur nom="Alice" email="alice@exemple.com">.
    • Dans irb, créez une instance et testez l’affichage avec puts et p.
  2. Égalité personnalisée :

    • Dans votre classe Utilisateur, redéfinissez l’opérateur ==.
    • La méthode doit considérer deux utilisateurs comme égaux si leur email est identique, même si leur nom est différent.
      def ==(autre_utilisateur)
        self.email == autre_utilisateur.email
      end
    • Testez en créant deux objets Utilisateur avec le même email et en les comparant avec ==.
  3. Accès façon tableau :

    • Créez une classe Config qui stocke un hash de configuration dans une variable d’instance @data.
    • Définissez la méthode [] pour qu’elle permette d’accéder directement au hash interne.
      def [](cle)
        @data[cle]
      end
    • Créez une instance et vérifiez que vous pouvez faire config[:host] au lieu de config.data[:host].