|
Play Framework/Scala example source code file (Binders.scala)
The Binders.scala Play Framework example source code
/*
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
*/
package play.api.mvc
import scala.annotation._
import play.api.mvc._
import controllers.Assets.Asset
import java.net.{ URI, URLEncoder }
import java.util.UUID
import scala.annotation._
import scala.collection.JavaConverters._
import reflect.ClassTag
/**
* Binder for query string parameters.
*
* You can provide an implementation of `QueryStringBindable[A]` for any type `A` you want to be able to
* bind directly from the request query string.
*
* For example, if you have the following type to encode pagination:
*
* {{{
* /**
* * @param index Current page index
* * @param size Number of items in a page
* */
* case class Pager(index: Int, size: Int)
* }}}
*
* Play will create a `Pager(5, 42)` value from a query string looking like `/foo?p.index=5&p.size=42` if you define
* an instance of `QueryStringBindable[Pager]` available in the implicit scope.
*
* For example:
*
* {{{
* object Pager {
* implicit def queryStringBinder(implicit intBinder: QueryStringBindable[Int]) = new QueryStringBindable[Pager] {
* override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Pager]] = {
* for {
* index <- intBinder.bind(key + ".index", params)
* size <- intBinder.bind(key + ".size", params)
* } yield {
* (index, size) match {
* case (Right(index), Right(size)) => Right(Pager(index, size))
* case _ => Left("Unable to bind a Pager")
* }
* }
* }
* override def unbind(key: String, pager: Pager): String = {
* intBinder.unbind(key + ".index", pager.index) + "&" + intBinder.unbind(key + ".size", pager.size)
* }
* }
* }
* }}}
*
* To use it in a route, just write a type annotation aside the parameter you want to bind:
*
* {{{
* GET /foo controllers.foo(p: Pager)
* }}}
*/
@implicitNotFound(
"No QueryString binder found for type ${A}. Try to implement an implicit QueryStringBindable for this type."
)
trait QueryStringBindable[A] {
self =>
/**
* Bind a query string parameter.
*
* @param key Parameter key
* @param params QueryString data
* @return `None` if the parameter was not present in the query string data. Otherwise, returns `Some` of either
* `Right` of the parameter value, or `Left` of an error message if the binding failed.
*/
def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, A]]
/**
* Unbind a query string parameter.
*
* @param key Parameter key
* @param value Parameter value.
* @return a query string fragment containing the key and its value. E.g. "foo=42"
*/
def unbind(key: String, value: A): String
/**
* Javascript function to unbind in the Javascript router.
*/
def javascriptUnbind: String = """function(k,v) {return encodeURIComponent(k)+'='+encodeURIComponent(v)}"""
/**
* Transform this QueryStringBindable[A] to QueryStringBindable[B]
*/
def transform[B](toB: A => B, toA: B => A) = new QueryStringBindable[B] {
def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, B]] = {
self.bind(key, params).map(_.right.map(toB))
}
def unbind(key: String, value: B): String = self.unbind(key, toA(value))
}
}
/**
* Binder for URL path parameters.
*
* You can provide an implementation of `PathBindable[A]` for any type `A` you want to be able to
* bind directly from the request path.
*
* For example, given this class definition:
*
* {{{
* case class User(id: Int, name: String, age: Int)
* }}}
*
* You can define a binder retrieving a `User` instance from its id, useable like the following:
*
* {{{
* // In your routes:
* // GET /show/:user controllers.Application.show(user)
* // For example: /show/42
*
* object Application extends Controller {
* def show(user: User) = Action {
* …
* }
* }
* }}}
*
* The definition the binder can look like the following:
*
* {{{
* object User {
* implicit def pathBinder(implicit intBinder: PathBindable[Int]) = new PathBindable[User] {
* override def bind(key: String, value: String): Either[String, User] = {
* for {
* id <- intBinder.bind(key, value).right
* user <- User.findById(id).toRight("User not found").right
* } yield user
* }
* override def unbind(key: String, user: User): String = {
* intBinder.unbind(user.id)
* }
* }
* }
* }}}
*/
@implicitNotFound(
"No URL path binder found for type ${A}. Try to implement an implicit PathBindable for this type."
)
trait PathBindable[A] {
self =>
/**
* Bind an URL path parameter.
*
* @param key Parameter key
* @param value The value as String (extracted from the URL path)
* @return `Right` of the value or `Left` of an error message if the binding failed
*/
def bind(key: String, value: String): Either[String, A]
/**
* Unbind a URL path parameter.
*
* @param key Parameter key
* @param value Parameter value.
*/
def unbind(key: String, value: A): String
/**
* Javascript function to unbind in the Javascript router.
*/
def javascriptUnbind: String = """function(k,v) {return v}"""
/**
* Transform this PathBinding[A] to PathBinding[B]
*/
def transform[B](toB: A => B, toA: B => A) = new PathBindable[B] {
def bind(key: String, value: String): Either[String, B] = self.bind(key, value).right.map(toB)
def unbind(key: String, value: B): String = self.unbind(key, toA(value))
}
}
/**
* Transform a value to a Javascript literal.
*/
@implicitNotFound(
"No JavaScript literal binder found for type ${A}. Try to implement an implicit JavascriptLiteral for this type."
)
trait JavascriptLiteral[A] {
/**
* Convert a value of A to a JavaScript literal.
*/
def to(value: A): String
}
/**
* Default JavaScript literals converters.
*/
object JavascriptLiteral {
/**
* Convert a Scala String to Javascript String
*/
implicit def literalString: JavascriptLiteral[String] = new JavascriptLiteral[String] {
def to(value: String) = "\"" + value + "\""
}
/**
* Convert a Scala Int to Javascript number
*/
implicit def literalInt: JavascriptLiteral[Int] = new JavascriptLiteral[Int] {
def to(value: Int) = value.toString
}
/**
* Convert a Java Integer to Javascript number
*/
implicit def literalJavaInteger: JavascriptLiteral[java.lang.Integer] = new JavascriptLiteral[java.lang.Integer] {
def to(value: java.lang.Integer) = value.toString
}
/**
* Convert a Scala Long to Javascript Long
*/
implicit def literalLong: JavascriptLiteral[Long] = new JavascriptLiteral[Long] {
def to(value: Long) = value.toString
}
/**
* Convert a Scala Boolean to Javascript boolean
*/
implicit def literalBoolean: JavascriptLiteral[Boolean] = new JavascriptLiteral[Boolean] {
def to(value: Boolean) = value.toString
}
/**
* Convert a Scala Option to Javascript literal (use null for None)
*/
implicit def literalOption[T](implicit jsl: JavascriptLiteral[T]): JavascriptLiteral[Option[T]] = new JavascriptLiteral[Option[T]] {
def to(value: Option[T]) = value.map(jsl.to(_)).getOrElse("null")
}
/**
* Convert a Play Asset to Javascript String
*/
implicit def literalAsset: JavascriptLiteral[Asset] = new JavascriptLiteral[Asset] {
def to(value: Asset) = "\"" + value.name + "\""
}
}
/**
* Default binders for Query String
*/
object QueryStringBindable {
class Parsing[A](parse: String => A, serialize: A => String, error: (String, Exception) => String)
extends QueryStringBindable[A] {
def bind(key: String, params: Map[String, Seq[String]]) = params.get(key).flatMap(_.headOption).map { p =>
try {
Right(parse(p))
} catch {
case e: Exception => Left(error(key, e))
}
}
def unbind(key: String, value: A) = key + "=" + serialize(value)
}
/**
* QueryString binder for String.
*/
implicit def bindableString = new QueryStringBindable[String] {
def bind(key: String, params: Map[String, Seq[String]]) = params.get(key).flatMap(_.headOption).map(Right(_)) // No need to URL decode from query string since netty already does that
// Use an option here in case users call index(null) in the routes -- see #818
def unbind(key: String, value: String) = key + "=" + URLEncoder.encode(Option(value).getOrElse(""), "utf-8")
}
/**
* QueryString binder for Int.
*/
implicit object bindableInt extends Parsing[Int](
_.toInt, _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as Int: %s".format(key, e.getMessage)
)
/**
* QueryString binder for Integer.
*/
implicit def bindableJavaInteger: QueryStringBindable[java.lang.Integer] =
bindableInt.transform(i => i, i => i)
/**
* QueryString binder for Long.
*/
implicit object bindableLong extends Parsing[Long](
_.toLong, _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as Long: %s".format(key, e.getMessage)
)
/**
* QueryString binder for Java Long.
*/
implicit def bindableJavaLong: QueryStringBindable[java.lang.Long] =
bindableLong.transform(l => l, l => l)
/**
* QueryString binder for Double.
*/
implicit object bindableDouble extends Parsing[Double](
_.toDouble, _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as Double: %s".format(key, e.getMessage)
)
/**
* QueryString binder for Java Double.
*/
implicit def bindableJavaDouble: QueryStringBindable[java.lang.Double] =
bindableDouble.transform(d => d, d => d)
/**
* QueryString binder for Float.
*/
implicit object bindableFloat extends Parsing[Float](
_.toFloat, _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as Float: %s".format(key, e.getMessage)
)
/**
* QueryString binder for Java Float.
*/
implicit def bindableJavaFloat: QueryStringBindable[java.lang.Float] =
bindableFloat.transform(f => f, f => f)
/**
* QueryString binder for Boolean.
*/
implicit object bindableBoolean extends Parsing[Boolean](
_.trim match {
case "true" => true
case "false" => false
case b => b.toInt match {
case 1 => true
case 0 => false
}
}, _.toString,
(key: String, e: Exception) => "Cannot parse parameter %s as Boolean: should be true, false, 0 or 1".format(key)
) {
override def javascriptUnbind = """function(k,v){return k+'='+(!!v)}"""
}
/**
* QueryString binder for Java Boolean.
*/
implicit def bindableJavaBoolean: QueryStringBindable[java.lang.Boolean] =
bindableBoolean.transform(b => b, b => b)
/**
* Path binder for java.util.UUID.
*/
implicit object bindableUUID extends Parsing[UUID](
UUID.fromString(_), _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as UUID: %s".format(key, e.getMessage)
)
/**
* QueryString binder for Option.
*/
implicit def bindableOption[T: QueryStringBindable] = new QueryStringBindable[Option[T]] {
def bind(key: String, params: Map[String, Seq[String]]) = {
Some(
implicitly[QueryStringBindable[T]].bind(key, params)
.map(_.right.map(Some(_)))
.getOrElse(Right(None)))
}
def unbind(key: String, value: Option[T]) = value.map(implicitly[QueryStringBindable[T]].unbind(key, _)).getOrElse("")
override def javascriptUnbind = javascriptUnbindOption(implicitly[QueryStringBindable[T]].javascriptUnbind)
}
/**
* QueryString binder for Java Option.
*/
implicit def bindableJavaOption[T: QueryStringBindable]: QueryStringBindable[play.libs.F.Option[T]] = new QueryStringBindable[play.libs.F.Option[T]] {
def bind(key: String, params: Map[String, Seq[String]]) = {
Some(
implicitly[QueryStringBindable[T]].bind(key, params)
.map(_.right.map(play.libs.F.Option.Some(_)))
.getOrElse(Right(play.libs.F.Option.None.asInstanceOf[play.libs.F.Option[T]])))
}
def unbind(key: String, value: play.libs.F.Option[T]) = {
if (value.isDefined) {
implicitly[QueryStringBindable[T]].unbind(key, value.get)
} else {
""
}
}
override def javascriptUnbind = javascriptUnbindOption(implicitly[QueryStringBindable[T]].javascriptUnbind)
}
private def javascriptUnbindOption(jsUnbindT: String) = "function(k,v){return v!=null?(" + jsUnbindT + ")(k,v):''}"
/**
* QueryString binder for List
*/
implicit def bindableList[T: QueryStringBindable]: QueryStringBindable[List[T]] = new QueryStringBindable[List[T]] {
def bind(key: String, params: Map[String, Seq[String]]) = Some(Right(bindList[T](key, params)))
def unbind(key: String, values: List[T]) = unbindList(key, values)
override def javascriptUnbind = javascriptUnbindList(implicitly[QueryStringBindable[T]].javascriptUnbind)
}
/**
* QueryString binder for java.util.List
*/
implicit def bindableJavaList[T: QueryStringBindable]: QueryStringBindable[java.util.List[T]] = new QueryStringBindable[java.util.List[T]] {
def bind(key: String, params: Map[String, Seq[String]]) = Some(Right(bindList[T](key, params).asJava))
def unbind(key: String, values: java.util.List[T]) = unbindList(key, values.asScala)
override def javascriptUnbind = javascriptUnbindList(implicitly[QueryStringBindable[T]].javascriptUnbind)
}
private def bindList[T: QueryStringBindable](key: String, params: Map[String, Seq[String]]): List[T] = {
for {
values <- params.get(key).toList
rawValue <- values
bound <- implicitly[QueryStringBindable[T]].bind(key, Map(key -> Seq(rawValue)))
value <- bound.right.toOption
} yield value
}
private def unbindList[T: QueryStringBindable](key: String, values: Iterable[T]): String = {
(for (value <- values) yield {
implicitly[QueryStringBindable[T]].unbind(key, value)
}).mkString("&")
}
private def javascriptUnbindList(jsUnbindT: String) = "function(k,vs){var l=vs&&vs.length,r=[],i=0;for(;i<l;i++){r[i]=(" + jsUnbindT + ")(k,vs[i])}return r.join('&')}"
/**
* QueryString binder for QueryStringBindable.
*/
implicit def javaQueryStringBindable[T <: play.mvc.QueryStringBindable[T]](implicit ct: ClassTag[T]) = new QueryStringBindable[T] {
def bind(key: String, params: Map[String, Seq[String]]) = {
try {
val o = ct.runtimeClass.newInstance.asInstanceOf[T].bind(key, params.mapValues(_.toArray).asJava)
if (o.isDefined) {
Some(Right(o.get))
} else {
None
}
} catch {
case e: Exception => Some(Left(e.getMessage))
}
}
def unbind(key: String, value: T) = {
value.unbind(key)
}
override def javascriptUnbind = Option(ct.runtimeClass.newInstance.asInstanceOf[T].javascriptUnbind())
.getOrElse(super.javascriptUnbind)
}
}
/**
* Default binders for URL path part.
*/
object PathBindable {
class Parsing[A](parse: String => A, serialize: A => String, error: (String, Exception) => String)(implicit codec: Codec)
extends PathBindable[A] {
def bind(key: String, value: String): Either[String, A] = {
try {
Right(parse(value))
} catch {
case e: Exception => Left(error(key, e))
}
}
def unbind(key: String, value: A): String = serialize(value)
}
/**
* Path binder for String.
*/
implicit object bindableString extends Parsing[String](
(s: String) => s, (s: String) => s, (key: String, e: Exception) => "Cannot parse parameter %s as String: %s".format(key, e.getMessage)
)
/**
* Path binder for Int.
*/
implicit object bindableInt extends Parsing[Int](
_.toInt, _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as Int: %s".format(key, e.getMessage)
)
/**
* Path binder for Java Integer.
*/
implicit def bindableJavaInteger: PathBindable[java.lang.Integer] =
bindableInt.transform(i => i, i => i)
/**
* Path binder for Long.
*/
implicit object bindableLong extends Parsing[Long](
_.toLong, _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as Long: %s".format(key, e.getMessage)
)
/**
* Path binder for Java Long.
*/
implicit def bindableJavaLong: PathBindable[java.lang.Long] =
bindableLong.transform(l => l, l => l)
/**
* Path binder for Double.
*/
implicit object bindableDouble extends Parsing[Double](
_.toDouble, _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as Double: %s".format(key, e.getMessage)
)
/**
* Path binder for Java Double.
*/
implicit def bindableJavaDouble: PathBindable[java.lang.Double] =
bindableDouble.transform(d => d, d => d)
/**
* Path binder for Float.
*/
implicit object bindableFloat extends Parsing[Float](
_.toFloat, _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as Float: %s".format(key, e.getMessage)
)
/**
* Path binder for Java Float.
*/
implicit def bindableJavaFloat: PathBindable[java.lang.Float] =
bindableFloat.transform(f => f, f => f)
/**
* Path binder for Boolean.
*/
implicit object bindableBoolean extends Parsing[Boolean](
_.trim match {
case "true" => true
case "false" => false
case b => b.toInt match {
case 1 => true
case 0 => false
}
}, _.toString,
(key: String, e: Exception) => "Cannot parse parameter %s as Boolean: should be true, false, 0 or 1".format(key)
) {
override def javascriptUnbind = """function(k,v){return !!v}"""
}
/**
* Path binder for Java Boolean.
*/
implicit def bindableJavaBoolean: PathBindable[java.lang.Boolean] =
bindableBoolean.transform(b => b, b => b)
/**
* Path binder for java.util.UUID.
*/
implicit object bindableUUID extends Parsing[UUID](
UUID.fromString(_), _.toString, (key: String, e: Exception) => "Cannot parse parameter %s as UUID: %s".format(key, e.getMessage)
)
/**
* Path binder for Java PathBindable
*/
implicit def javaPathBindable[T <: play.mvc.PathBindable[T]](implicit ct: ClassTag[T]) = new PathBindable[T] {
def bind(key: String, value: String) = {
try {
Right(ct.runtimeClass.newInstance.asInstanceOf[T].bind(key, value))
} catch {
case e: Exception => Left(e.getMessage)
}
}
def unbind(key: String, value: T) = {
value.unbind(key)
}
override def javascriptUnbind = Option(ct.runtimeClass.newInstance.asInstanceOf[T].javascriptUnbind())
.getOrElse(super.javascriptUnbind)
}
}
Other Play Framework source code examplesHere is a short list of links related to this Play Framework Binders.scala source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.