Skip to content

Instantly share code, notes, and snippets.

@owainlewis
Last active August 12, 2021 07:03
Show Gist options
  • Save owainlewis/87d2e9c43d3d9e188cdfa839136f1e9e to your computer and use it in GitHub Desktop.
Save owainlewis/87d2e9c43d3d9e188cdfa839136f1e9e to your computer and use it in GitHub Desktop.
Validation examples
object EitherValidationExamples {
type ValidationError = String
def validateEmail(user: User): Either[ValidationError, User] =
if (user.email contains "@") Right(user) else Left("Must supply a valid email")
def validateAge(user: User): Either[ValidationError, User] =
if (user.age > 18) Right(user) else Left("Must be over 18")
// Combine multiple validations into one big validation that either contains a list of errors OR a valid object
def combineValidations[A,B](obj: B, eithers: Either[A,B]*): Either[Seq[A], B] = {
val errors = eithers.collect { case Left(e) => e }
if (errors.isEmpty) Right(obj) else Left(errors)
}
}
scala> val validUser = User("[email protected]", 21)
validUser: io.forward.cats.examples.User = User([email protected],21)
scala> val invalidUser = User("foo", 16)
invalidUser: io.forward.cats.examples.User = User(foo,16)
scala> validateEmail(validUser)
res5: cats.data.Validated[String,io.forward.cats.examples.User] = Valid(User([email protected],21))
scala> validateEmail(invalidUser)
res1: cats.data.Validated[String,io.forward.cats.examples.User] = Invalid(Must supply a valid email)
scala> validateEmail(invalidUser).isValid
res2: Boolean = false
package io.forward.cats.examples
import cats.data.Validated._
import cats.data.Validated
case class User(email: String, age: Int)
object Validation extends Validator {
def validateUser(user: User): Validated[List[String], User] =
runValidations(user, validateEmail, validateAge)
def validateEmail(user: User): Validated[String, User] =
if (user.email contains "@") valid(user) else invalid("Must supply a valid email")
def validateAge(user: User): Validated[String, User] =
if (user.age > 18) valid(user) else invalid("Must be over 18")
}
sealed trait Validator {
def runValidations[S,T](obj: T, validations: ((T) => Validated[S,T])*): Validated[List[S], T] =
combine(obj, validations.toList.map(_.apply(obj)))
def combine[S,T](obj: T, validations: List[Validated[S, T]]): Validated[List[S], T] = {
val errors = validations.collect { case Invalid(error) => error }
if (errors.isEmpty) valid(obj) else invalid(errors)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment