fuuid - Functional UUID Build Status Maven Central

Java UUID’s aren’t “exceptionally” safe. Operations throw and are not referentially transparent. We can fix that.

Currently this is the minimal implementation that keeps me content. This aims to offer a baseline set of tools for interacting with FUUID’s. Modules add functionality by bringing in additional dependencies to support additional interactions.

Quick Start

To use fuuid in an existing SBT project with Scala 2.11 or a later version, add the following dependency to your build.sbt:

libraryDependencies += "io.chrisdavenport" %% "fuuid" % "<version>"

Examples

import io.chrisdavenport.fuuid.FUUID
// import io.chrisdavenport.fuuid.FUUID

import cats.effect.IO
// import cats.effect.IO

// We place it in IO because this requires a Sync constraint
val create: IO[FUUID] = FUUID.randomFUUID[IO]
// create: cats.effect.IO[io.chrisdavenport.fuuid.FUUID] = IO$634632927

val fromString : Either[Throwable, FUUID] = FUUID.fromString("d6faceab-4193-4508-86ca-e1561d38fea6")
// fromString: Either[Throwable,io.chrisdavenport.fuuid.FUUID] = Right(d6faceab-4193-4508-86ca-e1561d38fea6)

val failsReferentiallyTransparently : Either[Throwable, FUUID] = FUUID.fromString("Not a UUID")
// failsReferentiallyTransparently: Either[Throwable,io.chrisdavenport.fuuid.FUUID] = Left(java.lang.IllegalArgumentException: Invalid UUID string: Not a UUID)

// For some syntax improvements
import cats.implicits._
// import cats.implicits._

// Uses cats Eq
val equalToItself : IO[Boolean] = for {
  fuuid <- FUUID.randomFUUID[IO]
} yield fuuid === fuuid
// equalToItself: cats.effect.IO[Boolean] = <function1>

equalToItself.unsafeRunSync
// res3: Boolean = true

// Uses cats Order
val laterGreaterThanEarlier : IO[Boolean] = for {
  fuuid1 <- FUUID.randomFUUID[IO]
  fuuid2 <- FUUID.randomFUUID[IO]
} yield fuuid2 > fuuid1
// laterGreaterThanEarlier: cats.effect.IO[Boolean] = IO$381646931

laterGreaterThanEarlier.unsafeRunSync
// res5: Boolean = true

Circe Integration

To use fuuid directly in you circe Json handling, add to your build.sbt:

libraryDependencies += "io.chrisdavenport" %% "fuuid-circe" % "<version>"

An example using this integration:

import io.chrisdavenport.fuuid.circe._
// import io.chrisdavenport.fuuid.circe._

import io.circe.syntax._
// import io.circe.syntax._

// Running UnsafeRunSync For Tut Purposes - Do Not Do this in your code please.
val circeFUUID = FUUID.randomFUUID[IO].unsafeRunSync
// circeFUUID: io.chrisdavenport.fuuid.FUUID = f5019bcb-20b9-4c2b-abf7-29fa5bfee458

val circeFUUIDJson = circeFUUID.asJson
// circeFUUIDJson: io.circe.Json = "f5019bcb-20b9-4c2b-abf7-29fa5bfee458"

val reparsing = circeFUUIDJson.as[FUUID]
// reparsing: io.circe.Decoder.Result[io.chrisdavenport.fuuid.FUUID] = Right(f5019bcb-20b9-4c2b-abf7-29fa5bfee458)

Http4s Integration

To use fuuid to define http4s paths, add to your build.sbt:

libraryDependencies += "io.chrisdavenport" %% "fuuid-http4s" % "<version>"

An example using this integration:

import io.chrisdavenport.fuuid.http4s.FUUIDVar
// import io.chrisdavenport.fuuid.http4s.FUUIDVar

import org.http4s._, org.http4s.dsl.io._
// import org.http4s._
// import org.http4s.dsl.io._

def getEntityByUuid(id: FUUID): IO[String] = ???
// getEntityByUuid: (id: io.chrisdavenport.fuuid.FUUID)cats.effect.IO[String]

val service: HttpRoutes[IO] =
  HttpRoutes.of[IO] {
    case GET -> Root / "uuid" / FUUIDVar(id) =>
      for {
        entity <- getEntityByUuid(id)
        response <- Ok(entity)
      } yield response
  }
// service: org.http4s.HttpRoutes[cats.effect.IO] = Kleisli(org.http4s.HttpRoutes$$$Lambda$7548/917382206@2d8c1a1f)

Doobie Integration

To use fuuid to store UUID’s using doobie, add to your build.sbt:

libraryDependencies += "io.chrisdavenport" %% "fuuid-doobie" % "<version>"

This dependency will provide a Meta[FUUID] which depends on Meta[UUID] to provide support for FUUID. You will need to provide the instance of Meta[UUID] in scope. Firstly, we import:

import doobie._
import doobie.implicits._
import io.chrisdavenport.fuuid.doobie.implicits._

Postgres Example

An example of a query and an insert using this integration in Postgres.

// This importe will provide `Meta[UUID]` support for postgres
import doobie.postgres.implicits._
// import doobie.postgres.implicits._

// This is the table we'll use for the insert and update below
def createdTable: Update0 = {
  sql"""
    CREATE TABLE tablewithUUIDid (
      id   UUID NOT NULL
    )""".update
}
// createdTable: doobie.Update0

def queryBy(fuuid: FUUID): Query0[FUUID] = {
    sql"""SELECT id from tablewithUUIDid where id = ${fuuid}""".query[FUUID]
  }
// queryBy: (fuuid: io.chrisdavenport.fuuid.FUUID)doobie.Query0[io.chrisdavenport.fuuid.FUUID]

def insertId(fuuid: FUUID): Update0 = {
  sql"""INSERT into tablewithUUIDid (id) VALUES ($fuuid)""".update
}
// insertId: (fuuid: io.chrisdavenport.fuuid.FUUID)doobie.Update0

H2 Example

An example of a query and an insert using this integration in H2:

// This importe will provide `Meta[UUID]` support for h2
import doobie.h2.implicits._
// import doobie.h2.implicits._

// This is the table we'll use for the insert and update below
def createdTable: Update0 = {
  sql"""
    CREATE TABLE tablewithUUIDid (
      id   UUID NOT NULL
    )""".update
}
// createdTable: doobie.Update0

def queryBy(fuuid: FUUID): Query0[FUUID] = {
    sql"""SELECT id from tablewithUUIDid where id = ${fuuid}""".query[FUUID]
  }
// queryBy: (fuuid: io.chrisdavenport.fuuid.FUUID)doobie.Query0[io.chrisdavenport.fuuid.FUUID]

def insertId(fuuid: FUUID): Update0 = {
  sql"""INSERT into tablewithUUIDid (id) VALUES ($fuuid)""".update
}
// insertId: (fuuid: io.chrisdavenport.fuuid.FUUID)doobie.Update0