Skip to content

Instantly share code, notes, and snippets.

@teamon
Created August 21, 2012 22:09
Show Gist options
  • Save teamon/3419888 to your computer and use it in GitHub Desktop.
Save teamon/3419888 to your computer and use it in GitHub Desktop.
package play
import _root_.scalaz._
import _root_.scalaz.Scalaz._
import play.api.libs.concurrent.Promise
package object scalaz {
implicit val PromiseInstance = new Monad[Promise] {
// override def map[A,B](fa: Promise[A])(f: A => B) = fa.map(f)
def point[A](a: => A) = Promise.pure(a)
def bind[A,B](fa: Promise[A])(f: A => Promise[B]) = fa.flatMap(f)
}
import play.api.libs.json._
object json {
type VA[A] = ValidationNEL[String, A]
// type Writez[A] = Writes[A]
trait Readz[A] { def reads(js: JsValue): VA[A] }
def writez[A](f: A => List[(String, JsValue)]): Writes[A] = new Writes[A] {
def writes(a: A) = toJson(f(a).toMap)
}
def writezAs[A,B](f: A => B)(implicit wz: Writes[B]): Writes[A] = new Writes[A] {
def writes(a: A) = wz.writes(f(a))
}
implicit def sops(key: String) = new {
def <>[B:Writes](v: B) = (key, toJson(v))
}
def field[A](key: String, validators: (A => Option[String])*)(implicit readz: Readz[A]): JsValue => VA[A] = js => {
(js \ key) match {
case JsUndefined(error) => "Field '%s' not found".format(key).failure.toValidationNEL
case value => readz.reads(js).fold(
e => e.map(("Field '%s': ".format(key))+).failure,
s => validators.map(_(s)).flatten match {
case Nil => s.success
case x :: xs => NonEmptyList(x, xs:_*).failure
}
)
}
}
val nonEmptyString: String => Option[String] = s => if(s.trim == "") "must not be empty".some else none
def fieldOpt[A](key: String)(implicit readz: Readz[A]): JsValue => VA[Option[A]] = js => {
readz.reads(js \ key).fold(
e => none[A],
s => some(s)
).success
}
def toJson[A](a: A)(implicit wz: Writes[A]) = wz.writes(a)
def fromJson[A](js: JsValue)(implicit rz: Readz[A]) = rz.reads(js)
def readz2[A, T1:Readz, T2:Readz](k1: String, k2: String)(f: (T1,T2) => A): Readz[A] = new Readz[A]{
def reads(js: JsValue) = {
val data = for {
f1 <- field[T1](k1)
f2 <- field[T2](k2)
} yield (f1 |@| f2)(f)
data(js)
}
}
def writez1[A, T1:Writes](k1: String)(f: A => T1): Writes[A] = writez(a => {
val r = f(a)
List(k1 <> r)
})
def writez2[A, T1:Writes, T2:Writes](k1: String, k2: String)(f: A => (T1,T2)): Writes[A] = writez(a => {
val r = f(a)
List(k1 <> r._1, k2 <> r._2)
})
def writez3[A, T1:Writes, T2:Writes, T3:Writes](k1: String, k2: String, k3: String)(f: A => (T1,T2,T3)): Writes[A] = writez(a => {
val r = f(a)
List(k1 <> r._1, k2 <> r._2, k3 <> r._3)
})
def writez4[A, T1:Writes, T2:Writes, T3:Writes, T4:Writes](k1: String, k2: String, k3: String, k4: String)(f: A => (T1,T2,T3,T4)): Writes[A] = writez(a => {
val r = f(a)
List(k1 <> r._1, k2 <> r._2, k3 <> r._3, k4 <> r._4)
})
def writez5[A, T1:Writes, T2:Writes, T3:Writes, T4:Writes, T5:Writes](k1: String, k2: String, k3: String, k4: String, k5: String)(f: A => (T1,T2,T3,T4,T5)): Writes[A] = writez(a => {
val r = f(a)
List(k1 <> r._1, k2 <> r._2, k3 <> r._3, k4 <> r._4, k5 <> r._5)
})
def writez6[A, T1:Writes, T2:Writes, T3:Writes, T4:Writes, T5:Writes, T6:Writes](k1: String, k2: String, k3: String, k4: String, k5: String, k6: String)(f: A => (T1,T2,T3,T4,T5,T6)): Writes[A] = writez(a => {
val r = f(a)
List(k1 <> r._1, k2 <> r._2, k3 <> r._3, k4 <> r._4, k5 <> r._5, k6 <> r._6)
})
def writez7[A, T1:Writes, T2:Writes, T3:Writes, T4:Writes, T5:Writes, T6:Writes, T7:Writes](k1: String, k2: String, k3: String, k4: String, k5: String, k6: String, k7: String)(f: A => (T1,T2,T3,T4,T5,T6,T7)): Writes[A] = writez(a => {
val r = f(a)
List(k1 <> r._1, k2 <> r._2, k3 <> r._3, k4 <> r._4, k5 <> r._5, k6 <> r._6, k7 <> r._7)
})
def writez8[A, T1:Writes, T2:Writes, T3:Writes, T4:Writes, T5:Writes, T6:Writes, T7:Writes, T8:Writes](k1: String, k2: String, k3: String, k4: String, k5: String, k6: String, k7: String, k8: String)(f: A => (T1,T2,T3,T4,T5,T6,T7,T8)): Writes[A] = writez(a => {
val r = f(a)
List(k1 <> r._1, k2 <> r._2, k3 <> r._3, k4 <> r._4, k5 <> r._5, k6 <> r._6, k7 <> r._7, k8 <> r._8)
})
protected def SimpleReadz[A](f: JsValue => ValidationNEL[String, A]): Readz[A] = new Readz[A] {
def reads(js: JsValue) = f(js)
}
object Writes extends DefaultWrites
object Readz extends DefaultReadz
trait DefaultReadz {
implicit val ShortReadz = SimpleReadz[Short]({
case JsNumber(n) => n.toShort.success
case _ => "Number expected".failure.toValidationNEL
})
implicit val IntReadz = SimpleReadz[Int]({
case JsNumber(n) => n.toInt.success
case _ => "Number expected".failure.toValidationNEL
})
implicit val LongReadz = SimpleReadz[Long]({
case JsNumber(n) => n.toLong.success
case _ => "Number expected".failure.toValidationNEL
})
implicit val FloatReadz = SimpleReadz[Float]({
case JsNumber(n) => n.toFloat.success
case _ => "Number expected".failure.toValidationNEL
})
implicit val DoubleReadz = SimpleReadz[Double]({
case JsNumber(n) => n.toDouble.success
case _ => "Number expected".failure.toValidationNEL
})
implicit val BigDecimalReadz = SimpleReadz[BigDecimal]({
case JsNumber(n) => n.success
case _ => "Number expected".failure.toValidationNEL
})
implicit val BooleanReadz = SimpleReadz[Boolean]({
case JsBoolean(b) => b.success
case _ => "Boolean expected".failure.toValidationNEL
})
implicit val StringReadz = SimpleReadz[String]({
case JsString(s) => s.success
case _ => "String expected".failure.toValidationNEL
})
implicit def OptionReads[A](implicit rz: Readz[A]): Readz[Option[A]] = new Readz[Option[A]]{
def reads(js: JsValue) = rz.reads(js).fold(_ => none[A], some).success
}
// /**
// * Deserializer for Map[String,V] types.
// */
// implicit def mapReads[V](implicit fmtv: Reads[V]): Reads[collection.immutable.Map[String, V]] = new Reads[collection.immutable.Map[String, V]] {
// def reads(json: JsValue) = json match {
// case JsObject(m) => m.map { case (k, v) => (k -> fromJson[V](v)(fmtv)) }.toMap
// case _ => throw new RuntimeException("Map expected")
// }
// }
// /**
// * Generic deserializer for collections types.
// */
implicit def listReadz[A](implicit ra: Readz[A]) = new Readz[List[A]] {
def reads(json: JsValue) = json match {
case JsArray(ts) => ts.toList.map(fromJson[A]).sequence[VA, A]
case _ => "Collection expected".failure.toValidationNEL
}
}
// /**
// * Deserializer for Array[T] types.
// */
// implicit def arrayReads[T: Reads: Manifest]: Reads[Array[T]] = new Reads[Array[T]] {
// def reads(json: JsValue) = json.as[List[T]].toArray
// }
// /**
// * Deserializer for JsValue.
// */
// implicit object JsValueReads extends Reads[JsValue] {
// def reads(json: JsValue) = json
// }
// /**
// * Deserializer for JsObject.
// */
// implicit object JsObjectReads extends Reads[JsObject] {
// def reads(json: JsValue) = json match {
// case o: JsObject => o
// case _ => throw new RuntimeException("JsObject expected")
// }
// }
// implicit def OptionReads[T](implicit fmt: Reads[T]): Reads[Option[T]] = new Reads[Option[T]] {
// import scala.util.control.Exception._
// def reads(json: JsValue) = catching(classOf[RuntimeException]).opt(fmt.reads(json))
// }
}
implicit def ListStringJsValueWritez: Writes[List[(String, JsValue)]] = writezAs(list => list.toMap)
implicit def NonEmptyListWritez[A:Writes]: Writes[NonEmptyList[A]] = writezAs(_.list)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment