Translate

lunes, 27 de febrero de 2023

Un Ejemplo de polimorfismo en golang

 


Si seguís el blog tenes que saber más o menos que es el polimorfismo. En golang no tenemos clases pero tenemos interfaces, por lo tanto podemos utilizar el polimorfismo. 


Supongamos que tenemos que imprimir diferentes cosas documentos de texto, planillas, etc. Y queremos que una impresora pueda imprimir cualquier documento. Por lo tanto la impresora va a tomar un objeto imprimible y lo imprimira. 

Entonces vamos a hacer la interfaz imprimible. 


type Imprimible interface {

to_string() string

}


Y ahora vamos a hacer estructuras que implementen la interfaz : 


type Doc struct {

texto string

}


type Datos struct {

dato1 string

dato2 string

}


func (doc Doc) to_string() string {

return doc.texto

}


func (datos Datos) to_string() string {

return datos.dato1 + " " + datos.dato2

}


Y ahora vamos a hacer la función imprimir que para que funcione el ejemplo va a imprimir por pantalla: 


func imprimir(imprimible Imprimible) {

fmt.Println(imprimible.to_string())

}


Y por ultimo vamos a utilizar esta función: 



func main() {

var doc = Doc{texto: "Hola!! Este es Doc"}

var datos = Datos{dato1: "dato1", dato2: "dato2"}

imprimir(doc)

imprimir(datos)

}


Y listo!!! 

Inyección de dependencia en Go usando Fx


Vamos por parte, que es la inyección de dependencias?

Cualquier aplicación no trivial está formada por dos o más objetos que colaboran entre sí para realizar alguna lógica. Tradicionalmente cada objeto se hacía cargo de obtener sus propias referencias a los objetos a los cuales colaboraba (sus dependencias). Esto lleva a código acoplado y difícil de probar. Y para solucionar esto esta la inyección de dependencias. El contexto ingresa las dependencias de un objeto. Para mayor información pueden ir a : https://emanuelpeg.blogspot.com/2009/07/inyeccion-de-dependencias.html


Y que es Fx? Según la documentación oficial publicada por Uber, Fx es un framework para Go que:

  • Facilita la inyección de dependencia.
  • Elimina la necesidad de estado global y func init().

Fx usa el patrón de inyección de constructor, tratemos de entender exactamente cómo hace que la inyección de dependencia sea fácil en Go.

Para usar Fx debemos: 

El primer paso es instalar la biblioteca a través de go get go.uber.org/fx.

Y luego podemos hacer esto: 

package main


import (

"context"

"go.uber.org/fx"

"go.uber.org/fx/fxevent"

"log"

"net"

"net/http"

"os"

"time"

)


func NewLogger() *log.Logger {

logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)

logger.Print("Create NewLogger.")

return logger

}


func NewHandler(logger *log.Logger) (http.Handler, error) {

logger.Print("Create NewHandler.")

return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {

logger.Print("Got a request.")

}), nil

}


func NewMux(lc fx.Lifecycle, logger *log.Logger) *http.ServeMux {

logger.Print("Create NewMux.")

mux := http.NewServeMux()

server := &http.Server{

Addr:    "127.0.0.1:8080",

Handler: mux,

}

lc.Append(fx.Hook{

OnStart: func(context.Context) error {

logger.Print("Starting HTTP server.")

ln, err := net.Listen("tcp", server.Addr)

if err != nil {

return err

}

go server.Serve(ln)

return nil

},

OnStop: func(ctx context.Context) error {

logger.Print("Stopping HTTP server.")

return server.Shutdown(ctx)

},

})


return mux

}


func Register(mux *http.ServeMux, h http.Handler) {

mux.Handle("/", h)

}


func main() {

app := fx.New(


fx.Provide(

NewLogger,

NewHandler,

NewMux,

),


fx.Invoke(Register),


fx.WithLogger(

func() fxevent.Logger {

return fxevent.NopLogger

},

),

)


startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)

defer cancel()

if err := app.Start(startCtx); err != nil {

log.Fatal(err)

}


if _, err := http.Get("http://localhost:8080/"); err != nil {

log.Fatal(err)

}


stopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)

defer cancel()

if err := app.Stop(stopCtx); err != nil {

log.Fatal(err)

}


}


En el ejemplo queremos costruir varias entidades, un *log.Logger,  un http.Handler, un servidor *http.ServeMux y queremos llamar a una request. Y fx, nos construye estos objetos. 

Si ejecutamos esta aplicación el resultado será : 

Create NewLogger.
Create NewMux.
Create NewHandler.
Starting HTTP server.
Got a request.
Stopping HTTP server.


Dejo link: 


viernes, 24 de febrero de 2023

Programación poliglota con GraalVM parte 2


Seguimos con GraalVM 

Veamos un ejemplo queremos ejecutar una aplicación javascript o node que dentro llama a codigo ruby, por ejemplo. 

Vamos a tener que crear un archivo .js que lo llamaré ejemplo.js que contenga lo siguiente: 


var array = Polyglot.eval("ruby", "[1,2,42,4]")

console.log(array[2]);


Y lo ejecutamos con : 


js --polyglot --jvm ejemplo.js

42

node --polyglot --jvm ejemplo.js

42


Así de fácil o queremos ejecutar en una aplicación R, javascript :

array <- eval.polyglot("js", "[1,2,42,4]")
print(array[3L])

Y lo corremos con : 

Rscript --polyglot --jvm ejemplo.R
[1] 42

Y puedo seguir con ejemplos pero creo que se entiende :D


Dejo link : https://www.graalvm.org/reference-manual/polyglot-programming/#running-polyglot-applications

martes, 21 de febrero de 2023

Probar código asíncrono con cats

Cómo simplificar las pruebas unitarias para código asincrónico haciéndolas sincrónicas con Cats? Esta es la pregunta que vamos a intantar responder. 

Supongamos que estamos midiendo el tiempo de actividad en un conjunto de servidores.  Habrá dos componentes. El primero es un UptimeClient que sondea servidores remotos para conocer su tiempo de actividad:


import scala.concurrent.Future

trait UptimeClient {

    def getUptime(hostname: String): Future[Int]

}


También tendremos un UptimeService que mantiene una lista de servidores y permite al usuario sondearlos por su tiempo de actividad total:


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

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

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

import scala.concurrent.ExecutionContext.Implicits.global


class UptimeService(client: UptimeClient) {

    def getTotalUptime(hostnames: List[String]): Future[Int] =

        hostnames.traverse(client.getUptime).map(_.sum)

}

Hemos modelado UptimeClient como un trait porque vamos a querer probarlo en pruebas unitarias. Por ejemplo, podemos escribir un cliente de prueba que nos permita proporcionar datos ficticios en lugar de llamar a servidores reales:


class TestUptimeClient(hosts: Map[String, Int]) extends UptimeClient {

    def getUptime(hostname: String): Future[Int] =

        Future.successful(hosts.getOrElse(hostname, 0))

}


Ahora, supongamos que estamos escribiendo pruebas unitarias para UptimeService. Queremos probar su capacidad para sumar valores, independientemente de dónde los obtenga. Aquí hay un ejemplo:


def testTotalUptime() = {

   val hosts= Map("host1" -> 10, "host2" -> 6)

   val client= new TestUptimeClient(hosts)

   val service= new UptimeService(client)

   val actual= service.getTotalUptime(hosts.keys.toList)

   val expected = hosts.values.sum

   assert(actual == expected)

}


El código no compila porque hemos cometido un error clásico. Olvidamos que nuestro código de aplicación es asíncrono. Nuestro resultado real es de tipo Future[Int] y nuestro resultado esperado es de tipo Int. ¡No podemos compararlos directamente!

Hay un par de maneras de resolver este problema. Podríamos modificar nuestro código de prueba para acomodar la asincronía. Sin embargo, existe otra alternativa. ¡Hagamos que nuestro código de servicio sea sincrónico para que nuestra prueba funcione sin modificaciones!

Necesitamos implementar dos versiones de UptimeClient: una asíncrona para usar en producción y una síncrona para usar en nuestras pruebas unitarias:


trait RealUptimeClient extends UptimeClient {

    def getUptime(hostname: String): Future[Int]

}

trait TestUptimeClient extends UptimeClient {

    def getUptime(hostname: String): Int

}


La pregunta es: ¿qué tipo de resultado debemos dar al método abstracto en UptimeClient? Future[Int] o Int:


trait UptimeClient {

