octopus
octopus copied to clipboard
How to write rule for validating collections?
case class Customer(id: Int, name: String)
case class CustomerCollection(customers: Vector[Customer])
implicit val customerValidator: AsyncValidator[CustomerCollection] =
AsyncValidator[CustomerCollection]
.async.rule(_.customers, ....)
That's the point - you don't need to write rules for collections manually.
import octopus.dsl._
import octopus.syntax._
case class Customer(id: Int, name: String)
case class CustomerCollection(customers: Vector[Customer])
implicit val cutsomerValidator: Validator[Customer] = Validator[Customer]
.rule(_.name, (_: String).trim.nonEmpty, "name must not be empty")
val customers = CustomerCollection(Vector(Customer(1, "C1"), Customer(2, " "), Customer(3, "")))
customers.validate
// invalid:
// customers[1].name: name must not be empty
// customers[2].name: name must not be empty
Octopus will derive rules for collection automatically!
However sometimes you may want to additionally restrict collection itself. In this case you can define your own rules and compose with derived ones:
implicit val customerCollectionValidator: Validator[CustomerCollection] = Validator[CustomerCollection]
.derived
.rule(_.customers, (_: Vector[Customer]).length % 2 == 0, "number of customers must be even")
customers.validate
// invalid:
// customers[1].name: name must not be empty
// customers[2].name: name must not be empty
// customers: number of customers must be even
Intentionally this should work equally well for asynchronous validators.
@krzemin This works as well but how do I combine the collection validator to the actual class validator as in my original example:
case class Customer(id: Int, name: String)
case class CustomerCollection(customers: Vector[Customer])
implicit val customerValidator: AsyncValidator[CustomerCollection] =
AsyncValidator[CustomerCollection]
.async.rule(_.customers, ....)
the advantage I would get is all the validation result would be combined
I'm not sure if I got your question right. If you mark your rule for Customer
as implicit
, validation results for the whole collection would be automatically combined by the library (as in the example). In case this is not what you want to achieve, can you write more precisely what kind of result composing would you expect? Perhaps providing a type signature or code with example values and expected semantics.
I think I misunderstood. If it combines automatically due to implicit then that should be good enough. I will try it out. Thanks.
@Ibrahim-Islam can we close this?
Hello,
I'm using version 0.4.1, which seems to be the latest. I have tried the example above like this:
test("Customer collectionsexample") {
import octopus.dsl._
import octopus.syntax._
case class Customer(id: Int, name: String)
case class CustomerCollection(customers: Vector[Customer])
implicit val customerValidator: Validator[Customer] = Validator[Customer]
.rule(_.name, (_: String).trim.nonEmpty, "name must not be empty")
implicit val customerCollectionValidator: Validator[CustomerCollection] = Validator
.derived
.rule(_.customers, (_: Vector[Customer]).length % 2 == 0, "number of customers must be even")
val customers = CustomerCollection(Vector(Customer(1, "C1"), Customer(2, " "), Customer(3, "")))
println(customers.validate)
}
And the test can not compile with the following error:
value derived is not a member of octopus.Validator[CustomerCollection]
possible cause: maybe a semicolon is missing before `value derived`?
.derived
Do I missed something ?
Notice also that when I remove the customerCollection validator, it works fine. But I need to introduce validation items at collection level
It seems that works better like this:
implicit val customerCollectionValidator: Validator[CustomerCollection] = Validator[CustomerCollection]
.rule(_.customers, (_: Vector[Customer]).length % 2 == 0, "number of customers must be even")
.composeDerived