Rutas
/
Elixir
Elixir
/
Ejercicios
/
Oficina de la ciudad
Oficina de la ciudad

Oficina de la ciudad

Ejercicio de aprendizaje

Introducción

Documentación

La documentación en Elixir es un ciudadano de primera clase.

Hay dos atributos de módulo que se usan comúnmente para documentar tu código: @moduledoc para documentar un módulo y @doc para documentar una función que sigue al atributo. El atributo @moduledoc suele aparecer en la primera línea del módulo, y el atributo @doc suele aparecer justo antes de la definición de una función, o de la especificación de tipos de la función, si la tiene. La documentación se suele escribir en un string multilínea usando la sintaxis heredoc.

La documentación de Elixir se escribe 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

Especificaciones de tipos

Elixir es un lenguaje de tipado dinámico, lo que significa que no proporciona comprobaciones de tipos en tiempo de compilación. Aun así, las especificaciones de tipos pueden usarse como una forma de documentación.

Se puede añadir una especificación de tipos a una función usando el atributo de módulo @spec justo antes de la definición de la función. A @spec le sigue el nombre de la función y una lista con los tipos de todos sus argumentos, entre paréntesis y separados por comas. El tipo del valor devuelto se separa de los argumentos de la función con un doble dos puntos ::.

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

Tipos

Los tipos más usados son:

  • booleanos: boolean()
  • strings: String.t()
  • números: integer(), non_neg_integer(), pos_integer(), float()
  • listas: list()
  • un valor de cualquier tipo: any()

Algunos tipos también se pueden parametrizar; por ejemplo, list(integer) es una lista de enteros.

Los valores literales también se pueden usar como tipos.

Una unión de tipos se puede escribir usando la barra vertical |. Por ejemplo, integer() | :error significa un entero o el literal de átomo :error.

Puedes encontrar una lista completa de todos los tipos en la sección «Typespecs» de la documentación oficial.

Nombrar los argumentos

Los argumentos también se pueden nombrar en la especificación de tipos, lo cual es útil para distinguir varios argumentos del mismo tipo. El nombre del argumento, seguido de un doble dos puntos, va antes del tipo del argumento.

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

Tipos personalizados

Las especificaciones de tipos no se limitan a los tipos integrados. Los tipos personalizados se pueden definir usando el atributo de módulo @type. La definición de un tipo personalizado empieza con el nombre del tipo, seguido de un doble dos puntos y, a continuación, el propio tipo.

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

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

Un tipo personalizado se puede usar desde el mismo módulo donde se define, o desde otro módulo.

Instrucciones

Llevas un tiempo trabajando en la oficina municipal y has desarrollado un conjunto de herramientas que agilizan tu trabajo diario, por ejemplo a la hora de rellenar formularios.

Ahora se incorpora un nuevo colega y te has dado cuenta de que puede que tus herramientas no sean autoexplicativas. En tu oficina hay un montón de convenciones extrañas, como rellenar siempre los formularios con letras mayúsculas y evitar dejar campos vacíos.

Decides escribir un poco de documentación para que a tu nuevo colega le resulte más fácil ponerse manos a la obra y empezar a usar tus herramientas.

1. Documenta el propósito de las herramientas de formularios

Añade al módulo Form documentación que describa su propósito. Debería decir:

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

2. Documenta cómo rellenar campos con valores en blanco

Añade documentación y un typespec a la función Form.blanks/1. La documentación debería decir:

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.

El typespec debería explicar que la función acepta un único argumento, un número entero no negativo, y devuelve un string.

3. Documenta cómo dividir valores en arrays de letras mayúsculas

Añade documentación y un typespec a la función Form.letters/1. La documentación debería decir:

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.

El typespec debería explicar que la función acepta un único argumento, un string, y devuelve un array de strings.

4. Documenta cómo comprobar si un valor cabe en un campo con una longitud máxima

Añade documentación y un typespec a la función Form.check_length/2. La documentación debería decir:

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.

El typespec debería explicar que la función acepta dos argumentos, un string y un número entero no negativo, y devuelve uno de dos valores posibles. Devuelve o bien el átomo :ok o una tupla de dos elementos en la que el primero es el átomo :error y el segundo un número entero positivo.

5. Documenta los distintos formatos de dirección

Por alguna razón que desconoces, el sistema interno de la oficina municipal usa dos formas distintas de representar las direcciones: como un mapa o como una tupla.

Documenta este hecho definiendo tres tipos públicos personalizados:

  • address_map: un mapa con las claves :street, :postal_code y :city. Cada clave contiene un valor de tipo string.
  • address_tuple: una tupla con tres valores, street, postal_code y city. Cada valor es de tipo string. Diferencia los valores dándoles nombres en el typespec.
  • address: puede ser un address_map o un address_tuple.

6. Documenta cómo dar formato a la dirección

Añade documentación y un typespec a la función Form.format_address/1. La documentación debería decir:

Formats the address as an uppercase multiline string.

El typespec debería explicar que la función acepta un argumento, una dirección, y devuelve un string.

Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Elixir Exercism

¿Listo para empezar Oficina de la ciudad?

Regístrate en Exercism para aprender y dominar Elixir con 58 conceptos168 ejercicios y mentoría humana real, todo gratis.