Cats integration
Chimney provides Cats library integration module
for partial transformers.
To include it to your sbt project, add the following line to your build.sbt:
libraryDependencies += "io.scalaland" %% "chimney-cats" % "0.7.2"
The module is released for Scala 2.12.x and 2.13.x and cats 2.x.
If you want to use it with Scala.js, you need to replace %% with %%%.
The module provides package io.scalaland.chimney.cats with all the goodies
described here.
Contents
Cats integration module contains the following stuff:
type classes instances for partial transformers data structures
Applicativeinstance forpartial.ResultSemigroupinstance forpartial.Result.Errors
integration with
Validated(andValidatedNel,ValidatedNec) data type for partial transformers
Important
You need to import io.scalaland.chimney.cats._ in order to have all the above in scope.
Example
Let’s have a look at how to integrate Partial transformers with Cats’ Validated.
case class RegistrationForm(email: String,
username: String,
password: String,
age: String)
case class RegisteredUser(email: String,
username: String,
passwordHash: String,
age: Int)
import io.scalaland.chimney._
import io.scalaland.chimney.dsl._
import io.scalaland.chimney.partial
import io.scalaland.chimney.cats._
import cats.data._
def validateEmail(form: RegistrationForm): ValidatedNec[String, String] = {
if(form.email.contains('@')) {
Validated.valid(form.email)
} else {
Validated.invalid(NonEmptyChain(s"${form.username}'s email: does not contain '@' character"))
}
}
def validateAge(form: RegistrationForm): ValidatedNec[String, Int] = form.age.toIntOption match {
case Some(value) if value >= 18 => Validated.valid(value)
case Some(value) => Validated.invalid(NonEmptyChain(s"${form.username}'s age: must have at least 18 years"))
case None => Validated.invalid(NonEmptyChain(s"${form.username}'s age: invalid number"))
}
implicit val partialTransformer: PartialTransformer[RegistrationForm, RegisteredUser] =
PartialTransformer
.define[RegistrationForm, RegisteredUser]
.withFieldComputedPartial(_.email, form => validateEmail(form).toPartialResult)
.withFieldComputed(_.passwordHash, form => hashpw(form.password))
.withFieldComputedPartial(_.age, form => validateAge(form).toPartialResult)
.buildTransformer
val okForm = RegistrationForm("john@example.com", "John", "s3cr3t", "40")
okForm.transformIntoPartial[RegisteredUser].asValidatedNec
// Valid(RegisteredUser(email = "john@example.com", username = "John", passwordHash = "...", age = 40))
Array(
RegistrationForm("john_example.com", "John", "s3cr3t", "10"),
RegistrationForm("alice@example.com", "Alice", "s3cr3t", "19"),
RegistrationForm("bob@example.com", "Bob", "s3cr3t", "21.5")
).transformIntoPartial[Array[RegisteredUser]].asValidatedNel
// Invalid(NonEmptyList(
// Error(StringMessage("John's email: does not contain '@' character"), ErrorPath(List(Index(0), Accessor("email")))),
// Error(StringMessage("John's age: must have at least 18 years"), ErrorPath(List(Index(0), Accessor("age")))),
// Error(StringMessage("Bob's age: invalid number"), ErrorPath(List(Index(2), Accessor("age"))))
// ))
Form validation logic is implemented in terms of Validated data type. You can easily convert
it to a partial.Result required by withFieldComputedPartial by just using .toPartialResult
which is available after importing the cats integration utilities (import io.scalaland.chimney.cats._).
Result of the partial transformation is then converted to ValidatedNel or ValidatedNec using either
.asValidatedNel or .asValidatedNec extension method call.