Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

#59: ValidatingSalatDAO validates using a list of functions

  • Loading branch information...
commit 52f85253b01c04edd49d94ca64f5226a1c1c3fe2 1 parent 47d7f23
@rktoomey rktoomey authored
View
62 salat-core/src/main/scala/com/novus/salat/dao/ValidatingDAO.scala
@@ -3,7 +3,7 @@
*
* Module: salat-core
* Class: ValidatingDAO.scala
- * Last modified: 2012-12-04 17:16:30 EST
+ * Last modified: 2012-12-05 09:29:05 EST
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,47 +30,7 @@ import scala.Right
import com.novus.salat.Context
import com.mongodb
-object Validation {
- /** Example of a validation that does nothing but return success
- * @tparam T the object to validate
- * @return the input wrapped in the successful side of an Either[Throwable, T]
- */
- implicit def nil[T]: Validation[T] = new Validation[T] {
- def apply(x: T) = Right(x)
- }
-}
-
-/** Simple validation type class
- * @tparam T type to validate
- */
-trait Validation[T] {
- /** Simple validation method
- * @param x the input to validate
- * @return if successful, the input wrapped in Right; if unsuccessful, a Throwable of your choice wrapped in Left
- */
- def apply(x: T): Either[Throwable, T]
-}
-
-trait ChainedValidation[T] extends Validation[T] {
-
- def validationChain: Iterable[Validation[T]]
-
- require(validationChain.nonEmpty, "%s: validation chain must have at least one entry!".format(getClass.getName))
-
- /** Validation method that applies a chain of checks to the input object
- * @param x the input to validate
- * @return if successful, the input wrapped in Right; if unsuccessful, a Throwable of your choice wrapped in Left
- */
- def apply(x: T) = {
- val (failed, success) = validationChain.map(_.apply(x)).partition(_.isLeft)
- if (failed.nonEmpty) {
- Left(ValidationErrorChain(x, failed.map(_.left.get)))
- }
- else Right(success.last.right.get)
- }
-}
-
-case class ValidationErrorChain(t: Any, iter: Iterable[Throwable]) extends Error(
+case class ValidationError[T](t: T, iter: Iterable[Throwable]) extends Error(
"""
|VALIDATION FAILED
|
@@ -81,8 +41,11 @@ case class ValidationErrorChain(t: Any, iter: Iterable[Throwable]) extends Error
|%s
""".stripMargin.format(t, iter.map(_.getMessage).mkString("\n")))
-abstract class Validates[T: Validation] {
- def apply(x: T) = implicitly[Validation[T]].apply(x)
+abstract class Validates[T](validators: List[T => Either[Throwable, T]]) {
+ def apply(x: T) = validators.map(_.apply(x)).partition(_.isLeft) match {
+ case (Nil, passed) => Right(x)
+ case (failed, _) => Left(ValidationError(x, failed.map(_.left.get)))
+ }
}
case class MutilValidateError(ts: Traversable[Throwable]) extends Error(
@@ -94,14 +57,9 @@ case class MutilValidateError(ts: Traversable[Throwable]) extends Error(
abstract class ValidatingSalatDAO[ObjectType <: AnyRef, ID <: Any](override val collection: MongoCollection)(implicit mot: Manifest[ObjectType],
mid: Manifest[ID], ctx: Context) extends SalatDAO[ObjectType, ID](collection)(mot, mid, ctx) {
- implicit def validator: Validation[ObjectType]
-
- log.debug(
- """
- |%s
- """.stripMargin, description)
+ def validators: List[ObjectType => Either[Throwable, ObjectType]]
- object validates extends Validates[ObjectType]
+ object validates extends Validates[ObjectType](validators)
/** @param t instance of ObjectType
* @param wc write concern
@@ -145,7 +103,7 @@ abstract class ValidatingSalatDAO[ObjectType <: AnyRef, ID <: Any](override val
case Left(e) => throw e
}
- override lazy val description = "ValidatingSalatDAO[%s,%s](%s) using validator %s".format(mot.erasure.getSimpleName, mid.erasure.getSimpleName, collection.name, implicitly[Validation[ObjectType]].getClass.getName)
+ override lazy val description = "ValidatingSalatDAO[%s,%s](%s)".format(mot.erasure.getSimpleName, mid.erasure.getSimpleName, collection.name)
/** @param t object to save
* @param wc write concern
View
39 salat-core/src/test/scala/com/novus/salat/test/dao/AlphaDAO.scala
@@ -3,7 +3,7 @@
*
* Module: salat-core
* Class: AlphaDAO.scala
- * Last modified: 2012-12-04 13:14:23 EST
+ * Last modified: 2012-12-05 09:30:14 EST
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -115,45 +115,16 @@ object UserDAO extends SalatDAO[User, ObjectId](collection = MongoConnection()(S
object RoleDAO extends SalatDAO[Role, ObjectId](collection = MongoConnection()(SalatSpecDb)(RoleColl))
object ToValidate {
- object validator extends Validation[ToValidate] {
- def apply(x: ToValidate) = if (x.a < 0) {
- Left(NegativeA(x))
- }
- else if (x.b.isEmpty) {
- Left(EmptyB(x))
- }
- else Right(x)
- }
+
+ def notNegativeA(x: ToValidate): Either[Throwable, ToValidate] = if (x.a < 0) Left(NegativeA(x)) else Right(x)
+ def notEmptyB(x: ToValidate): Either[Throwable, ToValidate] = if (x.b.isEmpty) Left(EmptyB(x)) else Right(x)
case class NegativeA(x: ToValidate) extends Error("Expected x.a to be positive but got %d instead".format(x.a))
case class EmptyB(x: ToValidate) extends Error("Expected x.b to be non-empty but got %s instead".format(x))
}
-object ToValidateChain {
- object validator extends ChainedValidation[ToValidate] {
- object nonNegativeA extends Validation[ToValidate] {
- def apply(x: ToValidate) = if (x.a < 0) {
- Left(ToValidate.NegativeA(x))
- }
- else Right(x)
- }
- object nonEmptyB extends Validation[ToValidate] {
- def apply(x: ToValidate) = if (x.b.isEmpty) {
- Left(ToValidate.EmptyB(x))
- }
- else Right(x)
- }
-
- def validationChain = nonNegativeA :: nonEmptyB :: Nil
- }
-}
-
case class ToValidate(@Key("_id") id: ObjectId = new ObjectId, a: Int, b: String)
object SimpleValidationDAO extends ValidatingSalatDAO[ToValidate, ObjectId](MongoConnection()(SalatSpecDb)(ToValidateColl)) {
- implicit def validator = ToValidate.validator
-}
-
-object ChainedValidationDAO extends ValidatingSalatDAO[ToValidate, ObjectId](MongoConnection()(SalatSpecDb)(ToValidateColl)) {
- implicit def validator = ToValidateChain.validator
+ def validators = ToValidate.notNegativeA _ :: ToValidate.notEmptyB _ :: Nil
}
View
41 salat-core/src/test/scala/com/novus/salat/test/dao/ValidatingSalatDAOSpec.scala
@@ -3,7 +3,7 @@
*
* Module: salat-core
* Class: ValidatingSalatDAOSpec.scala
- * Last modified: 2012-12-04 17:17:43 EST
+ * Last modified: 2012-12-05 09:28:59 EST
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ package com.novus.salat.test.dao
import com.novus.salat.test.SalatSpec
import org.bson.types.ObjectId
-import com.novus.salat.dao.ValidationErrorChain
+import com.novus.salat.dao.ValidationError
class ValidatingSalatDAOSpec extends SalatSpec {
@@ -35,41 +35,24 @@ class ValidatingSalatDAOSpec extends SalatSpec {
"insert a valid object" in {
val _id = new ObjectId
val valid = ToValidate(id = _id, a = 1, b = "Hello")
- ToValidate.validator.apply(valid) must beRight(valid)
+ ToValidate.notNegativeA(valid) must beRight(valid)
+ ToValidate.notEmptyB(valid) must beRight(valid)
SimpleValidationDAO.insert(valid) must beSome(_id)
}
"throw an exception instead of inserting an invalid object" in {
val _id = new ObjectId
+
val invalid = ToValidate(id = _id, a = -1, b = "Hello")
- ToValidate.validator.apply(invalid) must beLeft(ToValidate.NegativeA(invalid))
- SimpleValidationDAO.insert(invalid) must throwA[ToValidate.NegativeA]
+ ToValidate.notNegativeA(invalid) must beLeft(ToValidate.NegativeA(invalid))
+ SimpleValidationDAO.validates.apply(invalid) must beLeft(ValidationError(invalid, ToValidate.NegativeA(invalid) :: Nil))
+ SimpleValidationDAO.insert(invalid) must throwA[ValidationError[ToValidate]]
SimpleValidationDAO.findOneById(_id) must beNone
+
val invalid2 = ToValidate(id = _id, a = 1, b = "")
- ToValidate.validator.apply(invalid2) must beLeft(ToValidate.EmptyB(invalid2))
- SimpleValidationDAO.insert(invalid2) must throwA[ToValidate.EmptyB]
+ ToValidate.notEmptyB(invalid2) must beLeft(ToValidate.EmptyB(invalid2))
+ SimpleValidationDAO.validates.apply(invalid2) must beLeft(ValidationError(invalid2, ToValidate.EmptyB(invalid2) :: Nil))
+ SimpleValidationDAO.insert(invalid2) must throwA[ValidationError[ToValidate]]
SimpleValidationDAO.findOneById(_id) must beNone
}
}
- "Validating DAO using a chained validator" should {
- "insert a valid object" in {
- val _id = new ObjectId
- val valid = ToValidate(id = _id, a = 1, b = "Hello")
- ToValidateChain.validator.apply(valid) must beRight(valid)
- ChainedValidationDAO.insert(valid) must beSome(_id)
- }
- "throw a validation chain exception containing a single validation failure" in {
- val _id = new ObjectId
- val invalid = ToValidate(id = _id, a = -1, b = "Hello")
- ToValidateChain.validator.apply(invalid) must beLeft(ValidationErrorChain(invalid, ToValidate.NegativeA(invalid) :: Nil))
- ChainedValidationDAO.insert(invalid) must throwA[ValidationErrorChain]
- ChainedValidationDAO.findOneById(_id) must beNone
- }
- "throw a validation chain exception containing multiple validation failures" in {
- val _id = new ObjectId
- val invalid2 = ToValidate(id = _id, a = -1, b = "")
- ToValidateChain.validator.apply(invalid2) must beLeft(ValidationErrorChain(invalid2, ToValidate.NegativeA(invalid2) :: ToValidate.EmptyB(invalid2) :: Nil))
- ChainedValidationDAO.insert(invalid2) must throwA[ValidationErrorChain]
- ChainedValidationDAO.findOneById(_id) must beNone
- }
- }
}
Please sign in to comment.
Something went wrong with that request. Please try again.