Skip to main content

Validated

Defines Validated capabilities for V[_], so that Fields know how to use it when validating. Also this typeclass has strategy field that will give a hint for short-circuiting while validating.

Instances​

Predefined instances:

  • jap.fields.data.Accumulate - accumulates errors.
  • jap.fields.data.FailFast - holds first error that occured. Built using Either
  • cats.data.ValidatedNel[_, Unit] - accumulates. Part of cats module
  • cats.data.ValidatedNec[_, Unit] - accumulates. Part of cats module
  • List - accumulates errors. If list is empty means result is valid else contains errors.
  • Either[_, Unit] - fail fast error. Right[Unit] is valid, Left[E] is invalid holding error.
  • Option - fail fast error. None is valid, Some[E] is invalid holding error

If you need you can use your own Validated data type by creating typeclass instance for it. Extend AccumulateLike if your type should accumulate errors or if it should fail with first error occured use FailFastLike

Accumulate​

Validated data type that accumulates errors.

sealed trait Accumulate[+E]
object Accumulate {
case object Valid extends Accumulate[Nothing]
case class Invalid[+E](errors: List[E]) extends Accumulate[E]
}

FailFast​

Validated data type that returns first error occured. Declared as Option[E] tagged type.

  • None is valid
  • Some[E] holds the error

Syntax​

Having Validated for your V[_] in scope you can use such syntax

Create​

import jap.fields._
import jap.fields.data.Accumulate
import jap.fields.typeclass.Validated
import jap.fields.syntax.ValidatedSyntax._

val V: Validated[Accumulate] = Accumulate
// V: Validated[[E >: Nothing <: Any] => Accumulate[E]] = jap.fields.data.Accumulate$@2fab6544
val vr1 = V.valid
// vr1: Accumulate[Nothing] = Valid
val vr2 = V.invalid("ERR01")
// vr2: Accumulate[String] = Invalid(errors = List("ERR01"))
val vr3 = "ERR02".invalid[Accumulate]
// vr3: Accumulate[String] = Invalid(errors = List("ERR02"))
val vr4 = V.traverse(List("ERR01", "ERR02"))(V.invalid)
// vr4: Accumulate[String] = Invalid(errors = List("ERR01", "ERR02"))
V.sequence(List(vr1, vr2, vr3))
// res0: Accumulate[String] = Invalid(errors = List("ERR01", "ERR02"))

Operations​

vr1.isValid
// res1: Boolean = true
vr2.when(false)
// res2: Accumulate[String] = Valid
vr2.unless(true)
// res3: Accumulate[String] = Valid
vr2.asError("ERROR02")
// res4: Accumulate[String] = Invalid(errors = List("ERROR02"))
vr2.asInvalid(vr4)
// res5: Accumulate[String] = Invalid(errors = List("ERR01", "ERR02"))
vr2.isInvalid
// res6: Boolean = true
vr2.errors
// res7: List[String] = List("ERR01")
vr1 && vr2
// res8: Accumulate[String] = Invalid(errors = List("ERR01"))
vr2.and(vr3)
// res9: Accumulate[String] = Invalid(errors = List("ERR01", "ERR02"))
vr1 || vr2
// res10: Accumulate[String] = Valid
vr2.or(vr3)
// res11: Accumulate[String] = Invalid(errors = List("ERR01", "ERR02"))
List(vr1, vr2, vr3).sequence
// res12: Accumulate[String] = Invalid(errors = List("ERR01", "ERR02"))
List(vr1, vr1).sequence
// res13: Accumulate[Nothing] = Valid

Fail Multiple Fields​

V.traverse is very useful when you want to fail multiple Field`s with same error

import jap.fields.DefaultAccumulateVM._
V.traverse(Field(FieldPath("1"), 1), Field(FieldPath("2"), 2))(_.failMessage("ERROR"))