alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Play Framework/Scala example source code file (Binders.scala)

This example Play Framework source code file (Binders.scala) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Play Framework (and Scala) source code examples by using tags.

All credit for the original source code belongs to Play Framework; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Play Framework tags/keywords

a, annotation, api, b, cannot, exception, javascriptliteral, map, mvc, parsing, play, play framework, querystringbindable, seq, string

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 examples

Here 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

 

new blog posts

 

Copyright 1998-2021 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.