Validator


Readable and Customizable DSL


skinny-validator is portable, so you can use skinny-validator with Play2, Scalatra and any other web app frameworks. Furthermore, you can use it in not only web apps but also any other applications (batch operation, cli and so on).

You can see simple usage of skinny-validator here:

validator/src/test/scala/UsageSpec.scala


Creating New Validation Rule


The way to create new validation rule is pretty simple:

That’s all.

The following is an example from the built-in validations.

import skinny.validator._

object required extends required(true)

case class required(trim: Boolean = true) extends ValidationRule {
  def name = "required"
  def isValid(v: Any) = !isEmpty(v) && {
    if (trim) v.toString.trim.length > 0
    else v.toString.length > 0
  }
}

Then you can use it like this:

val validator = Validator(
  param("name" -> null) is required
)
validator.hasErrors // true

val errorsForName: Seq[Error] = validator.errors.get("name")
val error: Error = errorsForName.head
error.name // -> "required"
error.messageParams // -> List("name")

A validation rule which accepts value when using such as minLength(6) is like this:

case class minLength(min: Int) extends ValidationRule {
  def name = "minLength"
  override def messageParams = Seq(min.toString)

  def isValid(v: Any) = isEmpty(v) || {
    toHasSize(v).map(_.size >= min).getOrElse(v.toString.length >= min)
  }
}

val validator = Validator(
  param("name" -> "xxxx") is checkAll(required, minLength(6)),
  param("password" -> "xxxx") is required & minLength(6)
)

Basic usage involves combining validation rules with &. If a failure is found, by default the rest of the validation rules will be skipped. checkAll executes all the validations even if some of them already failed.


Built-in Validation Rules


The built-in validators are good enough to cover most common cases.

validator/src/main/scala/skinny/validator/BuiltinValidationRules.scala

validator/src/test/scala/skinny/validator


Validator & MapValidator


skinny-validator provides Validator and MapValidator. You already see Validator in above code. This one accepts parameters when defining validation rules.

MapValidator is another one which accepts Map[String, Any] object as parameters. Usually MapValidator is more useful in web applications.

val params = Map("name" -> "Alice", "age" -> 20)

val validator = MapValidator(params)(
  paramKey("name") is required,
  paramKey("age") is required & numeric
)

Error Messages


skinny-validator’s Error looks like this:

val error = validator.errors.head
error.name // -> required
error.messageParams // -> List("name")

skinny.validator.Messages loads error messages from *.conf or *.properties.

This is src/main/resources/messages.conf example:

error {
  required="{0} is required"
  minLength="{0} length must be greater than or equal to {1}"
}

Now Messages can load error messages for validation errors named “required” or “minLength”. The name comes from ValidationRule#name.

val messages = Messages.loadFromConfig()
// val messages = Messages.loadFromConfig("messages2") // loads messages2.conf
// val messages = Messages.loadFromProperties() // loads messages.properties
// val messages_ja = Messages.loadFromConfig(locale = Option(locale)) // loads messages_ja.conf

messages.get("required", Seq("name")) 
// -> Some("name is required")

messages.get("minLength", Seq("password", 6)) 
// -> Some("password length must be greater than or equal to 6")

How It Works in Skinny apps


You can easily understand how skinny-validator works in Skinny apps.

framework/src/main/scala/skinny/controller/feature/ValidationFeature.scala

The following is an example with Skinny Framework:

// withDate joins birthdayYear, birthdayMonth and birthdayDay into single birthday
def createParams = Params(params).withDate("birthday")

def createForm = validation(createParams,
  paramKey("name") is checkAll(minLength(5), maxLength(20)),
  paramKey("birthday") is required & dateFormat,
  paramKey("point") is numeric
)

def createStrongParameters = Seq(
  "name" -> ParamType.String,
  "birthday" -> ParamType.LocalDate,
  "point" -> ParamType.Int
)

def create = {
  if (createForm.validate) {
    val id = Member.createNewModel(createParams.permit(createStrongParameters))
    redirect(s"/members/${id}")
  } else {
    // error messages is set as "errorMessages" and "keyAndErrorMessages"
    render("/members/new")
  }
  // or using #fold
}

See also:

common/src/main/scala/skinny/ParamType.scala

common/src/main/scala/skinny/StrongParameters.scala

If you find a typo or mistake in this page, please report or fix it. How?