Parcours
/
Elixir
Elixir
/
Exercices
/
Bureau municipal
Bureau municipal

Bureau municipal

Exercice d'apprentissage

Introduction

Documentation

En Elixir, la documentation est un élément de premier ordre.

Deux attributs de module servent couramment à documenter le code : @moduledoc pour documenter un module, et @doc pour documenter la fonction qui suit l'attribut. L'attribut @moduledoc apparaît généralement sur la première ligne du module, tandis que l'attribut @doc apparaît juste avant la définition d'une fonction, ou avant la spécification de type de la fonction si elle en a une. La documentation s'écrit généralement dans une string multiligne avec la syntaxe heredoc.

La documentation Elixir s'écrit en Markdown.

defmodule String do
  @moduledoc """
  Strings in Elixir are UTF-8 encoded binaries.
  """

  @doc """
  Converts all characters in the given string to uppercase according to `mode`.

  ## Examples

      iex> String.upcase("abcd")
      "ABCD"

      iex> String.upcase("olá")
      "OLÁ"
  """
  def upcase(string, mode \\ :default)
end

Spécifications de types

Elixir est un langage à typage dynamique, ce qui signifie qu'il ne fournit pas de vérification de type à la compilation. On peut toutefois utiliser des spécifications de types comme une forme de documentation.

Une spécification de type peut être ajoutée à une fonction à l'aide de l'attribut de module @spec, juste avant la définition de la fonction. @spec est suivi du nom de la fonction et d'un tableau des types de tous ses arguments, entre parenthèses et séparés par des virgules. Le type de la valeur de retour est séparé des arguments de la fonction par un double deux-points ::.

@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length

Types

Voici les types les plus couramment utilisés :

  • les booléens : boolean()
  • les strings : String.t()
  • les nombres : integer(), non_neg_integer(), pos_integer(), float()
  • les tableaux : list()
  • une valeur de n'importe quel type : any()

Certains types peuvent aussi être paramétrés ; par exemple, list(integer) est un tableau d'entiers.

Des valeurs littérales peuvent aussi être utilisées comme types.

Une union de types peut s'écrire à l'aide de la barre verticale |. Par exemple, integer() | :error désigne soit un entier, soit le littéral d'atome :error.

Tous les types sont répertoriés dans la section « Typespecs » de la documentation officielle.

Nommer les arguments

Dans une spécification de type, les arguments peuvent aussi être nommés, ce qui est utile pour distinguer plusieurs arguments de même type. Le nom de l'argument, suivi de deux deux-points, précède le type de l'argument.

@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()

Types personnalisés

Les spécifications de types ne se limitent pas aux types intégrés. On peut définir des types personnalisés à l'aide de l'attribut de module @type. La définition d'un type personnalisé commence par le nom du type, suivi de deux deux-points, puis du type lui-même.

@type color :: {hue :: integer, saturation :: integer, lightness :: integer}

@spec to_hex(color()) :: String.t()

Un type personnalisé peut être utilisé depuis le module où il est défini, ou depuis un autre module.

Instructions

Tu travailles au bureau municipal depuis un moment, et tu as développé un ensemble d'outils qui accélèrent ton travail quotidien, par exemple pour remplir des formulaires.

Maintenant, un nouveau collègue te rejoint, et tu as réalisé que tes outils n'étaient peut-être pas auto-explicatifs. Il y a beaucoup de conventions étranges dans ton bureau, comme toujours remplir les formulaires en lettres majuscules et éviter de laisser des champs vides.

Tu décides d'écrire de la documentation pour que ton nouveau collègue puisse se lancer directement et commencer à utiliser tes outils.

1. Documente l'objectif des outils de formulaire

Ajoute de la documentation au module Form qui décrit son objectif. Elle doit dire :

A collection of loosely related functions helpful for filling out various forms at the city office.

2. Documente le remplissage de champs avec des valeurs vides

Ajoute de la documentation et une spécification de type à la fonction Form.blanks/1. La documentation doit dire :

Generates a string of a given length.

This string can be used to fill out a form field that is supposed to have no value.
Such fields cannot be left empty because a malicious third party could fill them out with false data.

La spécification de type doit expliquer que la fonction accepte un seul argument, un entier non négatif, et renvoie une string.

3. Documente le découpage de valeurs en tableaux de lettres majuscules

Ajoute de la documentation et une spécification de type à la fonction Form.letters/1. La documentation doit dire :

Splits the string into a list of uppercase letters.

This is needed for form fields that don't offer a single input for the whole string,
but instead require splitting the string into a predefined number of single-letter inputs.

La spécification de type doit expliquer que la fonction accepte un seul argument, une string, et renvoie un tableau de strings.

4. Documente la vérification qu'une valeur tient dans un champ avec une longueur maximale

Ajoute de la documentation et une spécification de type à la fonction Form.check_length/2. La documentation doit dire :

Checks if the value has no more than the maximum allowed number of letters.

This is needed to check that the values of fields do not exceed the maximum allowed length.
It also tells you by how much the value exceeds the maximum.

La spécification de type doit expliquer que la fonction accepte deux arguments, une string et un entier non négatif, et renvoie l'une de deux valeurs possibles. Elle renvoie soit l'atome :ok, soit un tuple de 2 éléments dont le premier est l'atome :error et le second un entier positif.

5. Documente les différents formats d'adresse

Pour une raison qui t'échappe, le système interne du bureau municipal utilise deux façons différentes de représenter les adresses : soit sous forme de map, soit sous forme de tuple.

Documente ce fait en définissant trois types publics personnalisés :

  • address_map - un map avec les clés :street, :postal_code et :city. Chaque clé contient une valeur de type string.
  • address_tuple - un tuple avec trois valeurs : street, postal_code et city. Chaque valeur est de type string. Différencie les valeurs en leur donnant des noms dans la spécification de type.
  • address - peut être soit un address_map, soit un address_tuple.

6. Documente le formatage de l'adresse

Ajoute de la documentation et une spécification de type à la fonction Form.format_address/1. La documentation doit dire :

Formats the address as an uppercase multiline string.

La spécification de type doit expliquer que la fonction accepte un argument, une adresse, et renvoie une string.

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Elixir Exercism

Prêt à commencer Bureau municipal ?

Inscris-toi sur Exercism pour apprendre et maîtriser Elixir avec 58 concepts168 exercices, et un vrai mentorat humain, le tout gratuitement.