Translate

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



Libros gratuitos

 

Download FREE IT Guides!

 

Linux Bible, 10th Edition ($36.00 Value) FREE for a Limited Time

Linux Bible, 10th Edition is the ultimate hands-on Linux user guide, whether you're a true beginner or a more advanced user navigating recent changes. this updated tenth edition covers the latest versions of Red Hat Enterprise Linux

 
 

Teach Yourself VISUALLY Windows 11 ($19.00 Value) FREE for a Limited Time

Teach Yourself VISUALLY Windows 11 collects all the resources you need to master the day-to-day use of Microsoft’s new operating system and delivers them in a single resource. Fully illustrated, step-by-step

 
 

RESTful Architecture Cheatsheet

The web has become an integral part of our daily lives, and with it, web applications have also become increasingly popular. However, as the number of web applications has grown, so has the complexity of managing them

 
 

Modern API Development with Spring and Spring Boot ($33.99 Value) FREE for a Limited Time

Developers need to know how to adapt to these modern API design principles. Apps are now developed with APIs that enable ease of integration for the cloud environment and distributed systems.

 

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 



miércoles, 15 de marzo de 2023

Libro Gratuito: Programación avanzada de Linux


Están regalando este libro y bueno simplemente me estoy haciendo eco de la noticia. 

Este libro es especial para la gente que sabe C y quiere programar para linux. 

Sin más, dejo el link: https://blog.educalix.com/wp-content/uploads/2023/03/advanced-linux-programming.pdf

lunes, 13 de marzo de 2023

Vamos a implementar un framework de procesamiento paralelo simple con Cats parte 2

Dado que en el post anterios refrescamos nuestra memoria de Futures, veamos cómo podemos dividir el trabajo en lotes. Podemos consultar la cantidad de CPU disponibles en nuestra máquina mediante una llamada API desde la biblioteca estándar de Java:

Runtime.getRuntime.availableProcessors

// res11: Int = 2

Podemos particionar una secuencia (en realidad cualquier cosa que implemente Vector) usando el método agrupado. Usaremos esto para dividir lotes de trabajo para cada CPU:


(1 to 10).toList.grouped(3).toList

// res12: List[List[Int]] = List(

//List(1, 2, 3),

//List(4, 5, 6),

//List(7, 8, 9),

//List(10)

// )


Implemente una versión paralela de foldMap llamada parallelFoldMap:


def parallelFoldMap[A, B: Monoid] (values: Vector[A]) (func: A => B): Future[B] = {

    // Calculate the number of items to pass to each CPU:

    val numCores = Runtime.getRuntime.availableProcessors

    val groupSize = (1.0 * values.size / numCores).ceil.toInt

    // Create one group for each CPU:

    val groups: Iterator[Vector[A]] = values.grouped(groupSize)

    // Create a future to foldMap each group:

    val futures: Iterator[Future[B]] =

        groups map { group =>

            Future {

                group.foldLeft(Monoid[B].empty)(_ |+| func(_))

            }

    }


    // foldMap over the groups to calculate a final result:

    Future.sequence(futures) map { iterable =>

    iterable.foldLeft(Monoid[B].empty)(_ |+| _)

    }

}

val result: Future[Int] = parallelFoldMap((1 to 1000000).toVector)(identity)

Await.result(result, 1.second)

// res14: Int = 1784293664


Aunque implementamos foldMap nosotros mismos arriba, el método también está disponible como parte de la clase de tipo Foldable:


import cats.Monoid

import cats.instances.int._

// for Monoid

import cats.instances.future._ // for Applicative and Monad

import cats.instances.vector._ // for Foldable and Traverse

import cats.syntax.foldable._// for combineAll and foldMap

import cats.syntax.traverse._// for traverse

import scala.concurrent._

import scala.concurrent.duration._

import scala.concurrent.ExecutionContext.Implicits.global


def parallelFoldMap[A, B: Monoid](values: Vector[A]) (func: A => B): Future[B] = {

    val numCores = Runtime.getRuntime.availableProcessors

    val groupSize = (1.0 * values.size / numCores).ceil.toInt

    values.grouped(groupSize).toVector.traverse(group => Future(group.toVector.foldMap(func)))

        .map(_.combineAll)

}

val future: Future[Int] = parallelFoldMap((1 to 1000).toVector)(_ * 1000)

Await.result(future, 1.second)

// res18: Int = 500500000

Vamos a implementar un framework de procesamiento paralelo simple con Cats

Vamos a implementar un framework de procesamiento paralelo simple pero poderoso utilizando Monoids, Functors y una serie de otras cosas.

Si ha utilizado Hadoop o Spark o ha trabajado en "grandes datos", habrá oído hablar de MapReduce, que es un modelo de programación para realizar el procesamiento de datos en paralelo en grupos de máquinas (también conocidos como "nodos"). Como sugiere el nombre, el modelo se construye alrededor de una fase de map, que es la misma función de map que conocemos de Scala y la clase de tipo Functor, y una fase de reducción, que generalmente llamamos plegar o fold en Scala.

Recuerde que la firma general para el mapa es aplicar una función A => B a una F[A], devolviendo una F[B]

map transforma cada elemento individual en una secuencia de forma independiente. Podemos paralelizar fácilmente porque no hay dependencias entre las transformaciones aplicadas a diferentes elementos (la firma de tipo de la función A => B).

Nuestro paso de reducción se convierte en un pliegue a la izquierda sobre los resultados del map distribuido.

Al distribuir el paso de reducción, perdemos el control sobre el orden de recorrido. Es posible que nuestra reducción general no sea completamente de izquierda a derecha: podemos reducir de izquierda a derecha en varias subsecuencias y luego combinar los resultados. Para asegurar la corrección necesitamos una operación de reducción que sea asociativa:

reduce(a1, reduce(a2, a3)) == reduce(reduce(a1, a2), a3)

Si tenemos asociatividad, podemos distribuir arbitrariamente el trabajo entre nuestros nodos siempre que las subsecuencias en cada nodo permanezcan en el mismo orden que el conjunto de datos inicial.

Nuestra operación de plegado nos obliga a sembrar el cálculo con un elemento de tipo B. Dado que el plegado se puede dividir en un número arbitrario de pasos paralelos, la semilla no debería afectar el resultado del cálculo. Esto naturalmente requiere que la semilla sea un elemento de identidad:

reduce(seed, a1) == reduce(a1, seed) == a1

En resumen, nuestro pliegue paralelo dará los resultados correctos si:

• requerimos que la función reductora sea asociativa;

• Sembramos el cálculo con la identidad de esta función.

¿Cómo suena este patrón? Así es, hemos completado el círculo de regreso a Monoid. El patrón de diseño monoide para trabajos de reducción de mapas está en el centro de los sistemas de big data recientes, como Summingbird de Twitter.

En este proyecto vamos a implementar un map-reduce de una sola máquina muy simple. Comenzaremos implementando un método llamado foldMap para modelar el flujo de datos que necesitamos.

Vimos foldMap es una de las operaciones derivadas que se encuentra encima de foldLeft y foldRight. Sin embargo, en lugar de usar Foldable, volveremos a implementar foldMap aquí nosotros mismos, ya que proporcionará información útil sobre la estructura de map‐reduce. Comience escribiendo la firma de foldMap. Debe aceptar los siguientes parámetros:

• una secuencia de tipo Vector[A];

• una función de tipo A => B, donde hay un Monoide para B;

Deberá agregar parámetros implícitos o límites de contexto para completar la firma de tipo.

import cats.Monoid

/** Single-threaded map-reduce function.

* Maps `func` over `values` and reduces using a `Monoid[B]`.

*/

def foldMap[A, B: Monoid](values: Vector[A])(func: A => B): B =

???

Ahora implemente el cuerpo de foldMap:

  1. comenzar con una secuencia de elementos de tipo A;
  2. mapear sobre la lista para producir una secuencia de elementos de tipo B;
  3. usa el Monoide para reducir los elementos a una sola B.

Aquí hay algunos resultados de muestra para referencia:


import cats.instances.int._ // for Monoid

foldMap(Vector(1, 2, 3))(identity)

// res1: Int = 6

import cats.instances.string._ // for Monoid

// Mapping to a String uses the concatenation monoid:

foldMap(Vector(1, 2, 3))(_.toString + "! ")

// res2: String = "1! 2! 3! "

// Mapping over a String to produce a String:

foldMap("Hello world!".toVector)(_.toString.toUpperCase)

// res3: String = "HELLO WORLD!"


Veamos la implementación: 


import cats.Monoid

import cats.syntax.semigroup._ // for |+|

def foldMap[A, B : Monoid](as: Vector[A])(func: A => B): B =

as.map(func).foldLeft(Monoid[B].empty)(_ |+| _)


Podemos hacer una pequeña alteración a este código para hacer todo en un solo paso:


def foldMap[A, B : Monoid](as: Vector[A])(func: A => B): B =

as.foldLeft(Monoid[B].empty)(_ |+| func(_))


Ahora que tenemos una implementación funcional de subproceso único de foldMap, veamos cómo distribuir el trabajo para que se ejecute en paralelo. Usaremos nuestra versión de subproceso único de foldMap como bloque de construcción. Escribiremos una implementación de CPU múltiple que simule la forma en que distribuiríamos el trabajo en un clúster de reducción de mapa:

  1. comenzamos con una lista inicial de todos los datos que necesitamos procesar;
  2. dividimos los datos en lotes, enviando un lote a cada CPU;
  3. las CPU ejecutan una fase de mapa a nivel de lote en paralelo;
  4. Las CPU ejecutan una fase de reducción de nivel de lote en paralelo, produciendo un local resultado de cada lote;
  5. reducimos los resultados de cada lote a un único resultado final.

Scala proporciona algunas herramientas simples para distribuir el trabajo entre subprocesos. Podríamos usar la biblioteca de colecciones paralelas para implementar una solución, pero desafiémonos a nosotros mismos profundizando un poco más e implementando el algoritmo nosotros mismos usando Futures.

Ya sabemos bastante sobre la naturaleza monádica de Futures. Tomemos un momento para un resumen rápido y para describir cómo se programan los futuros de Scala detrás de escena.

Los futuros se ejecutan en un grupo de subprocesos, determinado por un parámetro ExecutionContext implícito.

Cada vez que creamos un futuro, ya sea a través de una llamada a Future.apply o algún otro combinador, debemos tener un ExecutionContext implícito en el alcance:

import scala.concurrent.Future

import scala.concurrent.ExecutionContext.Implicits.global

val future1 = Future {

     (1 to 100).toList.foldLeft(0)(_ + _)

}

// future1: Future[Int] = Future(Success(5050))

val future2 = Future {

    (100 to 200).toList.foldLeft(0)(_ + _)

}

// future2: Future[Int] = Future(Success(15150))


En este ejemplo, hemos importado un ExecutionContext.Implicits.global. Este contexto predeterminado asigna un grupo de subprocesos con un subproceso por CPU en nuestra máquina. Cuando creamos un futuro, ExecutionContext lo programa para su ejecución. Si hay un subproceso libre en el grupo, Future comienza a ejecutarse de inmediato. La mayoría de las máquinas modernas tienen al menos dos CPU, por lo que en nuestro ejemplo es probable que future1 y future2 se ejecuten en paralelo.

Algunos combinadores crean nuevos Futuros que programan el trabajo en función de los resultados de otros Futuros. Los métodos map y flatMap, por ejemplo, programan cálculos que se ejecutan tan pronto como se calculan sus valores de entrada y hay una CPU disponible:

val future3 = future1.map(_.toString)

// future3: Future[String] = Future(Success(5050))

val future4 = for {

    a <- future1

    b <- future2

} yield a + b

// future4: Future[Int] = Future(Success(20200))


Podemos convertir una List[Future[A]] en una Future[List[A]] usando Future.sequence:


Future.sequence(List(Future(1), Future(2), Future(3)))

// res6: Future[List[Int]] = Future(Success(List(1, 2, 3)))


Podemos convertir una List[Future[A]] en una Future[List[A]] usando Future.sequence:


import cats.instances.future._ // for Applicative

import cats.instances.list._// for Traverse

import cats.syntax.traverse._// for sequence

List(Future(1), Future(2), Future(3)).sequence

// res7: Future[List[Int]] = Future(Success(List(1, 2, 3)))


Se requiere un ExecutionContext en cualquier caso. Finalmente, podemos usar Await.result para bloquear un futuro hasta que haya un resultado disponible:


import scala.concurrent._

import scala.concurrent.duration._

Await.result(Future(1), 1.second) // wait for the result

// res8: Int = 1


También hay implementaciones de Monad y Monoid para Future disponibles en cats.instances.future:


import cats.{Monad, Monoid}

import cats.instances.int._

// for Monoid

import cats.instances.future._ // for Monad and Monoid

Monad[Future].pure(42)

Monoid[Future[Int]].combine(Future(1), Future(2))


Me quedo medio largo el post, así que vamos a seguir en el proximo post. 



viernes, 10 de marzo de 2023

Recursos sobre scala


 Queres empezar en scala y no sabes de donde sacar info? 

Te paso una lista de recursos: 

Platforms

Community

Coding Tools:

Programming Environments

Build Tools

Code Formatting / Linting

Free Books, Tutorials and Guides:

Non-free Books:

Advanced!:

Free Scala Courses: * Functional Programming Principles in Scala
Functional Program Design in Scala
Parallel Programming * Big Data Analysis with Scala and Spark
Introduction to Programming with Dependent Types in Scala (advanced)

Non-Free Courses:

Scala Conferences: * Functional Scala (UK/Remote)
LambdaConf (USA) * Typelevel Summits (Misc.) * Scala by the Bay (USA) * flatMap (Norway) * Scala Up North (Canada) * Scala Days (USA, Europe)
Scala World (UK)
Scala Swarm (Portugal)
Scala.io (France)
Scalar (Central Europe) * Scala Sphere (Poland)
nescala (USA)
LX SCALA (South-West Europe)
ScalaConf (Russia)

Podcasts:

Scala Jobs:

Scala Libraries:

Web Development and Microservices
ZIO HTTP * Caliban (https://github.com/ghostdogpr/caliban) * Play
Akka HTTP
Lagom * Sttp (HTTP Client) * http4s * Finch * Udash - Frontend and Backend
Lift
Scalatra
Skinny
Vert.x
Sangria - GraphQL

Web Front End (Scala.js)

Database Access

Functional Programming

Concurrency / Parallelism

Mathematics

Distributed Computing

Blockchain

Miscellaneous:

Open Source Applications written in Scala

Related Communities:

Blogs/Periodicals: