Skip to content

Instantly share code, notes, and snippets.

@gakuzzzz
Last active December 22, 2015 21:19
Show Gist options
  • Save gakuzzzz/6532349 to your computer and use it in GitHub Desktop.
Save gakuzzzz/6532349 to your computer and use it in GitHub Desktop.
Play2.2 ActionBuilder extended
package play.api.mvc
import play.api.libs.iteratee._
import play.api._
import scala.concurrent._
import scala.language.higherKinds
import scalaz.Id._
trait ActionBuilder[R[_], I[_]] {
self =>
final def apply[A](bodyParser: BodyParser[A])(block: R[I[A]] => Result): Action[I[A]] = async(bodyParser) { req: R[I[A]] =>
block(req) match {
case simple: SimpleResult => Future.successful(simple)
case async: AsyncResult => async.unflatten
}
}
final def apply(block: R[I[AnyContent]] => Result): Action[I[AnyContent]] = apply(BodyParsers.parse.anyContent)(block)
final def apply(block: => Result): Action[I[AnyContent]] = apply(_ => block)
final def async(block: => Future[SimpleResult]): Action[I[AnyContent]] = async(_ => block)
final def async(block: R[I[AnyContent]] => Future[SimpleResult]): Action[I[AnyContent]] = async(BodyParsers.parse.anyContent)(block)
final def async[A](bodyParser: BodyParser[A])(block: R[I[A]] => Future[SimpleResult]): Action[I[A]] = new Action[I[A]] {
def parser: BodyParser[I[A]] = composeParser(bodyParser)
def apply(request: Request[I[A]]) = try {
invokeBlock(request, block)
} catch {
case e: NotImplementedError => throw new RuntimeException(e)
case e: LinkageError => throw new RuntimeException(e)
}
override def executionContext = ActionBuilder.this.executionContext
}
def invokeBlock[A](request: Request[I[A]], block: R[I[A]] => Future[SimpleResult]): Future[SimpleResult] = block(fromRequest(request))
def unwrap[A](request: Request[I[A]]): Request[A]
def toRequest[A](request: R[I[A]]): Request[I[A]]
def fromRequest[A](request: Request[I[A]]): R[I[A]]
def composeParser[A](bodyParser: BodyParser[A]): BodyParser[I[A]]
def >>[R2[_], I2[_]](builder: ActionBuilder[R2, I2]): ActionBuilder[R2, ({type L[B] = I2[I[B]]})#L] = new ActionBuilder[R2, ({type L[B] = I2[I[B]]})#L] {
override def invokeBlock[A](request: Request[I2[I[A]]], block: R2[I2[I[A]]] => Future[SimpleResult]): Future[SimpleResult] = {
builder.invokeBlock(request, { req1: R2[I2[I[A]]] =>
ActionBuilder.this.invokeBlock(builder.unwrap(builder.toRequest(req1)), { req2: R[I[A]] =>
val req3: R2[I2[I[A]]] = ??? // generate req3 from req1 and req2
block(req3)
})
})
}
def unwrap[A](request: Request[I2[I[A]]]): Request[A] = ActionBuilder.this.unwrap(builder.unwrap(request))
def toRequest[A](request: R2[I2[I[A]]]): Request[I2[I[A]]] = builder.toRequest(request)
def fromRequest[A](request: Request[I2[I[A]]]): R2[I2[I[A]]] = builder.fromRequest(request)
def composeParser[A](bodyParser: BodyParser[A]): BodyParser[I2[I[A]]] = {
builder.composeParser(ActionBuilder.this.composeParser(bodyParser))
}
}
protected def executionContext: ExecutionContext = play.api.libs.concurrent.Execution.defaultContext
}
object Stack {
val MyAction = AuthAction >> IpCheckAction >> LoggigAction >> CsrfAction
}
object Action extends ActionBuilder[Request, Id] {
def unwrap[A](request: Request[A]): Request[A] = request
def toRequest[A](request: Request[A]): Request[A] = request
def fromRequest[A](request: Request[A]): Request[A] = request
def composeParser[A](bodyParser: BodyParser[A]): BodyParser[A] = bodyParser
}
class User(id: Int, name: String)
object AuthAction extends ActionBuilder[Request, ({type L[A] = (A, User)})#L] {
def unwrap[A](request: Request[(A, User)]): Request[A] = request.map(_._1)
def toRequest[A](request: Request[(A, User)]): Request[(A, User)] = request
def fromRequest[A](request: Request[(A, User)]): Request[(A, User)] = request
import play.api.libs.concurrent.Execution.Implicits.defaultContext
def composeParser[A](bodyParser: BodyParser[A]): BodyParser[(A, User)] = BodyParser {
req => Iteratee.flatten(authorized(req).map {
case Right(user) => bodyParser.map((_, user))(req)
case Left(result) => Done[Array[Byte], Either[SimpleResult, (A, User)]](Left(result))
})
}
private def authorized(request: RequestHeader)(implicit context: ExecutionContext): Future[Either[SimpleResult, User]] =
??? // do authentication and authorization
}
object IpCheckAction extends ActionBuilder[Request, Id] {
def unwrap[A](request: Request[A]): Request[A] = request
def toRequest[A](request: Request[A]): Request[A] = request
def fromRequest[A](request: Request[A]): Request[A] = request
def composeParser[A](bodyParser: BodyParser[A]): BodyParser[A] = BodyParser {
req => if (checkIp(req)) Done[Array[Byte], Either[SimpleResult, A]](Left(invalidIpResult))
else bodyParser(req)
}
private def checkIp(request: RequestHeader): Boolean = ??? // do IP check
private def invalidIpResult: SimpleResult = Results.Forbidden
}
object LoggigAction extends ActionBuilder[Request, Id] {
def unwrap[A](request: Request[A]): Request[A] = request
def toRequest[A](request: Request[A]): Request[A] = request
def fromRequest[A](request: Request[A]): Request[A] = request
def composeParser[A](bodyParser: BodyParser[A]): BodyParser[A] = bodyParser
override def invokeBlock[A](request: Request[A], block: Request[A] => Future[SimpleResult]): Future[SimpleResult] = {
Logger.info("action start")
block(request).andThen {
case _ => Logger.info("action end")
} (executionContext)
}
}
class RequestWithToken[A](val token: String, request: Request[A]) extends WrappedRequest(request)
object CsrfAction extends ActionBuilder[RequestWithToken, Id] {
def unwrap[A](request: Request[A]): Request[A] = request
def toRequest[A](request: RequestWithToken[A]): Request[A] = request
def fromRequest[A](request: Request[A]): RequestWithToken[A] = new RequestWithToken("random generate", request)
def composeParser[A](bodyParser: BodyParser[A]): BodyParser[A] = bodyParser
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment