Les onzes commandements de Jon Pretty (2e partie)

Aujourd'hui, suite et fin de l'article sur les onzes librairies présentées lors de Scalar 2019 😇

Caesura

Caesura est une librairie qui vous aide à parser vos CSV en Row puis en collections Scala ou bien directement en case classes, le dernier étant l'intérêt principal de cette librairie. Caesura se basant sur Magnolia, il est possible de parser une ligne de CSV directement dans une case class ayant des membres complexe, à savoir ayant des case classes imbriquées.

Exemple :

case class Address(city: String, zipcode: String)
case class Person(name: String, age: String, gender: String, address: Address)

val csv: String = """toto,10,male,Paris,75002"""
val toto: Person = Csv.parse(csv).as[Person]
println(toto)
//Person(toto,10,male,Address(Paris,75002))

Ici nous décodons une case class complexe mais nous pouvons aussi aller dans l'autre sens et encoder :

val csv: String = Row.from(
      Person("toto","10","male",Address("Paris","75002"))
    ).elems.mkString(",")
//toto,10,male,Paris,75002

Contextual

Contextual permet de définir comment vos String sont lues et d'avoir des retours au compile time sur la validité de vos String.

Exemple :

email"bourriquet@univalence.io" //La string bien crée car c'est bien un email
bin"11111111" //255
txt"""Avengers,
|assemble""" // "Avengers,\nassemble"

Guillotine

Mais c’est une révolte ? — Non, Sire, c’est une révolution !

Grâce à Guillotine vous pourrez enfin exécuter vos commandes shell de manière sympa :

val thanosQuote = sh"echo I am inevitable".exec[String]
//thanosQuote: String = "I am inevitable"

Bien sûr vous pouvez exécuter des commandes plus utiles tels que des

val gitCommit = sh"git commit -m \"yolo\""

et si jamais vous avez des erreurs dans votre commande, avec par exemple des guillemets mal placés, c'est lors du compile-time que vous aurez un retour.

Kaleidoscope

Une des librairies les plus utiles, dixit Jon Pretty, Kaleidoscope exauce vos rêves les plus fous et vous permet de faire du pattern matching sur des expressions régulières.

Exemple sympa :

val email = "tigrou@univalence.io"

val optEmail: Option[String] = email match {
  case r"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,6}$$" => Some(email)
  case _ => None
}
//optEmail contiendra bien "tigrou@univalence.io"

Bien sûr vous avez aussi la possibilité d'extraire certaines parties de la String que vous matchez avec une expression régulière avec la syntaxe suivante :

val r"^$prefix@([a-z0-9._%+-]+)@$domain@([a-z0-9.-]+)\.$tld@([a-z]{2,6})$$" = "tigrou@univalence.io"

//prefix: String = "tigrou"
//domain: String = "univalence.io"
//tld: String = "io"

Mutatus

Mutatus pour faire des mutations dans le cloud, ici GCP. La librairie propose une API simple pour sérialiser de la donnée vers GCP :

import mutatus._

case class Person(id: String, age: Int, address: Ref[Address])
case class Address(city: String, zipcode: String)

implicit val addressId: Id[Address] = _.id
implicit val personId: Id[Person] = _.id

val address: Address = Address("Forêt des rêves bleus", "12345")
//On sauvegarde address dans GCP Datastore
val addressRef: Ref[Address] = address.save()
//De même pour winnie
val winnie: Ref[Person] = Person("Winnie l'ourson", 93, addressRef).save()
//On peut accéder à l'addresse de winnie
val addressWinnie = winnie.address()

Winnie et son adresse sont maintenant dans votre GCP Datastore.

Vous pouvez aussi récupérer Winnie du Datastore avec :

val winnie = Dao[Person].all()

Le type Ref représente une référence à un datatype qui est stockée autre part, par exemple sur GCP.

Optometry

Optometry est une librairie semblable à Monocle, qui permet de créer des lens avec un syntaxe assez légère.

Créer une Lens est assez simple :

case class Person(name: String, address: Address, age: Int)
case class Address(city: String, zipcode: String)

val nameLens = Lens[Person](_.name)
val person: Person = Person("porcinet", Address("Forêt des rêves bleus", "12345"), 1) 

// nameLens(person) = "porcinet"

Vous pouvez bien sûr aller dans des niveaux plus profonds :

case class Person(name: String, address: Address, age: Int)
case class Address(city: String, zipcode: String)


val getCity = Lens[Person](_.address.city)
// getLines(person()) = "Forêt des rêves bleus"

D'autres opérations intéressantes sont disponibles comme le fait de pouvoir mettre à jour des membres spécifiques d'une case class imbriquée, de combiner des Lens, ...

Conclusion

Certaines librairies telles que Kaleidoscope ou Contextual apportent une vraie valeur ajoutée et vous seront utiles dans la plupart de vos projets.
Néanmoins il est vrai que ces librairies tombent souvent dans l'oubli car ce sont des libraries qui ne sont pas très connues et il s'agit alors de les utiliser si le cas se présente et d'envoyer des idées d'amélioration.