Have seen a few play apps' source and it feels like massive boilerplate and leaky promise/future/async crud everywhere.
Lets take this:
https://github.com/guardian/frontend/blob/master/article/app/controllers/ArticleController.scala
case class ArticlePage(article: Article, storyPackage: List[Trail], edition: String)
object ArticleController extends Controller with Logging {
def render(path: String) = Action { implicit request =>
val promiseOfArticle = Akka.future(lookup(path))
Async {
promiseOfArticle.map(_.map { renderArticle }.getOrElse { NotFound })
}
}
private def lookup(path: String)(implicit request: RequestHeader): Option[ArticlePage] = suppressApi404 {
// .. stuff omitted
}
private def renderArticle(model: ArticlePage)(implicit request: RequestHeader): Result = // .. omitted
}
Let's look at that render
method. What does it do? It's pretty hard to tell, even with a lot of the code removed. It's trying to render an article.
If we just wrote some vanilla code to do that, it would be:
def render(path:String) = {
renderArticle(lookup(path).getOrElse(NotFound))
}
So what's all that other stuff? We've got all the buzzwords covered:
- promise
- future
- async
Why is this here? Several of this apps controllers have this exact structure. Shouldn't this concern be separated somehow? Even the Action
DSL method
that starts this off sticks out like a sort thumb. This method is all unnecessary complexity. To figure out what it does, I have to know what futures
and promises are, and what Async
does, but moreover, almost none of this method's source is specific to the business logic being performed. Why?
Let's see some more boilerplate:
implicit request
- why do I have to type this in every controller? Shouldn't every controller have a request without me typing code?!views.html.article
- I really have to tell theArticleController
to use HTML and to use thearticle
view?Cached
- why is this in the controller? This should be a separate concern.
I realize that some of this might be because of Scala's type system, but it just feels like we've taken the same boilerplate from XML or Annotations and made it code and somehow that makes it OK.
I hated repeating myself and telling the framework what it should've already known in XML, XDoclet, and annotations. I don't like it any better in code.
Just to give my 2c.
In Play controllers are just plain old scala objects (potentially built from a bunch of mixins) - extending from
play.api.mvc.Controller
just gives you a few helpers, other than that, there is no special meaning attached to the Controller type. A controller method is special only in that sense that it needs to produce an Action at some point. This gives the developer full control over how he/she would like to organize their codebase. Personally, I tend to keep only real "Action producer" methods in my controller (just the way you suggested) but it's not enforced.Anyway, my main point is that play provides a generic toolkit and we tried to strike a good balance between providing flexibility and avoiding unnecessary sit-ups. I feel like some of your concerns are specific to this codebase (and I actually agree with most of your points) and some of them are related to the fact that it seems that you tend to prefer the implicitness of Rails (which is fine but that's a different strategy than what we try to accomplish with Play).
HTH,
Peter