Translate

Mostrando las entradas con la etiqueta Elixir. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Elixir. Mostrar todas las entradas

lunes, 9 de diciembre de 2024

¿Cómo Crear una Librería en Elixir?


Elixir es un lenguaje poderoso y flexible, ideal para crear librerías reutilizables. En esta guía, aprenderás cómo iniciar y publicar tu propia librería.

1. Inicializando el Proyecto


Para empezar, crea un nuevo proyecto con mix:

mix new mi_libreria --module MiLibreria


Esto generará una estructura básica con carpetas como `lib` (para el código) y `mix.exs` (configuración del proyecto).  

En el archivo `mix.exs`, ajusta la metadata básica, como nombre y descripción, para que sea más descriptiva:


def project do

  [

    app: :mi_libreria,

    version: "0.1.0",

    elixir: "~> 1.15",

    description: "Una librería simple para manipular cadenas",

    start_permanent: Mix.env() == :prod

  ]

end


2. Implementando la Funcionalidad


Agregamos lógica en lib/mi_libreria.ex. Por ejemplo, una función para convertir cadenas a mayúsculas:


defmodule MiLibreria do

  @moduledoc """

  MiLibreria es una colección de funciones útiles para cadenas.

  """


  @doc """

  Convierte una cadena en mayúsculas.


  ## Ejemplo

      iex> MiLibreria.convertir_mayusculas("hola")

      "HOLA"

  """

  def convertir_mayusculas(cadena) when is_binary(cadena) do

    String.upcase(cadena)

  end

end


La documentación (`@moduledoc` y `@doc`) ayuda a otros desarrolladores a entender y usar tu librería.


3. Generando Documentación con ExDoc


Añade `ExDoc` al archivo `mix.exs` para generar documentación en HTML:


{:ex_doc, "~> 0.29", only: :dev, runtime: false}


Instala las dependencias:


mix deps.get


Genera la documentación:


mix docs


Esto crea una carpeta `doc` con una versión navegable de tu documentación.


4. Publicando en Hex


Crea una cuenta en Hex.pm:

   Tenemos que registrarnos en Hex.pm y genera una clave de autenticación con:  


   mix hex.user register


Completa la Metadata en mix.exs:


   def project do

     [

       app: :mi_libreria,

       version: "0.1.0",

       description: "Librería para manipulación de cadenas",

       package: package_info()

     ]

   end


   defp package_info do

     [

       maintainers: ["Tu Nombre"],

       licenses: ["MIT"],

       links: %{"GitHub" => "https://github.com/tu_usuario/mi_libreria"}

     ]

   end


  Publica la librería:


   mix hex.publish


Crear una librería en Elixir es sencillo y gratificante. Con estas herramientas y pasos, puedes compartir tu trabajo con la comunidad y contribuir al crecimiento del ecosistema. 

martes, 3 de diciembre de 2024

Ecto = bases de datos + Elixir



Ecto es la herramienta principal para interactuar con bases de datos en el ecosistema de Elixir. Más que un simple ORM, Ecto combina la construcción de consultas, validaciones y migraciones, brindando una experiencia robusta y funcional.

Ecto es un toolkit para bases de datos en Elixir que se compone de tres partes principales:

  • Ecto.Schema: Define la estructura de los datos.
  • Ecto.Changeset: Maneja validaciones y transformaciones.
  • Ecto.Query: Facilita la construcción de consultas.


Para empezar debemos añadir las dependencias en mix.exs:


defp deps do

  [

    {:ecto_sql, "~> 3.10"},

    {:postgrex, ">= 0.0.0"} # Adaptador para PostgreSQL

  ]

end


Luego debemos configurar el repositorio en config/config.exs:


config :mi_app, MiApp.Repo,

  database: "mi_app_db",

  username: "usuario",

  password: "contraseña",

  hostname: "localhost"


config :mi_app, ecto_repos: [MiApp.Repo]


Crear el repositorio:


mix ecto.gen.repo -r MiApp.Repo


Para luego ejecutar migraciones iniciales:


mix ecto.create


Creamos un módulo para tu esquema, que representa una tabla en la base de datos:


defmodule MiApp.Usuario do

  use Ecto.Schema

  schema "usuarios" do

    field :nombre, :string

    field :email, :string

    field :edad, :integer

    timestamps()

  end

end


Y ahora hagamos consultas básicas con Ecto.Query. Ecto ofrece una sintaxis declarativa para construir consultas SQL. Ejemplo:


import Ecto.Query


# Obtener todos los usuarios

query = from u in "usuarios", select: u

MiApp.Repo.all(query)


# Filtrar usuarios mayores de 18 años

query = from u in MiApp.Usuario, where: u.edad > 18, select: u.nombre

MiApp.Repo.all(query)


Los cambios en los datos se gestionan con Ecto.Changeset, lo que facilita aplicar validaciones.


defmodule MiApp.Usuario do

  use Ecto.Schema

  import Ecto.Changeset


  schema "usuarios" do

    field :nombre, :string

    field :email, :string

    field :edad, :integer

    timestamps()

  end


  def cambios_usuario(usuario, attrs) do

    usuario

    |> cast(attrs, [:nombre, :email, :edad])

    |> validate_required([:nombre, :email])

    |> validate_format(:email, ~r/@/)

    |> validate_number(:edad, greater_than_or_equal_to: 0)

  end

end


Ecto permite definir relaciones como has_many, belongs_to y many_to_many. Veamos un ejemplo: 


defmodule MiApp.Usuario do

  use Ecto.Schema


  schema "usuarios" do

    field :nombre, :string

    has_many :posts, MiApp.Post

    timestamps()

  end

end


defmodule MiApp.Post do

  use Ecto.Schema


  schema "posts" do

    field :titulo, :string

    belongs_to :usuario, MiApp.Usuario

    timestamps()

  end

end


Consultas relacionales:


query = from u in MiApp.Usuario, preload: [:posts]

usuarios = MiApp.Repo.all(query)


Ahora actualización de registros:


usuario = MiApp.Repo.get(MiApp.Usuario, 1)

cambioset = MiApp.Usuario.cambios_usuario(usuario, %{nombre: "Nuevo Nombre"})

MiApp.Repo.update(cambioset)


Y Borramos:


usuario = MiApp.Repo.get(MiApp.Usuario, 1)

MiApp.Repo.delete(usuario)


Las migraciones permiten gestionar cambios en el esquema de la base de datos.


Crear una migración:


mix ecto.gen.migration crea_usuarios


Editar la migración:


defmodule MiApp.Repo.Migrations.CreaUsuarios do

  use Ecto.Migration


  def change do

    create table(:usuarios) do

      add :nombre, :string

      add :email, :string

      add :edad, :integer

      timestamps()

    end

  end

end


Ejecutar migración:

mix ecto.migrate


Ecto transforma la interacción con bases de datos en una experiencia declarativa, segura y extensible. Ya sea que estés manejando datos simples o esquemas complejos, Ecto te da las herramientas para hacerlo de manera eficiente.


domingo, 1 de diciembre de 2024

Creación de pruebas en Elixir con ExUnit


ExUnit es el framework de pruebas integrado en Elixir, diseñado para ayudarte a escribir pruebas claras y efectivas. Desde pruebas unitarias hasta pruebas más complejas, ExUnit ofrece las herramientas necesarias para asegurar que tu código funcione como esperas.

ExUnit viene incluido con Elixir, por lo que no necesitas instalar dependencias adicionales. Solo asegúrate de que tu entorno de desarrollo esté configurado para ejecutarlas.


ExUnit.start()


Los archivos de pruebas suelen estar en el directorio test/ y deben tener el sufijo _test.exs.


Veamos un ejemplo: 


defmodule MiApp.MiModuloTest do

  use ExUnit.Case


  test "una prueba simple" do

    assert 1 + 1 == 2

  end

end


Para ejecutar las pruebas, utiliza:


mix test


Las aserciones son fundamentales para comprobar el comportamiento esperado. ExUnit ofrece varias:


- assert: Verifica que una condición sea verdadera.

- refute: Verifica que una condición sea falsa.

- assert_raise: Verifica que se lance una excepción específica.


Veamos algunos ejemplos: 


assert String.length("Hola") == 4

refute String.contains?("Hola", "mundo")

assert_raise ArgumentError, fn -> String.to_integer("no_numero") end


ExUnit permite convertir ejemplos en la documentación en pruebas automáticas.


defmodule MiModulo do

  @doc """

  Duplica un número.


  ## Ejemplo


      iex> MiModulo.duplicar(2)

      4


  """

  def duplicar(n), do: n * 2

end


defmodule MiModuloTest do

  use ExUnit.Case

  doctest MiModulo

end


Para organizar tus pruebas, podemos usar describe:


defmodule MiApp.MiModuloTest do

  use ExUnit.Case


  describe "función suma/2" do

    test "suma números positivos" do

      assert MiApp.MiModulo.suma(2, 3) == 5

    end


    test "suma números negativos" do

      assert MiApp.MiModulo.suma(-2, -3) == -5

    end

  end

end


Y podemos usar setup para definir configuraciones comunes:


defmodule MiApp.MiModuloTest do

  use ExUnit.Case


  setup do

    {:ok, numero: 42}

  end


  test "usa datos de setup", %{numero: numero} do

    assert numero == 42

  end

end


Elixir soporta pruebas concurrentes por defecto. Si necesitas pruebas asincrónicas, podemos indícarlo:


defmodule MiApp.AsyncTest do

  use ExUnit.Case, async: true


  test "prueba concurrente" do

    Task.async(fn -> :ok end)

    |> Task.await()

    |> assert == :ok

  end

end


ExUnit facilita capturar salidas a consola y logs:


import ExUnit.CaptureIO


test "captura salida de IO.puts" do

  salida = capture_io(fn -> IO.puts("Hola, mundo!") end)

  assert salida == "Hola, mundo!\n"

end


import ExUnit.CaptureLog


test "captura logs" do

  salida = capture_log(fn -> Logger.info("Esto es un log") end)

  assert salida =~ "Esto es un log"

end


Para ejecutar pruebas individuales, podemos usar:


mix test test/mi_modulo_test.exs:10


ExUnit es una herramienta flexible y poderosa para escribir pruebas en Elixir. Desde las pruebas más básicas hasta configuraciones avanzadas, te ayuda a mantener un código confiable y fácil de mantener. 


domingo, 24 de noviembre de 2024

Macros en Elixir


Elixir no solo es conocido por su modelo de concurrencia y eficiencia, sino también por su poderosa capacidad de metaprogramación, gracias a sus macros. Con ellas, puedes escribir código que genera más código, permitiendo crear herramientas, DSLs (lenguajes específicos de dominio) y extender el lenguaje.

En Elixir, las macros son funciones especiales que reciben expresiones y devuelven otras expresiones transformadas. Estas transformaciones ocurren en tiempo de compilación, lo que las diferencia de las funciones normales.


defmodule EjemploMacro do

  defmacro saludo do

    quote do

      IO.puts("¡Hola, mundo!")

    end

  end

end


require EjemploMacro

EjemploMacro.saludo()

# Salida: ¡Hola, mundo!


¿Cómo funciona esto?

  • quote/2: Captura código como datos. Convierte un bloque en su representación AST (Abstract Syntax Tree).
  • unquote/1: Inserta valores dinámicos en el código generado.
  • defmacro: Declara macros.


Imagina que necesitas loguear todas las llamadas a funciones en un módulo. Con macros, puedes automatizar este proceso:


defmodule LoggerMacro do

  defmacro log_call(fun_name, do: block) do

    quote do

      IO.puts("Llamando a #{unquote(fun_name)}")

      result = unquote(block)

      IO.puts("Resultado: #{result}")

      result

    end

  end

end


defmodule Ejemplo do

  require LoggerMacro


  def calcular do

    LoggerMacro.log_call(:calcular) do

      1 + 2

    end

  end

end


Ejemplo.calcular()

# Salida:

# Llamando a calcular

# Resultado: 3


Los macros son herramientas poderosas para crear DSLs. Por ejemplo, un DSL para rutas web (similar a Plug.Router):


defmodule MiniRouter do

  defmacro route(method, path, do: block) do

    quote do

      def handle_request(unquote(method), unquote(path)) do

        unquote(block)

      end

    end

  end

end


defmodule MyRouter do

  require MiniRouter


  MiniRouter.route(:get, "/") do

    IO.puts("¡Bienvenido!")

  end


  MiniRouter.route(:post, "/create") do

    IO.puts("¡Creando recurso!")

  end

end


MyRouter.handle_request(:get, "/")

# Salida: ¡Bienvenido!


Aunque las macros son útiles, pueden complicar el código:

  • Usa macros solo si la lógica no puede resolverse con funciones.
  • Opta por composición funcional siempre que sea posible.


Las macros en Elixir son una herramienta increíblemente poderosa para extender el lenguaje y crear soluciones elegantes. Sin embargo, requieren un buen entendimiento del AST y un uso responsable. 

miércoles, 20 de noviembre de 2024

Introducción a Plug de Elixir


Elixir, con su enfoque funcional y capacidad para manejar concurrencia de manera eficiente, es un lenguaje ideal para aplicaciones web. Si bien Phoenix es el framework más conocido, Plug es una alternativa minimalista perfecta para quienes buscan simplicidad y flexibilidad, similar a lo que Sinatra ofrece en Ruby.

Plug es un conjunto de especificaciones y módulos para construir aplicaciones web en Elixir. Se centra en el manejo de conexiones HTTP y proporciona herramientas básicas para construir rutas y middlewares.

  • Minimalista: Perfecto para aplicaciones simples o como base para proyectos más grandes.
  • Rápido: Aprovecha Cowboy, un servidor HTTP eficiente.
  • Flexible: Puedes usarlo directamente o integrarlo en frameworks más grandes como Phoenix.

Para comenzar, necesitas agregar Plug y Cowboy a tu proyecto. En el archivo `mix.exs`:

Antes creas el proyecto : 

mix new hello_world

Y Luego editamos mix.exs


defp deps do

  [

    {:plug, "~> 1.13"},

    {:plug_cowboy, "~> 2.6"}

  ]

end


Ejecuta mix deps.get para instalar las dependencias.

Para comenzar a crear Plugs, necesitamos conocer, y adherirse a la especificación Plug. Afortunadamente para nosotros, sólo hay dos funciones necesarias: init/1 y call/2.


Aquí hay un Plug simple que devuelve “Hello World!”:


defmodule Example.HelloWorldPlug do

  import Plug.Conn


  def init(options), do: options


  def call(conn, _opts) do

    conn

    |> put_resp_content_type("text/plain")

    |> send_resp(200, "Hello World!")

  end

end

Guarda el archivo en lib/example/hello_world_plug.ex.

La función init/1 se utiliza para inicializar las opciones de nuestros Plugs. Esta es llamada por el árbol de supervisión. De momento, está será una lista vacía que es ignorada.

El valor retornado por la función init/1 eventualmente será pasado a call/2 como su segundo argumento.

La función call/2 es ejecutada por cada petición que viene desde el servidor web, Cowboy. Esta recibe una estructura de conexión %Plug.Conn{} como su primer argumento y se espera que retorne una estructura de conexión %Plug.Conn{}.

Debido a que estamos iniciando nuestra aplicación plug desde cero, necesitamos definir el módulo de la aplicación. Actualiza lib/example.ex para iniciar y supervisar Cowboy:


defmodule Example do

  use Application

  require Logger


  def start(_type, _args) do

    children = [

      Plug.Adapters.Cowboy.child_spec(:http, Example.HelloWorldPlug, [], port: 8080)

    ]


    Logger.info("Started application")


    Supervisor.start_link(children, strategy: :one_for_one)

  end

end


Esto supervisa Cowboy, y a su vez, supervisa nuestro HelloWorldPlug.

En la petición a Plug.Adapters.Cowboy.child_spec/4, el tercer argumento será pasado a Example.HelloWorldPlug.init/1.

Aún no hemos terminado. Abre mix.exs de nuevo, y busca la función applications. De momento la parte de aplication en mix.exs necesita dos cosas:

  • Una lista de aplicaciones de dependencia (cowboy, logger, and plug) que necesintan iniciar, y
  • Configuración para nuestra aplicación, la cual también deberá iniciar automáticamente. Vamos a actualizarla para hacerlo:

def application do

  [

    extra_applications: [:cowboy, :logger, :plug],

    mod: {Example, []}

  ]

end

Estamos listos para probar este servidor web, minimalístico basado en Plug. En la línea de comando ejecuta:

mix run --no-halt

Cuando todo termine de compilar, y el mensaje [info] Started app aparece, abre el explorador web en 127.0.0.1:8080. Este debera de desplegar:

Hello World!

Plug es ideal para quienes buscan simplicidad y control en sus aplicaciones web. Su integración con Cowboy y su flexibilidad lo hacen una excelente opción para proyectos ligeros, APIs o como base para aprender Elixir.

Dejo link: https://elixirschool.com/es/lessons/misc/plug

lunes, 18 de noviembre de 2024

Elixir: Concurrencia Hecha Sencilla


Elixir, basado en la máquina virtual de Erlang (BEAM), utiliza el modelo de actores como su paradigma de concurrencia. Este modelo permite manejar múltiples procesos de manera eficiente y segura, lo que lo hace ideal para sistemas distribuidos y concurrentes.

El modelo de actores es un paradigma de concurrencia en el que las entidades llamadas actores:

  • Son unidades independientes de ejecución.
  • Tienen su propio estado y no comparten memoria con otros actores.
  • Se comunican mediante el envío de mensajes.

En Elixir, los procesos son implementaciones del modelo de actores y son extremadamente ligeros gracias a la eficiencia de la VM de Erlang.

En Elixir, los actores se implementan utilizando módulos como GenServer. Vamos a crear un ejemplo básico de un contador que incrementa su valor en respuesta a mensajes.

   defmodule Counter do

     use GenServer


     # Inicio del actor con un estado inicial

     def start_link(initial_value) do

       GenServer.start_link(__MODULE__, initial_value, name: __MODULE__)

     end


     # Callbacks

     def init(initial_value) do

       {:ok, initial_value}

     end


     # Manejar el mensaje para incrementar el contador

     def handle_call(:increment, _from, state) do

       {:reply, state + 1, state + 1}

     end


     def handle_call(:get, _from, state) do

       {:reply, state, state}

     end

   end


   # Iniciar el actor con un valor inicial de 0

   {:ok, _pid} = Counter.start_link(0)


   # Incrementar el contador

   Counter.call(:increment) # Devuelve 1

   Counter.call(:increment) # Devuelve 2


   # Obtener el valor actual

   Counter.call(:get) # Devuelve 2


Ahora crearemos múltiples actores que se comuniquen entre sí. Supongamos que tenemos un sistema donde un actor recopila datos y otro los procesa.


   defmodule DataCollector do

     use GenServer


     def start_link(processor_pid) do

       GenServer.start_link(__MODULE__, processor_pid, name: __MODULE__)

     end


     def init(processor_pid) do

       {:ok, processor_pid}

     end


     def handle_cast({:collect, data}, processor_pid) do

       send(processor_pid, {:process, data})

       {:noreply, processor_pid}

     end

   end

 

   defmodule DataProcessor do

     use GenServer


     def start_link(_) do

       GenServer.start_link(__MODULE__, [], name: __MODULE__)

     end


     def init(_) do

       {:ok, []}

     end


     def handle_info({:process, data}, state) do

       IO.puts("Procesando: #{data}")

       {:noreply, [data | state]}

     end

   end


   {:ok, processor_pid} = DataProcessor.start_link([])

   {:ok, collector_pid} = DataCollector.start_link(processor_pid)


   GenServer.cast(collector_pid, {:collect, "dato1"})

   GenServer.cast(collector_pid, {:collect, "dato2"})


DataCollector recopila datos y los envía a DataProcessor. DataProcessor procesa los datos recibidos y los guarda en su estado.


Como ventaja del modelo de actores tenemos:

Aislamiento Total: Los actores no comparten memoria, eliminando condiciones de carrera.

Escalabilidad: Los procesos son livianos y se ejecutan de manera concurrente.

Resiliencia: Si un actor falla, el sistema no se detiene; los supervisores pueden reiniciarlo.


El modelo de actores de Elixir proporciona una forma poderosa, segura y eficiente de manejar concurrencia. Al entender cómo implementar actores y supervisarlos, puedes construir sistemas robustos que escalen sin problemas.

miércoles, 6 de noviembre de 2024

Supervisores y Árboles de Supervisión en Elixir


La concurrencia es uno de los puntos fuertes de Elixir, y el modelo de supervisión es fundamental para construir aplicaciones resilientes. 

El modelo de concurrencia en Elixir está basado en procesos ligeros y aislados que se comunican entre sí enviándose mensajes, siguiendo el paradigma del modelo de actor. Estos procesos no comparten memoria, lo que reduce los riesgos de condiciones de carrera y hace que el sistema sea más seguro.

Supervisores son procesos especiales en Elixir que gestionan y supervisan otros procesos, reiniciándolos si fallan. Esto asegura que la aplicación siga funcionando incluso cuando ocurren errores.

Elixir provee módulos como Supervisor y Task.Supervisor para gestionar procesos de manera eficiente. Vamos a crear un simple supervisor que inicie y supervise un proceso Worker.


Primero, definimos el proceso Worker usando GenServer:


defmodule Worker do

  use GenServer


  def start_link(initial_state) do

    GenServer.start_link(__MODULE__, initial_state, name: __MODULE__)

  end


  def init(state), do: {:ok, state}


  def handle_cast(:do_work, state) do

    # Simulamos un trabajo que puede fallar

    if :rand.uniform() > 0.5 do

      {:stop, :error, state}

    else

      IO.puts("Trabajo completado exitosamente")

      {:noreply, state}

    end

  end

end


Ahora, creamos un supervisor que inicie y supervise nuestro proceso Worker:


defmodule MySupervisor do

  use Supervisor


  def start_link(_opts) do

    Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)

  end


  def init(:ok) do

    children = [

      {Worker, :some_initial_state}

    ]


    Supervisor.init(children, strategy: :one_for_one)

  end

end


Elixir ofrece varias estrategias de supervisión que permiten diferentes enfoques de recuperación en caso de fallo:


  • :one_for_one: Solo reinicia el proceso que falló.
  • :one_for_all: Reinicia todos los procesos supervisados si uno falla.
  • :rest_for_one: Reinicia el proceso que falló y todos los procesos que fueron iniciados después de este.
  • :simple_one_for_one: Útil para supervisar un número dinámico de procesos que comparten el mismo tipo de inicialización.


En el ejemplo anterior, usamos :one_for_one, que reiniciará únicamente el proceso Worker si falla.

Los árboles de supervisión permiten organizar supervisores en una jerarquía. Esto es ideal para sistemas complejos que necesitan distintos niveles de supervisión.

Por ejemplo, podríamos tener un supervisor principal que supervise varios supervisores secundarios, cada uno a cargo de procesos específicos. Aquí hay una estructura básica:


defmodule MainSupervisor do

  use Supervisor


  def start_link(_opts) do

    Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)

  end


  def init(:ok) do

    children = [

      MySupervisor, # Supervisor de workers

      AnotherSupervisor # Otro supervisor

    ]


    Supervisor.init(children, strategy: :one_for_all)

  end

end


En esta configuración:

  • MainSupervisor es el supervisor principal.
  • MySupervisor y AnotherSupervisor son supervisores secundarios.
  • Si cualquiera de los supervisores secundarios falla, MainSupervisor los reiniciará junto con sus respectivos procesos.


Los supervisores y árboles de supervisión en Elixir son una herramienta poderosa para construir sistemas concurrentes y resilientes. Siguiendo estos patrones, puedes crear aplicaciones que se recuperen automáticamente de fallos sin necesidad de intervención manual. Esto convierte a Elixir en una excelente opción para aplicaciones que requieren alta disponibilidad.

domingo, 3 de noviembre de 2024

Monadex en Elixir


Elixir, aunque no tiene una implementación nativa de mónadas como Haskell, permite patrones funcionales que se pueden potenciar con bibliotecas como Monadex. Monadex nos brinda las clásicas Maybe y Either, permitiéndonos manejar valores opcionales y errores de forma compositiva, y aprovechando el poder de la programación funcional.

Primero, agrega Monadex a tu archivo mix.exs:

defp deps do

  [

    {:monadex, "~> 1.1"}

  ]

end


Ejecuta mix deps.get para instalar la dependencia.


La mónada Maybe es útil cuando deseas trabajar con valores que podrían ser nil. Monadex provee funciones para encapsular operaciones en un flujo que se detiene automáticamente si algún valor es nil.

Supongamos que estamos buscando datos de un usuario y aplicando transformaciones a su información.


alias Monadex.Maybe


def get_user_info(user) do

  Maybe.return(user)

  |> Maybe.and_then(&get_name/1)

  |> Maybe.and_then(&upcase_name/1)

end


defp get_name(%{name: name}), do: Maybe.return(name)

defp get_name(_), do: Maybe.nothing()


defp upcase_name(name), do: Maybe.return(String.upcase(name))


user = %{name: "Juan"}

get_user_info(user) # Retorna: {:ok, "JUAN"}


Either es útil para operaciones que pueden tener éxito o devolver un error. Similar a Maybe, Either permite encadenar operaciones pero distingue entre :ok y :error.


Procesemos datos que pueden fallar en algún paso.


alias Monadex.Either


def process_data(data) do

  Either.return(data)

  |> Either.and_then(&validate/1)

  |> Either.and_then(&transform/1)

end


defp validate(data) do

  if valid?(data), do: Either.ok(data), else: Either.error("Invalid data")

end


defp transform(data), do: Either.ok("Processed: #{data}")


process_data("valid_data")  # Retorna: {:ok, "Processed: valid_data"}

process_data("invalid")      # Retorna: {:error, "Invalid data"}


Monadex facilita el manejo de errores y valores opcionales en Elixir, siguiendo patrones funcionales.

Dejo link: https://github.com/rob-brown/MonadEx


lunes, 28 de octubre de 2024

Quicksort en Elixir


Un Algoritmo que me gusta mucho es el quicksort, porque es un algoritmo por demás claro. Ya he escrito lo fácil que es implementarlo en scalaerlangrusthaskell y lisp.

Ahora le toca a Elixir. Básicamente el algoritmo toma un pivot y agrupa los menores del pivot al principio y los mayores al final y aplica quicksort a estos 2 grupos. Y si la lista es vacia o tiene un elemento, ya esta ordenada. 

Vamos al código: 


defmodule QuickSort do

  def sort([]), do: []

  def sort([pivot | tail]) do

    lower = Enum.filter(tail, &(&1 <= pivot))

    higher = Enum.filter(tail, &(&1 > pivot))


    sort(lower) ++ [pivot] ++ sort(higher)

  end

end


jueves, 4 de mayo de 2023

Primeros pasos con Phoenix parte 9

 


Seguimos la idea del post anterior: Phoenix adopta el diseño de enchufe o plugs para funcionalidad componible. 

Por ejemplo los Endpoints estan organizados por plugs: 

defmodule HelloWeb.Endpoint do

  ...


  plug :introspect

  plug HelloWeb.Router


Los conectores Endpoint predeterminados hacen bastante trabajo. Aquí están en orden:

Plug.Static: sirve activos estáticos. Dado que este complemento viene antes que el registrador, las solicitudes de activos estáticos no se registran.

Phoenix.LiveDashboard.RequestLogger: configura el registrador de solicitudes para Phoenix LiveDashboard, esto le permitirá tener la opción de pasar un parámetro de consulta para transmitir registros de solicitudes o habilitar/deshabilitar una cookie que transmite registros de solicitudes desde su tablero.

Plug.RequestId: genera un ID de solicitud único para cada solicitud.

Plug.Telemetry: agrega puntos de instrumentación para que Phoenix pueda registrar la ruta de la solicitud, el código de estado y la hora de la solicitud de forma predeterminada.

Plug.Parsers: analiza el cuerpo de la solicitud cuando hay disponible un analizador conocido. De forma predeterminada, este complemento puede manejar contenido codificado en URL, de varias partes y JSON (con Jason). El cuerpo de la solicitud se deja intacto si el tipo de contenido de la solicitud no se puede analizar.

Plug.MethodOverride: convierte el método de solicitud en PUT, PATCH o DELETE para solicitudes POST con un parámetro _method válido.

Plug.Head: convierte las solicitudes HEAD en solicitudes GET y elimina el cuerpo de la respuesta

Plug.Session: un complemento que configura la gestión de sesiones. Tenga en cuenta que fetch_session/2 aún debe llamarse explícitamente antes de usar la sesión, ya que este complemento solo configura cómo se obtiene la sesión.

En el medio del endpoint, también hay un bloque condicional:


  if code_reloading? do

    socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket

    plug Phoenix.LiveReloader

    plug Phoenix.CodeReloader

    plug Phoenix.Ecto.CheckRepoStatus, otp_app: :hello

  end


Este bloque solo se ejecuta en desarrollo. Permite:

  • recarga en vivo: si cambia un archivo CSS, se actualizan en el navegador sin actualizar la página;
  • recarga de código, para que podamos ver los cambios en nuestra aplicación sin reiniciar el servidor;
  • verificar el estado del repositorio, lo que garantiza que nuestra base de datos esté actualizada y, de lo contrario, genera un error legible y procesable.


En el enrutador, podemos declarar enchufes dentro de tuberías:


defmodule HelloWeb.Router do

  use HelloWeb, :router

  pipeline :browser do

    plug :accepts, ["html"]

    plug :fetch_session

    plug :fetch_live_flash

    plug :put_root_layout, {HelloWeb.LayoutView, :root}

    plug :protect_from_forgery

    plug :put_secure_browser_headers

    plug HelloWeb.Plugs.Locale, "en"

  end

  scope "/", HelloWeb do

   pipe_through :browser

    get "/", PageController, :index

  end


Las rutas se definen dentro de los ámbitos y los ámbitos pueden canalizarse a través de múltiples canalizaciones. Una vez que una ruta coincide, Phoenix invoca todos los complementos definidos en todas las tuberías asociadas a esa ruta. Por ejemplo, acceder a "/" se canalizará a través de la canalización del navegador, invocando en consecuencia todos sus conectores.

Como veremos en la guía de enrutamiento, las tuberías en sí mismas son tapones. Allí, también discutiremos todos los complementos en la tubería :browser.

Finalmente, los controladores también son enchufes, por lo que podemos hacer:


defmodule HelloWeb.PageController do

  use HelloWeb, :controller

  plug HelloWeb.Plugs.Locale, "en"


En particular, los complementos del controlador brindan una función que nos permite ejecutar complementos solo dentro de ciertas acciones. Por ejemplo, puedes hacer:

defmodule HelloWeb.PageController do

  use HelloWeb, :controller

 plug HelloWeb.Plugs.Locale, "en" when action in [:index]


Y el complemento solo se ejecutará para la acción de índice.

Al cumplir con el contrato de conexión, convertimos una solicitud de aplicación en una serie de transformaciones explícitas. No se detiene ahí. Para ver realmente qué tan efectivo es el diseño de Plug, imaginemos un escenario en el que necesitamos verificar una serie de condiciones y luego redirigir o detenernos si una condición falla. Sin enchufe, terminaríamos con algo como esto:



defmodule HelloWeb.MessageController do

  use HelloWeb, :controller

  def show(conn, params) do

    case Authenticator.find_user(conn) do

      {:ok, user} ->

        case find_message(params["id"]) do

          nil ->

            conn |> put_flash(:info, "That message wasn't found") |> redirect(to: ~p"/")

          message ->

            if Authorizer.can_access?(user, message) do

              render(conn, :show, page: message)

            else

              conn |> put_flash(:info, "You can't access that page") |> redirect(to: ~p"/")

            end

        end

      :error ->

        conn |> put_flash(:info, "You must be logged in") |> redirect(to: ~p"/")


    end

  end

end


¿Observa cómo unos pocos pasos de autenticación y autorización requieren anidamiento y duplicación complicados? Mejoremos esto con un par de enchufes.


defmodule HelloWeb.MessageController do

  use HelloWeb, :controller

  plug :authenticate

  plug :fetch_message

  plug :authorize_message

  def show(conn, params) do

    render(conn, :show, page: conn.assigns[:message])

  end


  defp authenticate(conn, _) do

   case Authenticator.find_user(conn) do

      {:ok, user} ->

        assign(conn, :user, user)

      :error ->

        conn |> put_flash(:info, "You must be logged in") |> redirect(to: ~p"/") |> halt()

    end

  end

  defp fetch_message(conn, _) do

    case find_message(conn.params["id"]) do

      nil ->

        conn |> put_flash(:info, "That message wasn't found") |> redirect(to: ~p"/") |> halt()

      message ->

        assign(conn, :message, message)

    end

  end


  defp authorize_message(conn, _) do

    if Authorizer.can_access?(conn.assigns[:user], conn.assigns[:message]) do

      conn

    else

      conn |> put_flash(:info, "You can't access that page") |> redirect(to: ~p"/") |> halt()

    end

  end

end


Para que todo esto funcione, convertimos los bloques de código anidados y usamos halt(conn) cada vez que llegamos a una ruta de error. La funcionalidad halt(conn) es esencial aquí: le dice a Plug que no se debe invocar el siguiente plug.

Al final del día, al reemplazar los bloques de código anidados con una serie aplanada de transformaciones de complemento, podemos lograr la misma funcionalidad de una manera mucho más componible, clara y reutilizable.


lunes, 24 de abril de 2023

Primeros pasos con Phoenix parte 8


Plug vive en el corazón de la capa HTTP de Phoenix. Interactuamos con los enchufes o Plug en cada paso del ciclo de vida de una request, y los componentes principales de Phoenix, como end points, enrutadores y controladores, son solo enchufes internamente. 

Plug es una especificación para módulos componibles entre aplicaciones web. También es una capa de abstracción para adaptadores de conexión de diferentes servidores web. La idea básica de Plug es unificar el concepto de "conexión" sobre la que operamos. Esto difiere de otras capas de middleware HTTP como Rack, donde la solicitud y la respuesta están separadas en la pila de middleware.

En el nivel más simple, la especificación Plug viene en dos formas: complementos de funciones y complementos de módulos.

Para actuar como un enchufe, una función necesita: aceptar una estructura de conexión (%Plug.Conn{}) como primer argumento y opciones de conexión como segundo;

Cualquier función que cumpla con estos dos criterios servirá. Por ejemplo:


def introspect(conn, _opts) do

  IO.puts """

  Verb: #{inspect(conn.method)}

  Host: #{inspect(conn.host)}

  Headers: #{inspect(conn.req_headers)}

  """


  conn

end


Esta función hace lo siguiente:

  1. Recibe una conexión y opciones (que no usamos)
  2. Imprime alguna información de conexión en el terminal.
  3. Devuelve la conexión

Veamos esta función en acción agregándola a nuestro end point en lib/hello_web/endpoint.ex. Podemos conectarlo en cualquier lugar, así que hagámoslo insertando plug :introspect justo antes de delegar la solicitud al enrutador:

defmodule HelloWeb.Endpoint do

  ...


  plug :introspect

  plug HelloWeb.Router


  def introspect(conn, _opts) do

    IO.puts """

    Verb: #{inspect(conn.method)}

    Host: #{inspect(conn.host)}

    Headers: #{inspect(conn.req_headers)}

    """


    conn

  end

end


Los complementos de función se conectan pasando el nombre de la función como un átomo. Para probar esto, vamos al navegador y buscamos http://localhost:4000. Debería ver algo como esto impreso en su terminal de shell:


Verb: "GET"

Host: "localhost"

Headers: [...]


Nuestro enchufe simplemente imprime información de la conexión. Aunque nuestro complemento inicial es muy simple, puede hacer prácticamente cualquier cosa que desee dentro de él. 

Ahora veamos la otra variante de enchufe, los enchufes de módulo.

Los conectores de módulo son otro tipo de conector que nos permite definir una transformación de conexión en un módulo. El módulo solo necesita implementar dos funciones:

  • init/1 que inicializa cualquier argumento u opción que se pase a call/2
  • call/2 que realiza la transformación de la conexión. call/2 es solo un complemento de función como vimos anteriormente

Para ver esto en acción, escribamos un complemento de módulo que coloque la clave y el valor :locale en la conexión para uso posterior en otros complementos, acciones de controlador y nuestras vistas. Coloquemos el contenido a continuación en un archivo llamado lib/hello_web/plugs/locale.ex:


defmodule HelloWeb.Plugs.Locale do

  import Plug.Conn


  @locales ["en", "fr", "de"]


  def init(default), do: default


  def call(%Plug.Conn{params: %{"locale" => loc}} = conn, _default) when loc in @locales do

    assign(conn, :locale, loc)

  end


  def call(conn, default) do

    assign(conn, :locale, default)

  end

end


Para probarlo, agreguemos este complemento de módulo a nuestro enrutador, agregando el complemento HelloWeb.Plugs.Locale, "en" a nuestra tubería de navegación en lib/hello_web/router.ex:


defmodule HelloWeb.Router do

  use HelloWeb, :router


  pipeline :browser do

    plug :accepts, ["html"]

    plug :fetch_session

    plug :fetch_flash

    plug :protect_from_forgery

    plug :put_secure_browser_headers

    plug HelloWeb.Plugs.Locale, "en"

  end

  ...


En la devolución de llamada init/1, pasamos una configuración regional predeterminada para usar si no hay ninguna presente en los parámetros. También utilizamos la coincidencia de patrones para definir varios encabezados de función call/2 para validar la configuración regional en los parámetros, y recurrimos a "en" si no hay ninguna coincidencia. assign/3  es parte del módulo Plug.Conn y es cómo almacenamos valores en la estructura de datos de conn.

Para ver la asignación en acción, vayamos a la plantilla en lib/hello_web/components/layouts/home.html.heex y agreguamos el siguiente código después del cierre de la etiqueta </h1>:


<p>Locale: <%= @locale %></p>


Si vamos a http://localhost:4000/ y debería ver la configuración regional exhibida. Y si vamos a http://localhost:4000/?locale=fr y debería ver la asignación cambiada a "fr". Alguien puede usar esta información junto con Gettext para proporcionar una aplicación web totalmente internacionalizada.

Eso es todo lo que hay que hacer con Plug. Phoenix adopta el diseño de enchufe de las transformaciones componibles en toda la pila. 



jueves, 6 de abril de 2023

Primeros pasos con Phoenix parte 7

 


Agreguemos un poco de complejidad a nuestra aplicación. Vamos a agregar una nueva página que reconocerá una parte de la URL, la etiquetará como "mensajero" y la pasará a través del controlador a la plantilla para que nuestro mensajero pueda saludar.

Como hicimos la última vez, lo primero que haremos será crear una nueva ruta.

Vamos a reutilizar HelloController creado en el post anterior y agregaremos una nueva acción de mostrar. Agregaremos una línea justo debajo de nuestra última ruta, así:


scope "/", HelloWeb do

  pipe_through :browser

  get "/", PageController, :home

  get "/hello", HelloController, :index

  get "/hello/:messenger", HelloController, :show

end


Usamos la sintaxis :messenger en la ruta. Phoenix tomará cualquier valor que aparezca en esa posición en la URL y lo convertirá en un parámetro. Por ejemplo, si apuntamos el navegador a: http://localhost:4000/hello/Frank, el valor de "messenger" será "Frank".

Las solicitudes a nuestra nueva ruta serán manejadas por la acción show HelloWeb.HelloController. Ya tenemos el controlador en lib/hello_web/controllers/hello_controller.ex, así que todo lo que tenemos que hacer es editar ese controlador y agregarle una acción show. Esta vez, necesitaremos extraer el messenger de los parámetros para que podamos pasarlo a la plantilla. Para hacer eso, agregamos esta función show al controlador:

def show(conn, %{"messenger" => messenger}) do

  render(conn, :show, messenger: messenger)

end


Dentro del cuerpo de la acción show, también pasamos un tercer argumento a la función render, un par clave-valor donde :messenger es la clave, y la variable messenger se pasa como el valor.

Si el cuerpo de la acción necesita acceso al mapa completo de parámetros enlazados a la variable params, además de la variable messenger enlazada, podríamos definir show/2 así:


def show(conn, %{"messenger" => messenger} = params) do

  ...

end


Es bueno recordar que las claves del mapa de parámetros siempre serán cadenas, y que el signo igual no representa una asignación, sino una afirmación de coincidencia de patrón.

Ahora necesitaremos una nueva plantilla. Dado que es para la acción de mostrar de HelloController, irá al directorio lib/hello_web/controllers/hello_html y se llamará show.html.heex. Se parecerá sorprendentemente a nuestra plantilla index.html.heex, excepto que necesitaremos mostrar el nombre de nuestro mensajero.

Para hacer eso, usaremos las etiquetas HEEx especiales para ejecutar expresiones Elixir: <%= %>. Observe que la etiqueta inicial tiene un signo igual como este: <%= . Eso significa que se ejecutará cualquier código de Elixir que se encuentre entre esas etiquetas, y el valor resultante reemplazará la etiqueta en la salida HTML. Si faltara el signo igual, el código aún se ejecutaría, pero el valor no aparecería en la página.

Nuestras plantillas están escritas en HEEx (HTML+EEx). HEEx es un superconjunto de EEx, por lo que comparte la sintaxis <%= %>.


Y así es como debería verse la plantilla:


<section>

  <h2>Hello World, from <%= @messenger %>!</h2>

</section>


Nuestro messenger aparece como @messenger.

Los valores que pasamos a la vista desde el controlador se denominan colectivamente nuestras "asignaciones". Podríamos acceder a nuestro valor de mensajería a través de asignaciones.messenger pero a través de alguna metaprogramación, Phoenix nos brinda la sintaxis @ mucho más limpia para usar en plantillas.

Si vamos a http://localhost:4000/hello/emanuel, debería ver una página similar a esta:

Hello World, from emanuel!

lunes, 3 de abril de 2023

Primeros pasos con Phoenix parte 6


Hay un par de cosas interesantes para notar sobre el post anterior. No necesitábamos detener y reiniciar el servidor mientras realizábamos los cambios. ¡Sí, Phoenix tiene recarga de código caliente! Además, aunque nuestro archivo index.html.heex consta de una sola etiqueta de sección, la página que obtenemos es un documento HTML completo. La plantilla index en realidad se representa una pagina compuesta por el diseño globlal de lib/hello_web/components/layouts/root.html.heex y lib/hello_web/components/layouts/app.html.heex. Si abrimos este archivo, verá una línea que se ve así en la parte inferior:

<%= @inner_content %>

Que es donde se inyecta el html particular. 

Plug es una biblioteca y una especificación para unir aplicaciones web. Es una parte esencial de cómo Phoenix maneja las solicitudes. Cada conector define una parte del procesamiento de solicitudes. En el endpoint encontrarás un esqueleto más o menos así:


defmodule HelloWeb.Endpoint do

  use Phoenix.Endpoint, otp_app: :hello


  plug Plug.Static, ...

  plug Plug.RequestId

  plug Plug.Telemetry, ...

  plug Plug.Parsers, ...

  plug Plug.MethodOverride

  plug Plug.Head

  plug Plug.Session, ...

  plug HelloWeb.Router

end


Cada uno de estos plug tiene una responsabilidad específica. El último complemento es precisamente el módulo HelloWeb.Router. Esto permite que el endpoint delegue todo el procesamiento de solicitudes adicional al enrutador. Como sabemos ahora, su responsabilidad principal es mapear pares de verbo/ruta a controladores. Luego, el controlador le dice a una vista que represente una plantilla.

En este momento, puede estar pensando que pueden ser muchos pasos para simplemente renderizar una página. Sin embargo, a medida que nuestra aplicación crezca en complejidad, veremos que cada capa tiene un propósito distinto:

Endpoint (Phoenix.Endpoint): contiene la ruta común e inicial por la que pasan todas las solicitudes. Si desea que suceda algo en todas las solicitudes, se dirige al punto final.

Router (Phoenix.Router): es responsable de enviar verbos/ruta a los controladores. El enrutador también nos permite medir la funcionalidad. Por ejemplo, algunas páginas de su aplicación pueden requerir la autenticación del usuario, otras no.

Controlador (Phoenix.Controller): el trabajo del controlador es recuperar la información solicitada, hablar con su dominio comercial y preparar datos para la capa de presentación.

Vista: la vista maneja los datos estructurados del controlador y los convierte en una presentación para mostrar a los usuarios. Las vistas a menudo reciben el nombre del formato de contenido que representan.

Hagamos un resumen rápido y cómo los últimos tres componentes funcionan juntos agregando otra página.


jueves, 30 de marzo de 2023

Primeros pasos con Phoenix parte 5


Cuando su navegador accede a http://localhost:4000/, envía una solicitud HTTP a cualquier servicio que se esté ejecutando en esa dirección, en este caso, nuestra aplicación Phoenix. La solicitud HTTP se compone de un verbo y una ruta. Por ejemplo, las siguientes solicitudes del navegador se traducen en:

BROWSER ADDRESS             VERBO PATH

http://localhost:4000/                 GET /

http://localhost:4000/hello         GET /hello

http://localhost:4000/hello/world GET /hello/world


Hay otros verbos HTTP. Por ejemplo, enviar un formulario generalmente usa el verbo POST.

Las aplicaciones web generalmente manejan las solicitudes asignando cada par de verbo/ruta a una parte específica de su código y esto lo hace el router. Por ejemplo, podemos asignar "/artículos" a una parte de nuestra aplicación que muestra todos los artículos. Por lo tanto, para agregar esto, nuestra primera tarea es agregar una nueva ruta.

El router asigna pares únicos de verbo/ruta HTTP a pares de controlador/acción que los manejarán. Los controladores en Phoenix son simplemente módulos Elixir. Las acciones son funciones que se definen dentro de estos controladores.

Phoenix genera un archivo de router para nosotros en lib/hello_web/router.ex. 

La ruta de nuestro "¡Bienvenido a Phoenix!"  tiene este aspecto.


    get "/", PageController, :home


Si ponemos  en el browser http://localhost:4000/ estamos llamando a / con el metodo GET por lo tanto será manejada por la función home en el módulo HelloWeb.PageController definido en lib/hello_web/controllers/page_controller.ex.

La página que vamos a construir dirá "¡Hola mundo, desde Phoenix!" cuando apuntamos nuestro navegador a http://localhost:4000/hello.

Lo primero que debemos hacer es crear la ruta de la página para una nueva página. Abramos lib/hello_web/router.ex en un editor de texto. 

Agreguemos una nueva ruta al enrutador que asigna una solicitud GET para /hello a la acción de índice de un HelloWeb.HelloController que se creará próximamente dentro del bloque do scope "/" del enrutador:


scope "/", HelloWeb do
  pipe_through :browser

  get "/", PageController, :home
  get "/hello", HelloController, :index
end


Los controladores son módulos de Elixir y las acciones son funciones de Elixir definidas en ellos. El propósito de las acciones es recopilar los datos y realizar las tareas necesarias para la representación. Nuestra ruta especifica que necesitamos un módulo HelloWeb.HelloController con una función index/2.

Para hacer que suceda la acción de índice, creemos un nuevo archivo lib/hello_web/controllers/hello_controller.ex y hagamos que tenga el siguiente aspecto:

defmodule HelloWorldWeb.HelloController do
  use HelloWorldWeb, :controller

  def index(conn, _params) do
    render(conn, :index)
  end
end


Todas las acciones del controlador toman dos argumentos. El primero es conn, una estructura que contiene una gran cantidad de datos sobre la solicitud. El segundo es params, que son los parámetros de solicitud. .

El núcleo de esta acción es render(conn, :index). Le dice a Phoenix que renderice la plantilla de índice. Los módulos responsables de renderizar se denominan vistas. De forma predeterminada, las vistas de Phoenix llevan el nombre del controlador (HelloController) y el formato (HTML en este caso), por lo que Phoenix espera que exista un HelloWeb.HelloHTML y defina una función index/1.

Las vistas de Phoenix actúan como capa de presentación. Por ejemplo, esperamos que el resultado del índice de representación sea una página HTML completa. Para hacernos la vida más fácil, a menudo usamos plantillas para crear esas páginas HTML.

Vamos a crear una nueva vista. Cree lib/hello_web/controllers/hello_html.ex y haga que se vea así:

defmodule HelloWorldWeb.HelloHTML do
  use HelloWorldWeb, :html
end

Para agregar plantillas a esta vista, podemos definirlas como componentes de función en el módulo o en archivos separados.

Comencemos definiendo un componente de función:

defmodule HelloWorldWeb.HelloHTML do
  use HelloWorldWeb, :html

  def index(assigns) do
    ~H"""
    Hello!
    """
  end
end

Definimos una función que recibe asignaciones como argumentos y usamos el sigilo ~H para poner los contenidos que queremos representar. Dentro del sigilo ~H, usamos un lenguaje de plantillas llamado HEEx, que significa "HTML+EEx". EEx es una biblioteca para incrustar Elixir que se distribuye como parte del propio Elixir. "HTML+EEx" es una extensión Phoenix de EEx compatible con HTML, con soporte para validación HTML. Este último lo protege de vulnerabilidades de seguridad como Cross-Site-Scripting sin trabajo adicional de su parte.

Un archivo de plantilla funciona de la misma manera. Los componentes de función son excelentes para plantillas más pequeñas y los archivos separados son una buena opción cuando tiene mucho marcado o sus funciones comienzan a parecer inmanejables.

Probémoslo definiendo una plantilla en su propio archivo. Primero elimine nuestra función def index(assigns) de arriba y reemplácela con una declaración embed_templates:

defmodule HelloWorldWeb.HelloHTML do
  use HelloWorldWeb, :html

  embed_templates "hello_html/*"
end


Aquí le estamos diciendo a Phoenix.Component que incruste todas las plantillas .heex que se encuentran en el directorio hermano hello_html en nuestro módulo como definiciones de funciones.

A continuación, debemos agregar archivos al directorio lib/hello_web/controllers/hello_html.

Tenga en cuenta que el nombre del controlador (HelloController), el nombre de la vista (HelloHTML) y el directorio de la plantilla (hello_html) siguen la misma convención de nomenclatura y se nombran uno tras otro. También se colocan juntos en el árbol de directorios:


lib/hello_web
├── controllers
│   ├── hello_controller.ex
│   ├── hello_html.ex
│   ├── hello_html
 |         ├── index.html.heex

Un archivo de plantilla tiene la siguiente estructura: NOMBRE.FORMATO.TEMPLATING_LANGUAGE. En nuestro caso, creemos un archivo index.html.heex en lib/hello_web/controllers/hello_html/index.html.heex:

<section>
  <h2>Hello World, from Phoenix!</h2>
</section>

Los archivos de plantilla se compilan en el módulo como componentes de función en sí mismos, no hay diferencia de tiempo de ejecución o rendimiento entre los dos estilos.

Ahora que tenemos la ruta, el controlador, la vista y la plantilla, deberíamos poder dirigir nuestros navegadores a http://localhost:4000/hello.

Para reiniciar el server hacemos : 

mix phx.server

viernes, 24 de marzo de 2023

Primeros pasos con Phoenix parte 4


El directorio lib/hello_web contiene las partes relacionadas con la web de nuestra aplicación. Se ve así cuando se expande:

lib/hello_web

├── controllers

│   ├── page_controller.ex

│   ├── page_html.ex

│   ├── error_html.ex

│   ├── error_json.ex

│   └── page_html

│       └── home.html.heex

├── components

│   ├── core_components.ex

│   ├── layouts.ex

│   └── layouts

│       ├── app.html.heex

│       └── root.html.heex

├── endpoint.ex

├── gettext.ex

├── router.ex

└── telemetry.ex


Todos los archivos que se encuentran actualmente en los directorios de controladores y componentes están ahí para crear el mensaje "¡Bienvenido a Phoenix!" página que vimos en http://localhost:4000/

Al observar los directorios de controladores y componentes, podemos ver que Phoenix proporciona funciones para manejar diseños y HTML y páginas de error listas para usar.

Además de los directorios mencionados, lib/hello_web tiene cuatro archivos en su raíz. lib/hello_web/endpoint.ex es el punto de entrada para las solicitudes HTTP. Una vez que el navegador accede a http://localhost:4000, el punto final comienza a procesar los datos, lo que finalmente conduce al enrutador, que se define en lib/hello_web/router.ex. El enrutador define las reglas para enviar solicitudes a los "controladores", que llama a un módulo de vista para devolver las páginas HTML a los clientes. 

A través de Telemetry, Phoenix puede recopilar métricas y enviar eventos de monitoreo de su aplicación. El archivo lib/hello_web/telemetry.ex define al supervisor responsable de administrar los procesos de telemetría. 

Finalmente, hay un archivo lib/hello_web/gettext.ex que proporciona internacionalización a través de Gettext. Si no le preocupa la internacionalización, puede omitir con seguridad este archivo y su contenido.

El directorio de assert contiene archivos de origen relacionados con los activos front-end, como JavaScript y CSS. Desde Phoenix v1.6, usamos esbuild para compilar activos, que es administrado por el paquete esbuild Elixir. La integración con esbuild está integrada en su aplicación. La configuración relevante se puede encontrar en su archivo config/config.exs.

Sus otros activos estáticos se colocan en la carpeta priv/static, donde se guarda priv/static/assets para los activos generados. Todo en priv/static es atendido por el complemento Plug.Static configurado en lib/hello_web/endpoint.ex. Cuando se ejecuta en modo dev (MIX_ENV=dev), Phoenix observa cualquier cambio que realice en el directorio de activos y luego se encarga de actualizar su aplicación fronend en su navegador mientras trabaja.

Tenga en cuenta que cuando crea su aplicación Phoenix por primera vez usando mix phx.new, es posible especificar opciones que afectarán la presencia y el diseño del directorio assert. De hecho, las aplicaciones de Phoenix pueden traer sus propias herramientas de front-end o no tener un front-end en absoluto (útil si está escribiendo una API, por ejemplo). 

Si la integración predeterminada de esbuild no cubre sus necesidades, por ejemplo, porque desea utilizar otra herramienta de compilación, puede cambiar a una compilación de activos personalizados.

En cuanto a CSS, Phoenix incluye Tailwind CSS Framework, que proporciona una configuración básica para los proyectos. Puede pasar a cualquier marco CSS de su elección. 

sábado, 18 de marzo de 2023

Primeros pasos con Phoenix parte 3

Seguimos con phoenix. Cuando usamos mix phx.new para generar una nueva aplicación Phoenix, crea una estructura de directorio como esta:

├── _build

├── assets

├── config

├── deps

├── lib

│   ├── hello

│   ├── hello.ex

│   ├── hello_web

│   └── hello_web.ex

├── priv

└── test

Repasaremos esos directorios uno por uno:

  • _build: un directorio creado por la herramienta de línea de comando mix que se incluye como parte de Elixir y que contiene todos los artefactos de compilación. Como hemos visto en "Up and Running", mix es la interfaz principal de su aplicación. Usamos Mix para compilar nuestro código, crear bases de datos, ejecutar nuestro servidor y más. Este directorio no debe registrarse en el control de versiones y puede eliminarse en cualquier momento. Eliminarlo obligará a Mix a reconstruir su aplicación desde cero.
  • assets: un directorio que guarda el código fuente de sus activos front-end, normalmente JavaScript y CSS. La herramienta esbuild agrupa automáticamente estas fuentes. Los archivos estáticos como imágenes y fuentes van en priv/static.
  • config: un directorio que contiene la configuración del proyecto. El archivo config/config.exs es el punto de entrada para la configuración. Al final de config/config.exs, importa la configuración específica del entorno, que se puede encontrar en config/dev.exs, config/test.exs y config/prod.exs. Finalmente, se ejecuta config/runtime.exs.
  • deps - un directorio con todas nuestras dependencias Mix. Puede encontrar todas las dependencias enumeradas en el archivo mix.exs, dentro de la definición de la función defp deps do. Este directorio no debe registrarse en el control de versiones y puede eliminarse en cualquier momento. Eliminarlo obligará a Mix a descargar todas las versiones desde cero.
  • lib: un directorio que contiene el código fuente de su aplicación. Este directorio se divide en dos subdirectorios, lib/hello y lib/hello_web. El directorio lib/hello será responsable de alojar toda su lógica de negocio y dominio y por lo general, interactúa directamente con la base de datos. Este es el "Modelo" en la arquitectura Model-View-Controller (MVC). lib/hello_web se encarga de exponer el dominio de tu negocio al mundo, en este caso, a través de una aplicación web. Contiene tanto la vista como el controlador de MVC. 
  • priv: un directorio que guarda todos los recursos que son necesarios en producción pero que no forman parte directamente de su código fuente. Por lo general, guarda scripts de bases de datos, archivos de traducción, imágenes, etc. Los activos generados, creados a partir de archivos en el directorio de activos, se colocan en priv/static/assets de forma predeterminada.
  • test - un directorio con todas nuestras pruebas de aplicaciones. A menudo refleja la misma estructura que se encuentra en lib.


El directorio lib/hello alberga todo el dominio de su negocio. Dado que nuestro proyecto aún no tiene ninguna lógica de negocio, el directorio está casi vacío. Solo encontrarás tres archivos:


lib/hello

├── application.ex

├── mailer.ex

└── repo.ex


El archivo lib/hello/application.ex define una aplicación Elixir llamada Hello.Application. Esto se debe a que las aplicaciones de Phoenix son simplemente aplicaciones de Elixir. El módulo Hello.Application define qué servicios forman parte de nuestra aplicación:


children = [

  # Start the Telemetry supervisor

  HelloWeb.Telemetry,

  # Start the Ecto repository

  Hello.Repo,

  # Start the PubSub system

  {Phoenix.PubSub, name: Hello.PubSub},

  # Start the Endpoint (http/https)

  HelloWeb.Endpoint

  # Start a worker by calling: Hello.Worker.start_link(arg)

  # {Hello.Worker, arg}

]


Por ahora, baste decir que nuestra aplicación inicia un repositorio de base de datos, un sistema PubSub para compartir mensajes entre procesos y nodos, y el punto final de la aplicación, que atiende de manera efectiva las solicitudes HTTP. Estos servicios se inician en el orden en que se definen y, cada vez que cierra su aplicación, se detienen en el orden inverso.

El archivo lib/hello/mailer.ex contiene el módulo Hello.Mailer, que define la interfaz principal para enviar correos electrónicos:

defmodule Hello.Mailer do

  use Swoosh.Mailer, otp_app: :hello

end


En el mismo directorio lib/hello, encontraremos un lib/hello/repo.ex. Define un módulo Hello.Repo que es nuestra interfaz principal para la base de datos. Si está utilizando Postgres (la base de datos predeterminada), verá algo como esto:


defmodule Hello.Repo do

  use Ecto.Repo,

    otp_app: :hello,

    adapter: Ecto.Adapters.Postgres

end


Como nosotros usamos mysql vemos esto: 


defmodule HelloWorld.Repo do

  use Ecto.Repo,

    otp_app: :hello_world,

    adapter: Ecto.Adapters.MyXQL

end



jueves, 16 de marzo de 2023

Primeros pasos con Phoenix parte 2


Tema que en el post anterior cree un hola mundo pero con postgres y no tengo instalado postgres así que tiraba error por todos lados. 

Vamos hacer un hola mundo con mysql : 

 mix phx.new hello_world --module HelloWorld --database mysql

Y luego hacemos : 

 cd hello_world

Y para configurar la base usamos :

 mix ecto.create

A mi me tira este error : 

00:07:59.485 [error] GenServer #PID<0.297.0> terminating

** (MyXQL.Error) (1045) (ER_ACCESS_DENIED_ERROR) Access denied for user 'root'@'localhost' (using password: NO)

    (db_connection 2.4.3) lib/db_connection/connection.ex:100: DBConnection.Connection.connect/2

    (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5

    (stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3

Last message: nil

State: MyXQL.Connection


Porque mi base mysql tiene password y no existe la base que esta queriendo consultar. Por ende vamos a el archivo hello_world/config/dev.exs y vamos a configurar los datos : 

config :hello_world, HelloWorld.Repo,
  username: "root",
  password: "acaVaElPassword",
  hostname: "localhost",
  database: "hello_world_dev",
  stacktrace: true,
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

Como vimos la base se llama hello_world_dev y la creamos con el sql : 

CREATE SCHEMA `hello_world_dev` ;

O de forma visual con el workbeach. 

Ahora si ejecutamos : mix ecto.create obtendremos la siguiente salida: 

Compiling 15 files (.ex)

Generated hello_world app

The database for HelloWorld.Repo has already been created


Ahora podemos levantar el server sin problemas :

mix phx.server


Y si vamos a http://127.0.0.1:4000/ obtendremos la pagina de inicio :D 



jueves, 9 de marzo de 2023

Gleam, un lenguaje funcional que corre en la Vm de Erlang.


Gleam es un lenguaje funcional que corre sobre la maquina virtual de Erlang. Es de tipado estatico, funcional y sintaxis similar a Erlang y Elixir.   

Gleam se ejecuta en la máquina virtual Erlang que impulsa sistemas a escala planetaria como WhatsApp y Ericsson, está listo para cargas de trabajo de cualquier tamaño. Gracias a un sistema de simultaneidad basado en múltiples núcleos que puede ejecutar millones de tareas simultáneas, estructuras de datos rápidas e inmutables y un recolector de basura simultáneo, su servicio puede escalar y mantenerse con facilidad.

Gleam viene con compilador, herramienta de compilación, formateador, integraciones de editor y administrador de paquetes, todo integrado, por lo que crear un proyecto Gleam es simplemente ejecutar gleam new.

Como parte del ecosistema BEAM más amplio, los programas Gleam pueden usar miles de paquetes publicados, ya sea que estén escritos en Gleam, Erlang o Elixir.

Sin valores nulos, sin excepciones, mensajes de error claros y un sistema de tipo práctico. Ya sea que esté escribiendo código nuevo o manteniendo código antiguo, Gleam está diseñado para hacer que su trabajo sea lo más divertido y libre de estrés posible.

Gleam también puede compilar en JavaScript, lo que le permite usar su código en el navegador o en cualquier otro lugar donde se pueda ejecutar JavaScript. También genera definiciones de TypeScript, por lo que puede interactuar con su código Gleam con confianza, incluso desde el exterior.


Veamos un pequeño hola mundo hecho en Gleam:

import gleam/io


pub fn main() {

  io.println("hello, friend!")

}


Por ejemplo veamos código asincrono: 


fn spawn_task(i) {

  task.async(fn() {

    let n = int.to_string(i)

    io.println("Hello from " <> n)

  })

}


pub fn main() {

  // Run a million threads, no problem

    list.range(0, 1_000_000)

  |> list.map(spawn_task)

  |> list.each(task.await_forever)

}

Dejo link: https://gleam.run/

domingo, 5 de marzo de 2023

Primeros pasos con Phoenix


Antes de empezar de manera muy rápida aclaremos que Phoenix es un framework web o para hacer API en Elixir. 

Phoenix está escrito en Elixir, y nuestro código de aplicación también estará escrito en Elixir. Por ende el primer paso es instalar Elixir , en mi caso lo voy a instalar con asdf

asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git

asdf install elixir 1.14.3-otp-25

El código de Elixir se compila en el código de bytes de Erlang para ejecutarse en la máquina virtual de Erlang. Sin Erlang, el código de Elixir no tiene una máquina virtual para ejecutarse, por lo que también debemos instalar Erlang. 

asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git

asdf install erlang 25.0.3

Y ahora vamos a setear las versiones: 

asdf global erlang 25.0.3

asdf global elixir 1.14.3-otp-25

Pero pueden ver como instalar en su equipo en el siguiente link : https://elixir-lang.org/install.html

Cuando instalamos Elixir siguiendo las instrucciones de la página de instalación de Elixir, normalmente también obtendremos Erlang. Para checkear esto hacemos : 

emanuel@crespo:~$ elixir --version

Erlang/OTP 25 [erts-13.1.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]


Elixir 1.14.3 (compiled with Erlang/OTP 25)


También podemos escribir erl que es el comando para la relp de Erlang. 

Si acabamos de instalar Elixir por primera vez, también necesitaremos instalar el administrador de paquetes Hex. Hex es necesario para ejecutar una aplicación de Phoenix (mediante la instalación de dependencias) y para instalar cualquier dependencia adicional que podamos necesitar en el camino. En mi caso es : 

mix local.hex


Ahora debemos instalar phoenix, en mi caso voy a instalar la ultima versión

mix archive.install hex phx_new 

Por lo visto, ya esta todo instalado, ahora vamos a crear nuestro primer proyecto: 

mix phx.new hello_world --module HelloWorld

Y si todo salio bien vamos a ejecutarlo: 

cd hello_world/
mix phx.server

Si vamos a http://localhost:4000/ nos aparecerá una pagina. 

Van a ver que tira como 5000 errores porque no configuramos la base de datos, pero eso lo vamos hacer en el proximo post. 

Dejo link; https://www.phoenixframework.org/

martes, 3 de noviembre de 2020

Elixir School


Elixir School es el primer destino para personas que buscan aprender y dominar el lenguaje de programación Elixir

Ya seas un veterano experimentado o esta es tu primera vez, vas a encontrar lo que necesitas en las lecciones y en los recursos auxiliares

Mediante el trabajo duro de voluntarios Elixir School ha sido traducido a muchos idiomas. Algunas de estas traducciones incluyen: Việt ngữ, 简体中文, 繁體中文, English, Slovenčina, 日本語, Polski, Português, Русском, Bahasa Indonesia, Bahasa Melayu, Українською, 한국어, Italiano, Deutsch, বাংলা, Türkçe, y ภาษาไทย.

Te damos la bienvenida y te motivamos a continuar haciendo Elixir School grandioso involucrándote en elixirschool/elixirschool!

De esta manera se presenta Elixir school que es un excelente sitio para comenzar con Elixir. 

Y sin más dejo link: https://elixirschool.com/es/