Un ejemplo práctico de módulo útil

13 minutos de programación comentada en vivo de un módulo usando una dependencia y cosas que hemos visto en los últimos capítulos, como pipelines o funciones de alto orden transformadoras, con el objetivo de dar la excusa para refactorizarlo en el próximo vídeo.

Cuando trabajamos con Elixir y necesitamos manejar datos estructurados, como una lista de empleados, es fundamental organizar nuestro código para facilitar la transformación y almacenamiento de esa información. Para ello, podemos aprovechar la flexibilidad que nos ofrece Elixir con sus módulos y submódulos, y combinarlo con la manipulación de JSON para guardar datos en archivos.

Primero, podemos definir un submódulo dentro de un módulo principal, por ejemplo, Empresa.Empleado. En este submódulo declaramos una estructura con los campos que nos interesan, como nombre, posición y sueldo. Esto nos permite crear instancias claras y tipadas de empleados:

defmodule Empresa do
  defmodule Empleado do
    defstruct nombre: nil, posicion: nil, sueldo: nil
  end
end

Con esta estructura, podemos crear empleados fácilmente, por ejemplo:

empleado = %Empresa.Empleado{nombre: "Dani", posicion: "Desarrolla", sueldo: 30000}

Para gestionar la escritura de estos empleados en formato JSON, podemos crear otro módulo, Empresa.Writer, donde agruparemos funciones relacionadas con la conversión y almacenamiento. En Elixir, podemos definir módulos anidados usando la sintaxis con puntos, como Empresa.Writer, lo que nos da un espacio de nombres organizado sin necesidad de que los archivos o carpetas reflejen esta estructura.

Dentro de Empresa.Writer, una función clave es toMap, que convierte una estructura Empleado en un mapa. Usamos pattern matching para asegurarnos de que recibimos un empleado con los campos esperados y devolvemos un mapa con esos datos. Si la entrada no cumple, devolvemos nil para indicar un error o dato inválido:

defmodule Empresa.Writer do
  def toMap(%Empresa.Empleado{nombre: nombre, posicion: posicion, sueldo: sueldo}) do
    %{nombre: nombre, posicion: posicion, sueldo: sueldo}
  end

  def toMap(_), do: nil
end

Luego, para convertir ese mapa a JSON, podemos usar la función toJson, que llama a JSON.encode! (asumiendo que tenemos el paquete JSON instalado). Aquí simplificamos y asumimos que la codificación siempre funciona, aunque en un entorno real sería mejor manejar posibles errores:

defmodule Empresa.Writer do
  # ... toMap definido anteriormente ...

  def toJson(data) do
    JSON.encode!(data)
  end
end

Finalmente, para guardar el JSON en un archivo, creamos una función dump que recibe una cadena y la escribe en un archivo usando File.write/2. Esta función devuelve :ok si la operación fue exitosa o una tupla con el error en caso contrario:

defmodule Empresa.Writer do
  # ... toMap y toJson definidos anteriormente ...

  def dump(json_string) do
    File.write("empleados.json", json_string)
  end
end

Para facilitar el uso de estas funciones, podemos crear una función pública write que reciba una lista de empleados, los transforme a mapas, filtre los que sean válidos (no nil), los convierta a JSON y finalmente los guarde en el archivo. Usamos pipelines para encadenar estas operaciones de forma clara y concisa:

defmodule Empresa.Writer do
  # ... funciones anteriores ...

  def write(empleados) do
    empleados
    |> Enum.map(&toMap/1)
    |> Enum.filter(&(&1 != nil))
    |> toJson()
    |> dump()
  end
end

Probando este sistema, podemos crear varios empleados y llamar a Empresa.Writer.write/1 con la lista. Si todo funciona correctamente, se generará un archivo empleados.json con la representación JSON de los empleados válidos.

Este enfoque nos muestra cómo organizar el código en Elixir usando módulos y submódulos, cómo transformar estructuras en mapas para facilitar la codificación JSON, y cómo manejar la escritura en archivos con funciones del módulo File. Además, el uso de pipelines hace que el flujo de datos sea claro y expresivo, facilitando la lectura y mantenimiento del código.

Lista de reproducción
  1. 1
    mix
    10 minutos
  2. 2
    Documentando código: comentarios, docs y moduledocs
    10 minutos
  3. 3
    Atributos de módulo
    9 minutos
  4. 4
    Dependencias
    12 minutos
  5. 5
    Un ejemplo práctico de módulo útil
    13 minutos
  6. 6
    Alias e import
    10 minutos
  7. 7
    Sobre las macros, require y use
    11 minutos
  8. 8
    Typespecs (parte 1, usando tipos básicos)
    10 minutos
  9. 9
    Typespecs (parte 2, tipos propios y t())
    11 minutos
  10. 10
    Comportamientos
    11 minutos
  11. 11
    Tratamiento de errores con rescue
    8 minutos
  12. 12
    Elevando errores con raise
    8 minutos
  13. 13
    with
    14 minutos
  14. 14
    Sigilos
    8 minutos
  15. 15
    Tests con ExUnit
    12 minutos
  16. 16
    Más particularidades de ExUnit
    13 minutos
  17. 17
    Microservicios en Elixir con Plug
    11 minutos
  18. 18
    Cómo Plug.Router te ayuda a escribir microservicios en Elixir
    14 minutos
  19. 19
    ¿Cómo hacer rutas dinámicas en Phoenix y Plug?
    13 minutos