    def getUptime(hostname: String): ???

}


Al principio esto puede parecer difícil. Pero afortunadamente, Cats brinda una solución en términos del tipo de identidad, Id. Id nos permite "envolver" tipos en un constructor de tipos sin cambiar su significado:


package cats

type Id[A] = A


Id nos permite abstraernos sobre los tipos de devolución en UptimeClient. Implementa esto ahora:


import cats.Id

trait UptimeClient[F[_]] {

    def getUptime(hostname: String): F[Int]

}

trait RealUptimeClient extends UptimeClient[Future] {

   def getUptime(hostname: String): Future[Int]

}

trait TestUptimeClient extends UptimeClient[Id] {

   def getUptime(hostname: String): Id[Int]

}


Ahora deberíamos poder desarrollar una definición de TestUptimeClient en una clase completa basada en Map[String, Int].


Dirijamos nuestra atención a UptimeService. Necesitamos reescribirlo para abstraer los dos tipos de UptimeClient:

class UptimeService[F[_]](client: UptimeClient[F]) {

    def getTotalUptime(hostnames: List[String]): F[Int] =

        hostnames.traverse(client.getUptime).map(_.sum)

}

Ahora descomente el cuerpo de getTotalUptime. Debería obtener un error de compilación similar al siguiente:


// <console>:28: error: could not find implicit value for

//evidence parameter of type cats.Applicative[F]

//hostnames.traverse(client.getUptime).map(_.sum)

//                              ^


El problema aquí es que traverse solo funciona en secuencias de valores que tienen un Aplicativo. En nuestro código original estábamos atravesando una Lista[Futuro[Int]]. Hay un aplicativo para Future, así que estuvo bien.

En esta versión estamos atravesando una Lista[F[Int]]. Necesitamos demostrarle al compilador que F tiene un Aplicativo. Haga esto agregando un parámetro de constructor implícito a UptimeService.


import cats.Applicative

import cats.syntax.functor._ // for map


class UptimeService[F[_]](client: UptimeClient[F]) (implicit a: Applicative[F]) {

    def getTotalUptime(hostnames: List[String]): F[Int] =

        hostnames.traverse(client.getUptime).map(_.sum)

}


Finalmente, dirijamos nuestra atención a nuestras pruebas unitarias. Nuestro código de prueba ahora funciona según lo previsto sin ninguna modificación. Creamos una instancia de TestUptimeClient y la envolvemos en un UptimeService. Esto une efectivamente F a Id, lo que permite que el resto del código funcione sincrónicamente sin preocuparse por las mónadas o los aplicativos:


def testTotalUptime() = {

    val hosts= Map("host1" -> 10, "host2" -> 6)

    val client= new TestUptimeClient(hosts)

    val service= new UptimeService(client)

    val actual= service.getTotalUptime(hosts.keys.toList)

    val expected = hosts.values.sum

    assert(actual == expected)

}

testTotalUptime()


Este estudio de caso proporciona un ejemplo de cómo Cats puede ayudarnos a abstraernos en diferentes escenarios computacionales. Usamos la clase de tipo Applicative para abstraer sobre código asíncrono y síncrono. Apoyarnos en una abstracción funcional nos permite especificar la secuencia de cálculos que queremos realizar sin preocuparnos por los detalles de la implementación.

Las clases de tipo como Functor, Applicative, Monad y Traverse proporcionan implementaciones abstractas de patrones como mapeo, compresión, secuenciación e iteración. Las leyes matemáticas de esos tipos aseguran que funcionen junto con un conjunto consistente de semántica.

Usamos Applicative en este caso de estudio porque era la clase de tipos menos poderosa que hacía lo que necesitábamos. Si hubiéramos requerido flatMap, podríamos haber cambiado Applicative por Monad. Si hubiéramos necesitado abstraernos sobre diferentes tipos de secuencias, podríamos haber usado Traverse. También hay clases de tipos como ApplicativeError y MonadError que ayudan a modelar fallas y cálculos exitosos.

lunes, 20 de febrero de 2023

Tutoriales de kubernetes


Este post es una pavada, pero bueno quiero compartirlo igual. Encontré un conjunto de tutoriales de kubernete en español en la pagina de kubernetes. No es una super novedad pero me sirvieron mucho. 

Dejo link : https://kubernetes.io/es/docs/tutorials/

jueves, 16 de febrero de 2023

¿Cuál es la diferencia entre "is not null" y "!= null" en C# ?



La principal diferencia entre e != null y is not null es la forma en que el compilador ejecuta la comparación.

El compilador garantiza que no se invoque ningún operador de igualdad == sobrecargado por el usuario cuando se evalúa la expresión x es nula.

Es decir si sobre escriben mal == estamos muertos, por eso esta bueno siempre usar "is not null" o "is null" 

Veamos un ejemplo: 

public class TestObject

{

  public string Test { get; set; }


  // attempt to allow TestObject to be testable against a string

  public static bool operator ==(TestObject a, object b)

  {

    if(b == null)

      return false;

    

    if(b is string)

      return a.Test == (string)b;


    if(b is TestObject)

      return a.Test == ((TestObject)b).Test;


    return false;

  }


  public static bool operator !=(TestObject a, object b)

  {

    if(b == null)

      return false;

    

    if(b is string)

      return a.Test != (string)b;


    if(b is TestObject)

      return a.Test != ((TestObject)b).Test;


    return false;

  }

}


Como vemos la sobrecarga de == y != cuando b es nulo, retorna false y eso esta mal. Por lo tanto, si ejecutamos el siguiente código:


TestObject e = null;

if(e == null)

  Console.WriteLine("e == null");

if(e is null)

  Console.WriteLine("e is null");


El Output va ser : e is null

Y si hacemos : 


TestObject e = new TestObject();

if(e != null)

  Console.WriteLine("e != null");

if(e is not null)

  Console.WriteLine("e is not null");


El Output va ser: e is not null

Ninguno de los operadores sobrecargados se implementa "correctamente", por lo que la consola nunca genera e == null o e != null.

martes, 14 de febrero de 2023

Cual es el lenguaje de programación más verde?

Cual es el lenguaje de programación más verde o más ecológico? 

Cuando elegimos lenguajes de programación, generalmente lo hacemos en función de su sintaxis, rendimiento o incluso curva de aprendizaje. Sin embargo, hace unos años, un grupo de investigadores portugueses investigó el consumo de energía de 27 lenguajes fueron medidos por su consumo de energía. 

El resumen del artículo dice lo siguiente:

“Este documento presenta un estudio del tiempo de ejecución, el uso de la memoria y el consumo de energía de veintisiete lenguajes de software bien conocidos. Supervisamos el rendimiento de dichos lenguajes utilizando diez problemas de programación diferentes, expresados en cada uno de los lenguajes. Nuestros resultados muestran hallazgos interesantes, como lenguaje más lentos o más rápidos que consumen menos o más energía, y cómo el uso de la memoria influye en el consumo de energía. Mostramos cómo usar nuestros resultados para brindar apoyo a los ingenieros de software para decidir qué lenguaje usar cuando la eficiencia energética es una preocupación”.

Los resultados son los siguientes:




jueves, 9 de febrero de 2023

Mastering API Architecture

 

EBOOK
[eBook] Mastering API Architecture
Hi Emanuel,

Designing and evolving your API architecture is a continuous journey. While you cannot predict how technology and business requirements will change, you can be sure APIs are key to a successful digital strategy. This O’Reilly eBook gives you practical tips on building, managing, and securing APIs, illustrated with a case study about moving a legacy application to the cloud.

In this eBook you will learn:

  • How to transition a legacy application to the cloud, illustrated through a case study
  • API fundamentals such as standards, schema, and specification
  • About API gateways, service mesh, and Kubernetes and when to use each
  • How to mitigate the most common API security risks in the OWASP API Top Ten

martes, 7 de febrero de 2023

Hacer un endpoit para saber el estado del host con echo en Golang parte 2


Todo muy lindo en el post anterior peeeeroo nos falto saber el estado del server, para ello vamos a utilizar la librería : "github.com/shirou/gopsutil". La idea es saber si tiene memoria libre, si el micro esta a full, etc. (estado de los recursos) 

Vamos a necesitar una estructura que contenga toda la información a mostrar : 


type StatusInfo struct {

RuntimeOS            string `json:"runtimeOS"`

TotalMemory          string `json:"total_memory"`

FreeMemory           string `json:"free_memory"`

PercentageUsedMemory string `json:"percentage_used_memory"`


TotalDiskSpace           string `json:"total_disk_space"`

UsedDiskSpace            string `json:"used_disk_space"`

FreeDiskSpace            string `json:"free_disk_space"`

PercentageDiskSpaceUsage string `json:"percentage_disk_space_usage"`


CpuIndexNumber string `json:"cpu_index_number"`

VendorId       string `json:"vendorId"`

Family         string `json:"family"`

NumberOfCores  string `json:"number_of_cores"`

ModelName      string `json:"model_name"`

Speed          string `json:"speed"`


Hostname                 string `json:"hostname"`

Uptime                   string `json:"uptime"`

NumberOfProcessesRunning string `json:"number_of_processes_running"`


Os       string `json:"os"`

Platform string `json:"platform"`


HostId string `json:"host_id"`

}

Y con la librería gopsutil podemos llenar el struct: 


func status(c echo.Context) error {

runtimeOS := runtime.GOOS

// memory

vmStat, err := mem.VirtualMemory()

dealwithErr(err)


diskStat, err := disk.Usage("/")

dealwithErr(err)


// cpu - get CPU number of cores and speed

cpuStat, err := cpu.Info()

dealwithErr(err)


// host or machine kernel, uptime, platform Info

hostStat, err := host.Info()

dealwithErr(err)


var sInfo StatusInfo


sInfo.RuntimeOS = runtimeOS

sInfo.TotalMemory = strconv.FormatUint(vmStat.Total, 10) + " bytes "

sInfo.FreeMemory = strconv.FormatUint(vmStat.Free, 10) + " bytes"

sInfo.PercentageUsedMemory = strconv.FormatFloat(vmStat.UsedPercent, 'f', 2, 64) + "%"


sInfo.TotalDiskSpace = strconv.FormatInt(int64(diskStat.Total), 10) + " bytes "

sInfo.UsedDiskSpace = strconv.FormatInt(int64(diskStat.Used), 10) + " bytes"

sInfo.FreeDiskSpace = strconv.FormatInt(int64(diskStat.Free), 10) + " bytes"

sInfo.PercentageDiskSpaceUsage = strconv.FormatFloat(diskStat.UsedPercent, 'f', 2, 64) + "%"


sInfo.CpuIndexNumber = strconv.FormatInt(int64(cpuStat[0].CPU), 10)

sInfo.VendorId = cpuStat[0].VendorID

sInfo.Family = cpuStat[0].Family

sInfo.NumberOfCores = strconv.FormatInt(int64(cpuStat[0].Cores), 10)

sInfo.ModelName = cpuStat[0].ModelName

sInfo.Speed = strconv.FormatFloat(cpuStat[0].Mhz, 'f', 2, 64)


sInfo.Hostname = hostStat.Hostname

sInfo.Uptime = strconv.FormatUint(hostStat.Uptime, 10)

sInfo.NumberOfProcessesRunning = strconv.FormatUint(hostStat.Procs, 10)


sInfo.Os = hostStat.OS

sInfo.Platform = hostStat.Platform


sInfo.HostId = hostStat.HostID


return c.JSON(http.StatusOK, sInfo)

}


Uso una función para mostrar el error : 


func dealwithErr(err error) {

if err != nil {

fmt.Println(err)

}

}



Y por último agregamos un endpoint en nuestro main: 


func main() {

e := echo.New()

        ....

e.GET("/status", func(c echo.Context) error {

return status(c)

})

e.Logger.Fatal(e.Start(":1323"))

}

Si vamos a localhost:1323/status podemos obtenere una salida como esta : 

{"runtimeOS":"linux","total_memory":"20769558528 bytes ","free_memory":"2220945408 bytes","percentage_used_memory":"75.97%","total_disk_space":"745407905792 bytes ","used_disk_space":"7737049088 bytes","free_disk_space":"737670856704 bytes","percentage_disk_space_usage":"1.04%","cpu_index_number":"0","vendorId":"GenuineIntel","family":"6","number_of_cores":"1","model_name":"11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz","speed":"4700.00","hostname":"crespo","uptime":"262837","number_of_processes_running":"462","os":"linux","platform":"ubuntu","host_id":"c47f72aa-2ce5-414b-97ad-d51759c9a406"}



sábado, 4 de febrero de 2023

Libros gratuitos de Java Code Geeks.

 

Download FREE IT Guides!

 

Full Stack Development with Spring Boot and React - Third Edition ($37.99 Value) FREE for a Limited Time

Taking a practical approach, this book will first walk you through the latest Spring Boot features for creating a robust backend, covering everything from setting up the environment and dependency injection to security and testing.

 
 

Beginning Programming with Java For Dummies, 6th Edition ($18.00 Value) FREE for a Limited Time

Consider Beginning Programming with Java For Dummies your indispensable guide to learning how to program in one of the most popular programming languages—Java! Java is an invaluable language to master

 
 

Uncommon Accountability: A Radical New Approach To Greater Success and Fulfillment ($25.00 Value) FREE for a Limited Time

The implementation of true, organization-wide accountability has the potential to transform your firm’s—and your personal—performance. Unfortunately, the word “accountability” often has negative connotations, including blame, fear, and conflict.

 
 

Best Python Courses

Python is a popular computer programming language. Many people choose to learn Python because it is easy to pick up compared with other programming languages. It is often the language of choice for automation and data science tasks

 
This is third-party material. If you're downloading a whitepaper or ebook for the first time, our distribution service, TradePub, will ask for some personal information in order to help us understand our audience. You will only have to do this once. After that, the system will recognise you. Note: We promise to respect your privacy and keep all information we collect safe. We will never share or sell your information with third parties.

Python en Español

 


Quiero recomendar una pagina que esta muy buena que se llama hablemos python, lo que trata es de unificar todos los recursos de python en español. 

Una propuesta genial, en esta pagina tenes canales de youtube, chat de telegram, canales de discord, cursos, tutoriales, preguntas frecuentes, ofertas de trabajo, etc... 

Dejo link: https://hablemospython.dev/

Foldable y Traverse parte 7

Nuestros métodos listTraverse y listSequence funcionan con cualquier tipo de Aplicativo, pero solo funcionan con un tipo de secuencia: List. Podemos generalizar sobre diferentes tipos de secuencias utilizando una type class, lo que nos lleva a Cats' Traverse. Aquí está la definición abreviada:

package cats

trait Traverse[F[_]] {

    def traverse[G[_]: Applicative, A, B] (inputs: F[A])(func: A => G[B]): G[F[B]]

    def sequence[G[_]: Applicative, B] (inputs: F[G[B]]): G[F[B]] = traverse(inputs)(identity)

}

Cats proporciona instancias de Poligonal para List, Vector, Stream, Option, Either y una variedad de otros tipos. Podemos invocar instancias como de costumbre usando Traverse.apply y usar los métodos poligonal y de secuencia como se describe en la sección anterior:


import cats.Traverse

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

import cats.instances.list._


// for Traverse

val totalUptime: Future[List[Int]] = Traverse[List].traverse(hostnames)(getUptime)

Await.result(totalUptime, 1.second)

// res0: List[Int] = List(1020, 960, 840)


val numbers = List(Future(1), Future(2), Future(3))

val numbers2: Future[List[Int]] = Traverse[List].sequence(numbers)

Await.result(numbers2, 1.second)

// res1: List[Int] = List(1, 2, 3)


También hay versiones sintácticas de los métodos, importadas a través de cats.syntax.traverse:


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

Await.result(hostnames.traverse(getUptime), 1.second)

// res2: List[Int] = List(1020, 960, 840)

Await.result(numbers.sequence, 1.second)

// res3: List[Int] = List(1, 2, 3)


Como puede ver, ¡es mucho más compacto y legible que el código foldLeft con el que comenzamos al principio!

Hacer un endpoit para saber el estado del host con echo en Golang


Me quedo relargo el titulo... pero esa es la idea, hacer uno o más endpoints que me digan si el host responde para saber ante un problema que hacer. 

Para esto usamos echo y hacemos algo tan fácil como esto: 

func main() {

e := echo.New()

e.GET("/health", func(c echo.Context) error {

return c.String(http.StatusOK, "Ok")

})

e.Logger.Fatal(e.Start(":1323"))

}


Ahora si hacemos un pedido por el método GET a la url /health y nos contesta, esta todo bien y si no bueno vamos a tener que investigar. 

Supongamos que queremos saber información del host, por X motivo en este caso podemos utilizar una librería llamada "github.com/zcalusic/sysinfo" 

Primero agregamos el endpoint : 


func main() {

e := echo.New()

e.GET("/health", func(c echo.Context) error {

return c.String(http.StatusOK, "Ok")

})


e.GET("/info", func(c echo.Context) error {

return info(c)

})

e.Logger.Fatal(e.Start(":1323"))

}


Ahora programamos la función info : 


func info(c echo.Context) error {

var si sysinfo.SysInfo

si.GetSysInfo()


return c.JSON(http.StatusOK, si)

}


Y listo, igual esto no es taaaannn útil, sería mejor saber tambien el estado del server, pero eso es para otro post. 



Foldable y Traverse parte 6

Podemos reescribir la poligonal en términos de un Aplicativo. Nuestro acumulador del ejemplo anterior:


Future(List.empty[Int])


es equivalente a Applicative.pure:


import cats.Applicative

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

import cats.syntax.applicative._ // for pure

List.empty[Int].pure[Future]


Nuestro combinador, que solía ser este:


def oldCombine(accum : Future[List[Int]],host: String): Future[List[Int]] = {

val uptime = getUptime(host)

for {

        accum <- accum

        uptime <- uptime

    } yield accum :+ uptime

}


ahora es equivalente a Semigroupal.combine:


import cats.syntax.apply._ // for mapN

// Combining accumulator and hostname using an Applicative:

def newCombine(accum: Future[List[Int]], host: String): Future[List[Int]] =

(accum, getUptime(host)).mapN(_ :+ _)


Al sustituir estos fragmentos en la definición de poligonal, podemos generalizarla para que funcione con cualquier aplicativo:


def listTraverse[F[_]: Applicative, A, B] (list: List[A])(func: A => F[B]): F[List[B]] =

list.foldLeft(List.empty[B].pure[F]) { (accum, item) =>

(accum, func(item)).mapN(_ :+ _)

}


def listSequence[F[_]: Applicative, B] (list: List[F[B]]): F[List[B]] =

listTraverse(list)(identity)


Podemos usar listTraverse para volver a implementar nuestro ejemplo de tiempo de actividad:


val totalUptime = listTraverse(hostnames)(getUptime)

Await.result(totalUptime, 1.second)

// res5: List[Int] = List(1020, 960, 840)


o podemos usarlo con otros tipos de datos de Applicative.



sábado, 28 de enero de 2023

Node + Typescript, configuraciones y scripts útiles


Si ya configuraste el entorno como un campeon, veamos unos trucos para que todo vaya como piña. 

Recarga en frío: La recarga en frío es buena para el desarrollo local. Para hacer esto, tendremos que instalar y configurar en un par de paquetes más: ts-node para ejecutar el código de mecanografiado directamente sin tener que esperar a que se compilara, y nodemon, para observar los cambios en nuestro código y reiniciar automáticamente cuando un el archivo ha cambiado.


npm install --save-dev ts-node nodemon


Ahora debemos agregar una configuración de nodemon.json.


{

  "watch": ["src"],

  "ext": ".ts,.js",

  "ignore": [],

  "exec": "npx ts-node ./src/index.ts"

}


Y luego, para ejecutar el proyecto, todo lo que tenemos que hacer es ejecutar nodemon. Agreguemos un script para eso.


"start:dev": "npx nodemon",


Al ejecutar npm run start:dev, npx nodemon iniciará nuestra aplicación usando npx ts-node ./src/index.ts, buscando cambios en los archivos .ts y .js desde /src.


Para limpiar y compilar el proyecto para la producción, podemos agregar un script de compilación.

Instalamos rimraf, una herramienta multiplataforma que actúa como el comando rm -rf (simplemente borra todo lo que le indique).


npm install --save-dev rimraf


Y luego, agregamos esto en paquete.json.


"build": "rimraf ./build && tsc",


Ahora, cuando ejecutamos npm run build, rimraf eliminará nuestra carpeta de compilación anterior antes de que el compilador de TypeScript emita código nuevo para dist.


Para iniciar la aplicación en producción, todo lo que debemos hacer es ejecutar primero el comando de compilación y luego ejecutar el JavaScript compilado en build/index.js. El script de inicio se ve así.

"start": "npm run build && node build/index.js"


Y listo.