Last active
December 18, 2015 16:09
-
-
Save sam/5809282 to your computer and use it in GitHub Desktop.
Get rid of Async wrappers in Play Framework Actions. The advantage of this approach over pattern-matching is that it's extensible by downstream code introducing new types, and moves what would otherwise be a runtime error (bad match) to a compiler error (no evidence found).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import play.api.mvc._ | |
import play.api.mvc.{Action => PlayAction} | |
import scala.concurrent._ | |
object Application extends Controller { | |
// Define the Type Class that will allow us to coerce our action return-types to Results. | |
trait RequestHandlerLike[-T] { | |
def apply(result: T): Result // = result | |
} | |
object RequestHandlerLike { | |
implicit object DefaultRequestHandlerLike extends RequestHandlerLike[Result] { | |
def apply(result: Result) = result | |
} | |
implicit object AsyncRequestHandlerLike extends RequestHandlerLike[Future[Result]] { | |
def apply(result: Future[Result]) = Results.Async(result) | |
} | |
} | |
// Define our custom Action method. | |
def Action[A,B](bp: BodyParser[A]) | |
(f: Request[A] => B) | |
(implicit handler: RequestHandlerLike[B]): PlayAction[A] = PlayAction(bp) { request => | |
handler(f(request)) | |
} | |
// Here's an alias for AnyContent body parsers. | |
// We're using Context Bounds here instead of an implicit since we don't need to actually | |
// use the RequestHandlerLike in this version, but we'd still like a compiler error | |
// if we can't find an evidence parameter for B. | |
def Action[B:RequestHandlerLike](f: Request[AnyContent] => B): PlayAction[AnyContent] = | |
Action(parse.anyContent)(f) | |
// Your standard Action, whose body returns a Result. | |
def index = Action { | |
Ok("Hello World!") | |
} | |
// If you return a Future[Result] it still works, and no need to wrap it with Async! | |
def showMeTheMoney = Action { | |
future { "Hello Future World!" }.map(Ok(_)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment