Aujourd’hui, on va parler arguments de méthodes et bonnes pratiques.

Pour avoir un code lisible et maintenable, il faut suivre quelques règles concernant l’ordre de ces arguments.

Conseil 1 : Les arguments optionnels

Quand on a une méthode avec plusieurs arguments, dont certains ont une valeur par défaut, pensez à les mettre en fin de chaîne :

Prenez cette définition de méthode

def foo(a, b = "val_b", c)
  # du code...
end

Tout semble correct, mais à l’usage, on se rend compte qu’il est impossible d’appeler cette méthode en spécifiant une valeur pour a et c sans nécessairement spécifier la valeur de b. On se retrouve à écrire

foo("val_a", "val_b", "val_c")

…ce qui est un peu idiot : à quoi bon définir une valeur par défaut à b si on est obligés de la re-spécifier à chaque appel ? ¯\(ツ)

Solution : placer les arguments optionnels fin de chaîne…

def foo(a, c, b = "val_b")
  # du code...
end

Ainsi, on pourra écrire

foo("val_a", "val_c")

sans problème 😎.

Conseil 2 : les keyword arguments (ou “kwargs”)

Le conseil 1 a une limite : quand on a 2 arguments optionnels, et qu’on a besoin de spécifier uniquement le 2e, il faudra alors tout de même spécifier celui qui le précède. Avec 2 arguments optionnels, la douleur est gérable, mais imaginez le bordel quand on passe à 4, 5 ou 6…

Exemple :

def foo(a, b, c = "val_c", d = "val_d", e = "val_e")
  # du code...
end

Si je veux appeler cette méthode en ne changeant que e, je dois quand même re-spécifier c et d :

foo("val_a", "val_b", "val_c", "val_d", "ma_val_e")

Quand on a un grand codebase et qu’on se retrouve avec ce genre d’appels :

foo(user.id, "DONE", true, false, 1, "always", true, post.id)`

On passe son temps à se dire “c’est quoi déjà ce false ? le 1 il fait quoi déjà ? Pourquoi "always" ? …”

Quand on a un problème complexe à régler ou une urgence à cause d’un bug en prod, c’est bon de se retirer de la charge mentale, croyez-moi.

Solution :

À partir du moment où on a deux arguments optionnels ou plus, il est bon de passer à des keyword arguments. Ça a de nombreux intérêts :

  • on s’en tape de l’ordre des arguments
  • on s’en tape des valeurs par défaut

La méthode précédente devient :

def foo(a, b, c: "val_c", d: "val_d", e: "val_e")
  # du code...
end

c, d, e ont des valeurs par défaut, mais si je ne veux modifier que e, je peux désormais faire

foo("val_a", "val_b", e: "ma_val_e")

💡BONUS: les arguments deviennent plus clairs, car ils ont un nom… Pas besoin de vérifier la déclaration de ma méthode pour se souvenir de ce qu’elle représente.

Level ninja master — le conseil du clean coder :

Si votre méthode a plus de 1 argument, passez aux keyword arguments à partir du 2e. Dans tous les cas.

Exemple

def send_message_to(recipient, confirm: true, bcc: "joe@bill.com", cc: "jane@doe.com")
  # ruby code
end

Dans cet exemple, le premier argument est clair, grâce au nom de la méthode. J’envoie un message au destinataire. Sans options, cette méthode sera appelée ainsi :

send_message_to("sam@sam.com")

C’est clair et concis.

Les autres arguments sont des keyword arguments. On en voit immédiatement l’intérêt en comparant un appel sur une version sans keywords :

send_message("recipient@mail.com", false, "joe@bill.com", "paul@smith.com")

et avec keywords :

send_message_to("sam@sam.com", bcc: "paul@smith.com", confirm: false)

J’insiste au passage sur le nommage de la méthode. Ruby se lit comme de l’anglais. Pensez à écrire du code qui se lit à voix haute.

Si vous prenez ces bonnes habitudes, votre futur vous vous remerciera quand il faudra relire le code pour le déboguer ou le modifier pour ajouter un argument supplémentaire à cette méthode.