Preparation

If you haven't already done so in the last chapter, import the svalidate classes:

scala> import svalidate._
import svalidate._

The library is built for (but not limited to) case classes - so lets define one for our user data:

scala> case class UserData(id: Int, email: String, firstName: String, lastName: String)
defined class UserData

It's not actually a requirement to use a case class, but it makes most sense in many situations.

Svalidate comes with a few built-in validations that are fairly common. A common example is the NonEmptyValidator:

Creating a form

scala> import svalidate.validators.NonEmptyString
import svalidate.validators.NonEmptyString

Let's use the NonEmptyString validator to make sure that the first name and last name are not empty:

scala> val nameValidation = form[UserData](
     |   Validation("firstName", _.firstName, NonEmptyString),
     |   Validation("lastName",  _.lastName,  NonEmptyString)
     | )
nameValidation: UserData => scala.collection.immutable.Map[String,Seq[String]] = <function1>

In the above example we create a so-called form we can use it many times to validate a UserData. We passed 2 Validation objects to the form: A validation is not more than a case class. It takes 3 arguments:

  • label The label: Validation's errors will be collected under this label. This doesn't have to be the same as the field name.
  • get The getter to get the value from the case class. In the first example, we return the property firstName of the UserData. It's type is UserData => String.
  • validator An implementation of the Validator[T] trait. The type of the Validator has to match the type we return in the getter.

So, for example if we want to validate the firstName we return a String, which can be validated using the NonEmptyString validator (because it extends Validator[String]).

Using a form to validate an object

Now that we created the validation, we can try it on an object instance.

scala> val jSparrow = UserData(1, "jack@blackpearl.sea", "Jack", "Sparrow")
jSparrow: UserData = UserData(1,jack@blackpearl.sea,Jack,Sparrow)

scala> val res1 = nameValidation(jSparrow)
res1: scala.collection.immutable.Map[String,Seq[String]] = Map()

That was boring - lets see what happens when we violate some rules:

scala> val invalidUserData = UserData(0, "", "", "")
invalidUserData: UserData = UserData(0,,,)

scala> val res2 = nameValidation(invalidUserData)
res2: scala.collection.immutable.Map[String,Seq[String]] = Map(firstName -> List(The field %s must not be empty), lastName -> List(The field %s must not be empty))

Lets see what we got:

Map(
  "firstName" -> List("The field %s must not be empty"),
  "lastName"  -> List("The field %s must not be empty")
)

So we get back a Map[String, List[String]] with the errors grouped by the label. To check if the validation succeeded, we can use the usual methods of Map:

scala> if(res2.isEmpty) {
     |   println("The user data is valid")
     | } else {
     |   println("Invalid user data:")
     |   res2.foreach { case (label, errors) =>
     |     println(s"$label:")
     |     errors.foreach { error =>
     |       println(error.format(label))
     |     }
     |   }
     | }
Invalid user data:
firstName:
The field firstName must not be empty
lastName:
The field lastName must not be empty