Translate

jueves, 16 de junio de 2022

Scripting en java


Supongamos que tenemos una lógica que cambia casi todo el tiempo, por ejemplo reglas de negocio que permitan calcular un impuesto, etc. 

Lo que podríamos hacer es tener en una variable un script que permita calcular esa lógica y ejecutarlo en java.  

Supongamos que al principio este impuesto es un porcentaje de un monto : 

String code="monto * 0.1"; 

Bindings bindings = new SimpleBindings();

bindings.put("monto", 1000);

ScriptEngineManager manager = new ScriptEngineManager();

ScriptEngine engine = manager.getEngineByExtension("js");

try {

  System.out.print(engine.eval(code, bindings));

} catch (ScriptException ex) {

  //catch statement

}


Luego esto puede cambiar y el impuesto volverse màs complejo, no importa porque si lo cambiamos el programa lo ejecuta : 

String code="monto * 0.01 +  (monto * 0.01) * 0.21 "; 

Bindings bindings = new SimpleBindings();

bindings.put("monto", 1000);

ScriptEngineManager manager = new ScriptEngineManager();

ScriptEngine engine = manager.getEngineByExtension("js");

try {

  System.out.print(engine.eval(code, bindings));

} catch (ScriptException ex) {

  //catch statement

}


Podemos modificar la logica sin volver a compilar, teniendo la potencia de un lenguaje script en nuestro querido java. 

En fin el scripting en java es una gran herramienta que no todos conocen. 


sábado, 11 de junio de 2022

Top de lenguajes en la JVM


Buscando que lenguajes están en el top 5 de más usados me encontré con esto en la wikipedia : 

As of April 2022, according to the TIOBE Index[1] of the top 100 programming languages, the top JVM languages are:

Lo que me sorprendio no fue la información sino que la wikipedia este tan actualizada. 

Otra cosa que me llamo la atención es que groovy le gane a scala o a clojure, pense que era diferente.

Y bueno, eso es todo, no me critiquen porque pegue algo en ingles en un blog en español, es de la wikipedia, no voy andar cambiando la fuente.  

Dejo link : https://en.wikipedia.org/wiki/List_of_JVM_languages

Mónadas en Cats parte 11

cats.syntax.either también agrega algunos métodos útiles para instancias de Either

Los usuarios de Scala 2.11 o 2.12 pueden usar orElse y getOrElse para extraer valores del lado derecho o devolver un valor predeterminado:

import cats.syntax.either._

"Error".asLeft[Int].getOrElse(0)

// res11: Int = 0

"Error".asLeft[Int].orElse(2.asRight[String])

// res12: Either[String, Int] = Right(2)


El método de aseguramiento nos permite verificar si el valor de la derecha satisface un predicado:


-1.asRight[String].ensure("Must be non-negative!")(_ > 0)

// res13: Either[String, Int] = Left("Must be non-negative!")


Los métodos recover y recoverWith con proporcionan un manejo de errores similar al de sus homónimos en Future:


"error".asLeft[Int].recover {

case _: String => -1

}

// res14: Either[String, Int] = Right(-1)

"error".asLeft[Int].recoverWith {

case _: String => Right(-1)

}

// res15: Either[String, Int] = Right(-1)


Hay métodos leftMap y bimap para complementar el map:


"foo".asLeft[Int].leftMap(_.reverse)

// res16: Either[String, Int] = Left("oof")

6.asRight[String].bimap(_.reverse, _ * 7)

// res17: Either[String, Int] = Right(42)

"bar".asLeft[Int].bimap(_.reverse, _ * 7)

// res18: Either[String, Int] = Left("rab")


El método swap nos permite intercambiar izquierda por derecha:


123.asRight[String]

// res19: Either[String, Int] = Right(123)

123.asRight[String].swap

// res20: Either[Int, String] = Left(123)


Finalmente, Cats agrega una serie de métodos de conversión: toOption, toList, toTry, toValidated, etc.



miércoles, 8 de junio de 2022

sttp: ¡el cliente Scala HTTP que siempre quisiste!


sttp es un cliente http, simple que permite manejar las respuestas. sttp proporciona interfaces tanto síncronas como asíncronas, procedimentales y funcionales.

Las implementaciones incluyen aquellas basadas en clientes akka-http, async-http-client, http4s, OkHttp y HTTP que se envían con Java. Se integran con Akka, Monix, fs2, cats-effect, scalaz y ZIO. Las versiones de Scala admitidas incluyen 2.11, 2.12, 2.13 y 3, Scala.JS y Scala Native.

Veamos un pequeño ejemplo: 

// In addition to the usual values brought into scope by `sttp.client3._`,

// the `quick` version also defines a default synchronous `backend`.

import sttp.client3.quick._

// Circe integration: `asJson` response description.

import sttp.client3.circe._


import io.circe.generic.auto._


// Case classes corresponding to the json returned by GitHub (just the 

// fields that interest us).

case class GitHubResponse(total_count: Int, items: List[GitHubItem])

case class GitHubItem(name: String, stargazers_count: Int)


val query = "language:scala"

val sort: Option[String] = Some("stars")


// Describing the request: specifying the method, uri and how to handle

// the response. The `query` parameter is automatically url-encoded

// `sort` will be unwrapped if `Some(_)`, and removed if `None`.

val request = basicRequest

  .get(uri"https://api.github.com/search/repositories?q=$query&sort=$sort")

  .response(asJson[GitHubResponse])

  

// As we are using the synchronous `HttpURLConnectionBackend`, `send()` will 

// return `Response[_]`. Async backends return e.g. `Future[Response[_]]`.

val response = request.send(backend)

// The body will be a `Left(_)` in case of a non-2xx response, or a json

// deserialization error. It will be `Right(_)` otherwise.

response.body match {

  case Left(error) => println(s"Error when executing request: $error")

  case Right(data) =>

    println(s"Found ${data.total_count} Scala projects.")

    println(s"Showing ${data.items.size} with most stars:")

    data.items.foreach { item =>

      println(s"  ${item.name} (${item.stargazers_count})")

    }

}

Dejo link : https://sttp.softwaremill.com/en/latest/


lunes, 6 de junio de 2022

Mónadas en Cats parte 10

Además de crear instancias de Left y Right directamente, también podemos importar los métodos de extensión asLeft y asRight desde cats.syntax.either :

import cats.syntax.either._ // for asRight

val a = 3.asRight[String]

// a: Either[String, Int] = Right(3)

val b = 4.asRight[String]

// b: Either[String, Int] = Right(4)

for {

x <- a

y <- b

} yield x*x + y*y

// res3: Either[String, Int] = Right(25)


Estos "constructores inteligentes" tienen ventajas sobre Left.apply y Right.apply porque devuelven resultados de tipo Either en lugar de Left y Right. Esto ayuda a evitar problemas de inferencia de tipo causados por la sobreflecha, como el problema del ejemplo a continuación:


def countPositive(nums: List[Int]) = nums.foldLeft(Right(0)) { (accumulator, num) =>

if(num > 0) {

accumulator.map(_ + 1)

} else {

Left("Negative. Stopping!")

}

}

// error: type mismatch;

//found

//required: scala.util.Right[Nothing,Int]

: scala.util.Either[Nothing,Int]

//accumulator.map(_ + 1)

//^^^^^^^^^^^^^^^^^^^^^^

// error: type mismatch;

//found

//required: scala.util.Right[Nothing,Int]

: scala.util.Left[String,Nothing]

//Left("Negative. Stopping!")

//^^^^^^^^^^^^^^^^^^^^^^^^^^^


Este código falla al compilar por dos razones:

1. el compilador infiere el tipo del acumulador como Right en lugar de Either;

2. no especificamos parámetros de tipo para Right.apply, por lo que el compilador infiere que el parámetro izquierdo es Nothing.

Cambiar a asRight evita ambos problemas. asRight tiene un tipo de retorno de Either, y nos permite especificar completamente el tipo con solo un parámetro de tipo:


def countPositive(nums: List[Int]) = nums.foldLeft(0.asRight[String]) { (accumulator, num) =>

if(num > 0) {

accumulator.map(_ + 1)

} else {

Left("Negative. Stopping!")

}

}

countPositive(List(1, 2, 3))

// res5: Either[String, Int] = Right(3)

countPositive(List(1, -2, 3))

// res6: Either[String, Int] = Left("Negative. Stopping!")


cats.syntax.either agrega algunos métodos de extensión útiles para el objeto complementario Any. Los métodos catchOnly y catchNonFatal son excelentes para capturar excepciones como instancias de:


Either.catchOnly[NumberFormatException]("foo".toInt)

// res7: Either[NumberFormatException, Int] = Left(

// java.lang.NumberFormatException: For input string: "foo"

// )

Either.catchNonFatal(sys.error("Badness"))

// res8: Either[Throwable, Nothing] = Left(java.lang.RuntimeException:


También hay métodos para crear un Either a partir de otros tipos:


Either.fromTry(scala.util.Try("foo".toInt))

// res9: Either[Throwable, Int] = Left(

//

// )

java.lang.NumberFormatException: For input string: "foo"

Either.fromOption[String, Int](None, "Badness")

// res10: Either[String, Int] = Left("Badness")

viernes, 3 de junio de 2022

Crear un proyecto Spring boot, con Scala y Sbt



La idea es crear un proyecto spring boot, con scala y sbt. (ya lo dice el titulo)

Primero hacemos un proyecto hello word con sbt (ojo tienen que tener sbt instalado, esta bueno instalarlo con sdkman) 

sbt new scala/hello-world.g8

Luego, tenemos que agregar nuestra dependencia web de spring boot en build.sbt : 

// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web

libraryDependencies += "org.springframework.boot" % "spring-boot-starter-web" % "2.6.7"

libraryDependencies += "org.springframework.boot" % "spring-boot-configuration-processor" % "2.6.7"


Deberían de modificar bastante el buld.sbt en mi caso me quedo así :


scalaVersion := "2.13.8"

organization := "com.miCompania"
name := "ejemplo"
version := "1.0"


libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "2.1.1"

// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web
libraryDependencies += "org.springframework.boot" % "spring-boot-starter-web" % "2.6.7"
libraryDependencies += "org.springframework.boot" % "spring-boot-configuration-processor" % "2.6.7"

Ahora lo que debemos de hacer es crear un paquete y mover el main a ese paquete para luego modificarlo de tal forma que llame a run de spring boot : 

package com.miCompania

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication


@SpringBootApplication
class Application

object Main extends App {
  SpringApplication.run(classOf[Application], args:_*)
}

Ojo, tienen que tener el main en un paquete sino no anda. 

Por ultimo hacemos nuestro endpoint, en este ejemplo voy a hacer un hola mundo, comun. Pero en este punto podemos utilizar todas las ventajas de spring boot : 

package com.assembly.endpoint

import org.springframework.http.ResponseEntity

import org.springframework.web.bind.annotation.{PathVariable, RequestMapping, RequestMethod, RestController}


@RestController

@RequestMapping(Array("/v1"))

class GreeterEndPoint {


  @RequestMapping(value=Array("/hello/{name}"), method = Array(RequestMethod.GET))

  def sayHello(@PathVariable(name = "name") name:String): ResponseEntity[String] =

    ResponseEntity.ok(s"Hello ${name}")

}


Lo puse en otro paquete, esto solo para que quede más ordenado, y listo!!! 

jueves, 2 de junio de 2022

Mónadas en Cats parte 9

Veamos otra mónada útil: el tipo Either de la biblioteca estándar de Scala. En Scala 2.11 y versiones anteriores, muchas personas no consideraban a Either como una mónada porque no tenía métodos map y flatMap. En Scala 2.12, sin embargo, esto cambio. 

En Scala 2.11, tampoco tenía un mapa predeterminado o un método flatMap. Esto hizo que la versión Scala 2.11 de Either fuera inconveniente para usar en comprensiones. Tuvimos que insertar llamadas a .right en cada cláusula del generador:

val either1: Either[String, Int] = Right(10)

val either2: Either[String, Int] = Right(32)

for {

  a <- either1.right

  b <- either2.right

} yield a + b


En Scala 2.12, se rediseñó, el moderno Either toma la decisión de que el lado derecho representa el caso de éxito y, por lo tanto, admite map y flatMap directamente. Esto hace que las comprensiones sean mucho más agradables:

for {

a <- either1

b <- either2

} yield a + b

// res1: Either[String, Int] = Right(42)


Cats retrotrae este comportamiento a Scala 2.11 a través de la importación cats.syntax.either, lo que nos permite usar el bien Either en todas las versiones compatibles de Scala. En Scala 2.12+ podemos omitir esta importación o dejarla en su lugar sin romper nada:


import cats.syntax.either._ // for map and flatMap

for {

a <- either1

b <- either2

} yield a + b



Why Ruby?


¿Quieres trabajar con Ruby pero no conoces el lenguaje? Nos hemos encontrado múltiples compañías que buscan profesionales que quieran cambiar su stack y aprender Ruby. ¿Te interesa?

Así comienza esta pagina que tiene como objetivo ampliar el mercado laboral de ruby. Si ya te intereso dejo el link : 

https://ruby.getmanfred.com/


miércoles, 1 de junio de 2022

Cache con Redis y Spring boot


Vamos ha hacer una pequeña cache para unos servicios en spring boot. 

Primero es necesario tener instalado Redis, y si ...

Luego agregamos esta dependencia a nuestro proyecto (uso gradle) : 

implementation("org.springframework.boot:spring-boot-starter-cache")

implementation("org.springframework.boot:spring-boot-starter-data-redis")


Luego configuramos en nuestro cache con esta clase : 

 package com.uap.demo.config

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.cache.RedisCacheConfiguration
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair
import java.time.Duration

@Configuration
class CacheConfig {

    @Bean
    fun cacheConfiguration(): RedisCacheConfiguration? {
        return RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(60))
            .disableCachingNullValues()
            .serializeValuesWith(SerializationPair.fromSerializer(GenericJackson2JsonRedisSerializer()))
    }
}

Yo le puse 60 minutos de vida a mis datos dado que no cambian tan frecuente. 

Ahora en el Application agregamos @EnableCaching, algo así : 

@SpringBootApplication
@EnableCaching
class DemoApplication

fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}

Por ultimo a nuestro servicio le decimos que vamos a cachear los datos, en este ejemplo tengo un servicio X : 

@Service
class XServiceImpl : XService {

    @Cacheable(value = ["xxxxx"])
    override fun x(parametro: String): List<String> {
          ...
    }
}

Lo que va a ocurrir es que cuando llamemos al servicio con un parametro, se va generar una entrada en Redis, y la próxima vez que llamemos este servicio usará la cache. 

Y listo!!! 

Another Redis Desktop Manager


Haciendo unas cosas con Redis me encontré con esta aplicación y me pareció muy buena. 

Un administrador de escritorio redis rápido y estable, compatible con Linux, Windows, Mac.

En Ubuntu esta en el store, así que lo instale totalmente de forma automática. 

Dejo unos screenshots : 





Dejo link : 

https://github.com/qishibo/AnotherRedisDesktopManager

jueves, 26 de mayo de 2022

Libros gratuitos de Java code geeks

 

Get schooled by Java Code Geeks

Download IT Guides!

 

JavaFX Programming Cookbook

JavaFX 2.0 and later is implemented as a native Java library, and applications using JavaFX are written in native Java code. JavaFX Script has been scrapped by Oracle, but development is...

 
 

Java Servlet Tutorial

A servlet can almost be thought of as an applet that runs on the server side-without a face. Java servlets make many Web applications possible.

Java...

 
 

Groovy Programming Cookbook

Groovy 1.0 was released on January 2, 2007, and Groovy 2.0 in July, 2012. Since version 2, Groovy can also be compiled statically, offering type inference and performance very close to...

 
 

Gradle Build Automation Handbook

Gradle was designed for multi-project builds which can grow to be quite large, and supports incremental builds by intelligently determining which parts of the build tree are up-to-date,...

 

miércoles, 25 de mayo de 2022

Mónadas en Cats parte 8

Ya demostramos la sintaxis de map y flatMap de Cats al escribir un método que abstraía sobre diferentes mónadas:

import cats.Monad

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

import cats.syntax.flatMap._ // for flatMap

def sumSquare[F[_]: Monad](a: F[Int], b: F[Int]): F[Int] =

for {

x <- a

y <- b

} yield x*x + y*y

Este método funciona bien en Opcion y List, pero no podemos llamarlo pasando valores simples:

sumSquare(3, 4)
// error: no type parameters for method sumSquare: (a: F[Int], b: F[
Int])(implicit evidence$1: cats.Monad[F])F[Int] exist so that it
can be applied to arguments (Int, Int)
...

Sería increíblemente útil si pudiéramos usar sumSquare con parámetros que estuvieran en una mónada o que no estuvieran en ninguna mónada. Esto nos permitiría abstraernos sobre código monádico y no monádico. Afortunadamente, Cats proporciona el tipo de identificación para cerrar la brecha:

import cats.Id
sumSquare(3 : Id[Int], 4 : Id[Int])
// res1: Id[Int] = 25

Id nos permite llamar a nuestro método monádico usando valores simples. Sin embargo, la semántica exacta es difícil de entender. ¡Convertimos los parámetros a sumSquare como Id[Int] y recibimos un Id[Int] como resultado! ¿Qué está sucediendo? Aquí está la definición de Id para explicar:

package cats
type Id[A] = A

id es en realidad un alias de tipo que convierte un tipo atómico en un constructor de tipo de parámetro único. Podemos emitir cualquier valor de cualquier tipo a una identificación correspondiente:

"Dave" : Id[String]
// res2: Id[String] = "Dave"
123 : Id[Int]
// res3: Id[Int] = 123
List(1, 2, 3) : Id[List[Int]]
// res4: Id[List[Int]] = List(1, 2, 3)

Cats proporciona instancias de varias clases de tipos para Id, incluidos Functor y Monad. Estos nos permiten llamar map, flatMap y pure con valores simples:

val a = Monad[Id].pure(3)
// a: Id[Int] = 3
val b = Monad[Id].flatMap(a)(_ + 1)
// b: Id[Int] = 4
import cats.syntax.functor._ // for map
import cats.syntax.flatMap._ // for flatMap
for {
x <- a
y <- b
} yield x + y
// res5: Id[Int] = 7

La capacidad de abstracción sobre el código monádico y no monádico es extremadamente poderosa. Por ejemplo, podemos ejecutar código de forma asincrónica en producción usando Future y sincrónicamente en prueba usando Id. 

domingo, 22 de mayo de 2022

Mónadas en Cats parte 7

 La sintaxis de las mónadas proviene de tres lugares:

  • cats.syntax.flatMap proporciona sintaxis para flatMap;
  • cats.syntax.functor proporciona sintaxis para el map;
  • cats.syntax.applicative proporciona sintaxis para pure.
En la práctica, a menudo es más fácil importar todo de una sola vez desde cats.implicits. Sin embargo, usaremos las importaciones individuales aquí para mayor claridad.

Podemos usar pure para construir instancias de una mónada. A menudo necesitaremos especificar el parámetro de tipo para eliminar la ambigüedad de la instancia particular que queremos.

import cats.instances.option._  // for Monad
import cats.instances.list._ // for Monad
import cats.syntax.applicative._ // for pure

1.pure[Option]
// res5: Option[Int] = Some(1)
1.pure[List]
// res6: List[Int] = List(1)

Es difícil demostrar los métodos flatMap y map directamente en las mónadas de Scala como Option y List, porque definen sus propias versiones explícitas de esos métodos. En su lugar, escribiremos una función genérica que realice un cálculo en los parámetros que vienen envueltos en una mónada a elección del usuario:

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

def sumSquare[F[_]: Monad](a: F[Int], b: F[Int]): F[Int] = a.flatMap(x => b.map(y => x*x + y*y))

import cats.instances.option._ // for Monad
import cats.instances.list._  // for Monad

sumSquare(Option(3), Option(4))
// res7: Option[Int] = Some(25)

sumSquare(List(1, 2, 3), List(4, 5))
// res8: List[Int] = List(17, 26, 20, 29, 25, 34)

Podemos reescribir este código usando comprensiones. El compilador "hará lo correcto" reescribiendo nuestra comprensión en términos de flatMap y map e insertando las conversiones implícitas correctas para usar nuestra Monad:

def sumSquare[F[_]: Monad](a: F[Int], b: F[Int]): F[Int] =
for {
    x <- a
    y <- b
} yield x*x + y*y

sumSquare(Option(3), Option(4))
// res10: Option[Int] = Some(25)

sumSquare(List(1, 2, 3), List(4, 5))
// res11: List[Int] = List(17, 26, 20, 29, 25, 34)

Eso es más o menos todo lo que necesitamos saber sobre las generalidades de las mónadas en Cats. 

sábado, 21 de mayo de 2022

Mónadas en Cats parte 6

Cats proporciona instancias para todas las mónadas en la biblioteca estándar (Option, List, Vector, etc.) a través de cats.instances:

import cats.instances.option._ // for Monad

Monad[Option].flatMap(Option(1))(a => Option(a*2))

// res0: Option[Int] = Some(2)

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

Monad[List].flatMap(List(1, 2, 3))(a => List(a, a*10))

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

import cats.instances.vector._ // for Monad

Monad[Vector].flatMap(Vector(1, 2, 3))(a => Vector(a, a*10))

// res2: Vector[Int] = Vector(1, 10, 2, 20, 3, 30)

Cats también proporciona una mónada para Future. A diferencia de los métodos de la propia clase Future, los métodos pure y flatMap de la mónada no pueden aceptar parámetros ExecutionContext implícitos (porque los parámetros no forman parte de las definiciones de la característica Monad). Para evitar esto, Cats requiere que tengamos un ExecutionContext en el alcance cuando invocamos una Monad para Future: 

import cats.instances.future._ // for Monad
import scala.concurrent._
import scala.concurrent.duration._

val fm = Monad[Future]
// error: Could not find an instance of Monad for scala.concurrent.
Future
// val fm = Monad[Future]
//
 ^^^^^^^^^^^^^

Poner el ExecutionContext en el alcance corrige la resolución implícita requerida para invocar la instancia:

import scala.concurrent.ExecutionContext.Implicits.global

val fm = Monad[Future]
// fm: Monad[Future] = cats.instances.FutureInstances$$anon$1@7ba44cd6

La instancia de Monad usa el ExecutionContext capturado para llamadas posteriores a pure y flatMap:

val future = fm.flatMap(fm.pure(1))(x => fm.pure(x + 2))
Await.result(future, 1.second)
// res4: Int = 3

Además de lo anterior, Cats proporciona una gran cantidad de nuevas mónadas que no tenemos en la biblioteca estándar. Nos familiarizaremos con algunos de estos en un momento.



jueves, 19 de mayo de 2022

Mónadas en Cats parte 5

La clase de tipo de mónada es cats.Monad. Monad extiende otras dos clases de tipos:

FlatMap, que proporciona el método flatMap, y Applicative, que proporciona pure. Applicative también extiende Functor, que le da a cada Monad un método de map.

Aquí hay algunos ejemplos que usan pure y flatMap, y map:

import cats.Monad

import cats.instances.option._ // for Monad

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

val opt1 = Monad[Option].pure(3)

// opt1: Option[Int] = Some(3)

val opt2 = Monad[Option].flatMap(opt1)(a => Some(a + 2))

// opt2: Option[Int] = Some(5)

val opt3 = Monad[Option].map(opt2)(a => 100 * a)

// opt3: Option[Int] = Some(500)


val list1 = Monad[List].pure(3)

// list1: List[Int] = List(3)

val list2 = Monad[List].flatMap(List(1, 2, 3))(a => List(a, a*10))

// list2: List[Int] = List(1, 10, 2, 20, 3, 30)

val list3 = Monad[List].map(list2)(a => a + 123)

// list3: List[Int] = List(124, 133, 125, 143, 126, 153)


Monad proporciona muchos otros métodos, incluidos todos los métodos de Functor.