Skip to content

Instantly share code, notes, and snippets.

@leon
Created May 17, 2013 09:01
Show Gist options
  • Save leon/5597895 to your computer and use it in GitHub Desktop.
Save leon/5597895 to your computer and use it in GitHub Desktop.
MongoResourceController, simplifying working with restful data using Reactive Mongo
package controllers
import play.api.mvc._
import play.api.libs.json._
import reactivemongo.bson._
import models._
import extensions._
import extensions.JsonTransforms._
import scala.concurrent.ExecutionContext.Implicits.global
class MongoResourceController[R](implicit format: Format[R], collection: MongoCollection[R]) extends ResourceRouter[String] {
def list: EssentialAction = Action { implicit request =>
val f = collection.find()
Async {
f.map { users =>
Ok(Json.toJson(users))
}
}
}
def show(id: String): EssentialAction = Action { implicit request =>
val userFuture = collection.findById(id)
Async {
userFuture.map { userOpt =>
userOpt.map { user =>
Ok(Json.toJson(user))
} getOrElse {
NotFound(s"User with id: $id")
}
}
}
}
def create: EssentialAction = update(BSONObjectID.generate.stringify)
def update(id: String) = Action(parse.json) { request =>
request.body.validate(setObjectId(id) andThen format).fold(
invalid = e => BadRequest(JsError.toFlatJson(e)),
valid = { validModel =>
Async {
collection.update(id, validModel).map { lastError =>
Created(Json.toJson(validModel))
}
}
}
)
}
def remove(id: String): EssentialAction = Action { implicit request =>
Async {
collection.remove(id).map { lastError =>
if (lastError.ok) Ok else InternalServerError(lastError.toString)
}
}
}
}
package controllers
import play.api.mvc._
import play.core.Router
import scala.runtime.AbstractPartialFunction
trait ResourceController[T] extends Controller {
def list: EssentialAction
def create: EssentialAction
def show(id: T): EssentialAction
def update(id: T): EssentialAction
def remove(id: T): EssentialAction
}
trait Routes {
def routes: PartialFunction[RequestHeader, Handler]
def documentation: Seq[(String, String, String)]
def setPrefix(prefix: String)
def prefix: String
}
abstract class ResourceRouter[T](implicit idBindable: PathBindable[T]) extends Router.Routes with ResourceController[T] {
private var path: String = ""
def setPrefix(prefix: String) {
path = prefix
}
def prefix = path
def documentation = Nil
def routes = new AbstractPartialFunction[RequestHeader, Handler] {
private val MaybeSlash = "/?".r
private val Id = "/([^/]+)/?".r
def withId(id: String, action: T => EssentialAction) = idBindable.bind("id", id).fold(badRequest, action)
override def applyOrElse[A <: RequestHeader, B >: Handler](rh: A, default: A => B) = {
if (rh.path.startsWith(path)) {
(rh.method, rh.path.drop(path.length)) match {
case ("GET", MaybeSlash()) => list
case ("POST", MaybeSlash()) => create
case ("GET", Id(id)) => withId(id, show)
case ("PUT", Id(id)) => withId(id, update)
case ("PATCH", Id(id)) => withId(id, update)
case ("DELETE", Id(id)) => withId(id, remove)
case _ => default(rh)
}
} else {
default(rh)
}
}
def isDefinedAt(rh: RequestHeader): Boolean = {
if (rh.path.startsWith(path)) {
(rh.method, rh.path.drop(path.length)) match {
case ("GET", MaybeSlash()) => true
case ("POST", MaybeSlash()) => true
case ("GET", Id(id)) => true
case ("PUT", Id(id)) => true
case ("PATCH", Id(id)) => true
case ("DELETE", Id(id)) => true
case _ => false
}
} else {
false
}
}
}
}
-> /api/user controllers.Users
GET /assets/*file controllers.Assets.at(path="/public", file)
package models
import play.api.libs.json._
import play.api.libs.functional.syntax._
import reactivemongo.bson.BSONObjectID
import play.modules.reactivemongo.json.BSONFormats.BSONObjectIDFormat
import play.api.Play.current
case class User(
email: String,
firstName: String,
lastName: String,
id: Option[BSONObjectID]
) {
val name = s"$firstName $lastName"
}
object User {
implicit val format = Json.format[User]
implicit object collection extends MongoCollection[User] {
def name = "users"
}
}
package controllers
import play.api.mvc._
import models._
object Users extends MongoResourceController[User] {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment