
com.github.vickumar1981.svalidate.package.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of svalidate_2.11 Show documentation
Show all versions of svalidate_2.11 Show documentation
Simple and easy validations for Scala and Java
The newest version!
package com.github.vickumar1981
/** Provides classes for doing simple validations in Java and Scala.
*
* ==Overview==
* The validation wrapper is provided as a simple wrapper around a list
*
* Results of a validation are stored in a [[com.github.vickumar1981.svalidate.ValidationResult]]
* A [[com.github.vickumar1981.svalidate.ValidationResult]] can be chained to make rules
* using the syntax provided from [[com.github.vickumar1981.svalidate.ValidationDsl]].
*
* A validator for a class is defined by implicitly
* implementing a [[com.github.vickumar1981.svalidate.ValidatableResult]]
*
* Importing [[com.github.vickumar1981.svalidate.util.ValidationSyntax]]._ will add validate
* and validateWith methods to a class if a validator for that class is implicitly defined.
*
* Analogous classes for Java can be found in the [[com.github.vickumar1981.svalidate.util]] package
*
*
* | Class | Description |
* | :--- | :--- |
* | [[com.github.vickumar1981.svalidate.ValidationResult]] | Holds the return value from a validation |
* | [[com.github.vickumar1981.svalidate.ValidationDsl]] | Provides DSL syntax for validations |
* | [[com.github.vickumar1981.svalidate.ValidatableResult]] | Interface to implement for defining a validation |
*
*/
package object svalidate {
/**
* A generic interface that holds a validation result of type T, interoperable with Seq[T]
* @tparam T the type of ValidationResult
*/
sealed trait ValidationResult[+T] {
/**
* List of errors from the validation
* @return the list of errors from the validation result
*/
def errors: Seq[T]
/**
* Check if the validation succeeded
* @return if the validation succeded
*/
def isSuccess: Boolean = errors.isEmpty
/**
* Check if the validation failed
* @return if the validation failed
*/
def isFailure: Boolean = !isSuccess
}
/**
* A case class that represents a validation success of type T. Extends a [[ValidationResult]][T] and
* implements the errors function with an empty list
*
* A [[ValidationSuccess]][T] can be created by using the success method in the [[Validation]] object
*
* {{{
* val success: ValidationSuccess[Int] = Validation.success[Int]
* }}}
* @tparam T the type of ValidationResult
*/
case class ValidationSuccess[+T]() extends ValidationResult[T] {
/**
* Returns an empty list
*/
override val errors: Seq[T] = Seq.empty
}
/**
* A case class that represents a validation failure of type T. Extends a [[ValidationResult]][T] and
* implements errors with the list of errors passed in
*
* A [[ValidationFailure]][T] can be created by using the fail mathod in the [[Validation]] object
*
* {{{
* val failed: ValidationFailure[String] = Validation.fail("validation failed.")
* }}}
* @tparam T the type of [[ValidationResult]]
*/
case class ValidationFailure[+T](errorList: Seq[T]) extends ValidationResult[T] {
/**
* Returns the list passed in the contstructor
*/
override val errors: Seq[T] = errorList
}
/**
* Implicit conversion from [[ValidationResult]][T] to Seq[T]. Allows [[ValidationResult]][T] to be
* substituted for Seq[T]
* @param v the input [[ValidationResult]][T]
* @tparam T the type of [[ValidatableResult]]
* @return a Seq[T] returned the errors of the input validation
*/
implicit def validationToSeq[T](v: ValidationResult[T]): Seq[T] = v.errors
/**
* Implicit conversion from Seq[T] to [[ValidationResult]][T]. Allows Seq[T] to be substituted
* for [[ValidationResult]][T]
* @param seq the input Seq
* @tparam T the type of [[ValidationResult]]
* @return a [[ValidationFailure]] containing errors from the input Seq
*/
implicit def seqToValidation[T](seq: Seq[T]): ValidationResult[T] =
if (seq.isEmpty) ValidationSuccess() else ValidationFailure(seq)
/**
* Validation is the same as [[ValidationResult]][String].
* Allows the default return type to be a list of strings
*/
type Validation = ValidationResult[String]
/**
* Companion object containing factory methods to create a [[ValidationSuccess]][T] or
* a [[ValidationFailure]][T]
*
* {{{
* val validationSuccess = Validation.success[Int]
* val validationFailure = Validation.fail("The validation failed.")
* }}}
*/
object Validation {
/**
* Creates a validation failure from a list of T
* @param validationErrors a list of type T
* @tparam T the type of [[ValidationFailure]] to create
* @return a [[ValidationFailure]][T] containing the list of errors
*/
def fail[T](validationErrors: T*): ValidationResult[T] = ValidationFailure[T](validationErrors.toSeq)
/**
* Creates a validation success of type T
* @tparam T the type of success to create
* @return a [[ValidationSuccess]][T]
*/
def success[T]: ValidationResult[T] = ValidationSuccess[T]()
}
/**
* A generic interface that defines which class to validate and what type of validation
* to return. Creating an implicit that extends this class creates a validator for that class
*
* {{{
* object TestValidation {
* case class Password(password: String)
* case class CustomError(errorCode: String, errorMessage: String)
*
* implicit object PasswordValidator extends ValidatableResult[Password, CustomError] {
* override def validate(value: Password): Validation =
* (value.nonEmpty orElse CustomError("password.empty", "Password cannot be empty"))
* }
* }
* }}}
*
* @tparam A the class to validate
* @tparam B the type of [[ValidationResult]] to use with [[ValidationDsl]]
*/
trait ValidatableResult[-A, B] extends ValidationDsl[B] {
/**
* Override this class to validate type A using a [[ValidationResult]][B]
* @param value
* @return
*/
def validate(value: A): ValidationResult[B] = Validation.success
}
/**
* A generic interface that defines which class to use validateWith and what type of validation to return
* @tparam A the class to validate
* @tparam B the class to pass into validateWith
* @tparam C the type of [[ValidationResult]] to use with [[ValidationDsl]]
*/
trait ValidatableWithResult[-A, B, C] extends ValidationDsl[C] {
/**
*
* @param value the value to validate
* @param b an additional value to validate with
* @return a [[ValidationResult]] of type C
*/
def validateWith(value: A, b: B): ValidationResult[C] = Validation.success
}
/**
* A [[Validatable]][A] extends [[ValidatableResult]][A][String] and using it defaults the [[ValidationDsl]]
* to use a String return type. This can be changed by implementing [[ValidatableResult]][A][B] directly
* where B is return type instead.
*
* {{{
* object TestValidation {
* case class Address(street: String, city: String, state: String, zipCode: String)
*
* implicit object AddressValidator extends Validatable[Address] {
* override def validate(value: Address): Validation = {
* (value.street.nonEmpty orElse "Street addr. is required") ++
* (value.city.nonEmpty orElse "City is required") ++
* (value.zipCode.matches("\\d{5}") orElse "Zip code must be 5 digits") ++
* (value.state.matches("[A-Z]{2}") orElse "State abbr must be 2 letters")
* }
* }
* }
* }}}
*
* @tparam A the class to validate
*/
trait Validatable[-A] extends ValidatableResult[A, String]
/**
* A [[ValidatableWith]][A][B] extends [[ValidatableWithResult]][A][B][String] and using it defaults
* the DSL to use a String return type. This can be changed by implementing [[ValidatableWithResult]][A][B]
* directly where B is return type instead.
*
* {{{
* object TestValidation {
* case class Contacts(facebook: Option[List[String]] = None, twitter: Option[List[String]] = None)
* case class ContactSettings(hasFacebookContacts: Option[Boolean] = Some(true),
* hasTwitterContacts: Option[Boolean] = Some(true))
*
* implicit object ContactInfoValidator extends ValidatableWith[Contacts, ContactSettings] {
* override def validateWith(value: Contacts, contactSettings: ContactSettings): Validation =
* Validation.success
* }
* }
* }}}
*
* @tparam A the class to validate
*/
trait ValidatableWith[-A, B] extends ValidatableWithResult[A, B, String]
/**
* Importing [[ValidationSyntax]] extends validate and validateWith methods to a class A if an implicit
* [[ValidatableResult]][A][_] or [[ValidatableWithResult]][A][_][_] is defined for that class.
*
* Import this object alongside the validator to add methods to a class
*
* {{{
* import test.example.Address
*
* object TestValidation {
* import test.example.ModelValidations.AddressValidator
* import com.github.vickumar1981.svalidate.ValidationSyntax._
*
* def main(args: Array[String]): Unit = {
* val addr = Address("", "", "", "")
* val errors = addr.validate()
* // Calls AddressValidator.validate()
* // Depending on the validator, a class can have
* // .validate(), .validate(b: B), or both functions extended to it
* }
* }
* }}}
*
* Because this object contains implicits, it is suggested to limit the scope of the import.
*/
object ValidationSyntax {
/**
* Adds a validate method to any class if an implicit validator is in scope
* @param value the input value being validated
* @param v the implicit validator
* @tparam A the type of value
* @tparam B the return type of the [[ValidationResult]]
*/
implicit class ValidatableOps[+A, B](value: A)(implicit v: ValidatableResult[A, B]) {
/**
* Calls the validate method from the implicit validator
* @return a [[ValidationResult]][B] from calling the validator
*/
def validate(): ValidationResult[B] = v.validate(value)
}
/**
* Adds a validateWith method to any class if an implicit validator is in scope
* @param value the input value being validated
* @param v the implicit validator that has a validateWith
* @tparam A the type of the value
* @tparam B the type of the parameter to validateWith
* @tparam C the return type of the [[ValidationResult]]
*/
implicit class ValidatableWithOps[+A, B, C](value: A)(
implicit v: ValidatableWithResult[A, B, C]) {
def validateWith(b: B): ValidationResult[C] = v.validateWith(value, b)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy