Skip to content

Instantly share code, notes, and snippets.

@takashima0411
Created October 30, 2018 08:06
Show Gist options
  • Save takashima0411/6e60b27665e4e5a4efad38a9604f6394 to your computer and use it in GitHub Desktop.
Save takashima0411/6e60b27665e4e5a4efad38a9604f6394 to your computer and use it in GitHub Desktop.
import cats.data.Validated.{Invalid, Valid}
import cats.data._
import cats.implicits._
import play.api.data.Forms._
import play.api.data._
import play.api.data.validation.Constraint
case class DomainObject(value1: String, value2: String)
object DomainObject {
val form = Form(
mapping(
"value1" -> nonEmptyText,
"value2" -> nonEmptyText
)(DomainObject.apply)(DomainObject.unapply)
)
def apply(value1: String, value2: String): Validated[NonEmptyChain[(String, String)], DomainObject] =
if (value1 == value2) new DomainObject(value1, value2).validNec
else ("conversionTypeValue", "must same value").invalidNec
def mapping[R, A1, A2](a1: (String, Mapping[A1]), a2: (String, Mapping[A2]))(
apply: Function2[A1, A2, Validated[NonEmptyChain[(String, String)], R]]
)(unapply: Function1[R, Option[(A1, A2)]]): Mapping[R] = {
new ObjectMapping2(apply, unapply, a1, a2)
}
class ObjectMapping2[R, A1, A2](apply: Function2[A1, A2, Validated[NonEmptyChain[(String, String)], R]],
unapply: Function1[R, Option[(A1, A2)]],
f1: (String, Mapping[A1]),
f2: (String, Mapping[A2]),
val key: String = "",
val constraints: Seq[Constraint[R]] = Nil)
extends Mapping[R]
with ObjectMapping {
val field1 = f1._2.withPrefix(f1._1).withPrefix(key)
val field2 = f2._2.withPrefix(f2._1).withPrefix(key)
def bind(data: Map[String, String]): Either[Seq[FormError], R] = {
merge(field1.bind(data), field2.bind(data)) match {
case Left(errors) => Left(errors)
case Right(values) =>
apply(values(0).asInstanceOf[A1], values(1).asInstanceOf[A2]) match {
case Valid(r) => applyConstraints(r)
case Invalid(ve) => Left(ve.map(e => FormError(e._1, e._2)).toList)
}
}
}
def unbind(value: R): Map[String, String] = {
unapply(value)
.map { fields =>
val (v1, v2) = fields
field1.unbind(v1) ++ field2.unbind(v2)
}
.getOrElse(Map.empty)
}
def unbindAndValidate(value: R): (Map[String, String], Seq[FormError]) = {
unapply(value)
.map { fields =>
val (v1, v2) = fields
val a1 = field1.unbindAndValidate(v1)
val a2 = field2.unbindAndValidate(v2)
(a1._1 ++ a2._1) ->
(a1._2 ++ a2._2)
}
.getOrElse(Map.empty[String, String] -> Seq(FormError(key, "unbind.failed")))
}
def withPrefix(prefix: String): ObjectMapping2[R, A1, A2] =
addPrefix(prefix).map(newKey => new ObjectMapping2(apply, unapply, f1, f2, newKey, constraints)).getOrElse(this)
def verifying(addConstraints: Constraint[R]*): ObjectMapping2[R, A1, A2] = {
new ObjectMapping2(apply, unapply, f1, f2, key, constraints ++ addConstraints.toSeq)
}
val mappings = Seq(this) ++ field1.mappings ++ field2.mappings
}
}
object Main {
def main(args: Array[String]): Unit = {
val invalidForm = DomainObject.form.bind(Map("value1" -> "value1", "value2" -> "value2"))
println(invalidForm.hasErrors)
println(invalidForm.errors)
val validForm = DomainObject.form.bind(Map("value1" -> "value", "value2" -> "value"))
println(validForm.hasErrors)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment