Skip to content

Instantly share code, notes, and snippets.

@zv3
Last active February 16, 2016 10:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zv3/2dad7cb63813e82f8412 to your computer and use it in GitHub Desktop.
Save zv3/2dad7cb63813e82f8412 to your computer and use it in GitHub Desktop.
// controllers/Application.scala
object Application extends Controller {
def index = ActionWithAttrs { implicit request =>
Ok(views.html.index())
}
}
object ActionWithAttrs extends StackableAction
@(implicit request: Request[_])
@main{
@indexHead @* content that goes in the head tag *@
} {
@indexBody
}
@import views.support.JavascriptPage
@(implicit request: Request[_])
@JavascriptPage.addNonBlockingJS("some-unique-id-for-this-script") {
window.alert("mega kill");
}
@import views.support.JavascriptPage
@(implicit request: Request[_])
<script src="/javascripts/your_javascript_app.js"></script>
<script id="non-blocking" type="text/javascript">
@defining(JavascriptPage.getNonBlockingJS()) { scripts =>
@scripts.map { case (_, item) => @item }
}
</script>
package views.support
import controllers.{RequestWithAttributes, AttributeKey}
import play.api.mvc.{Request, RequestHeader}
import play.twirl.api.Html
// views/support/JavascriptPage.scala
object JavascriptPage {
case class NonBlockingJS(key: String) extends AttributeKey[Html]
case class BlockingJS(key: String) extends AttributeKey[Html]
def addNonBlockingJS(div: String)(jscript: Html)(implicit request: Request[_]): Unit = {
request match {
case i: RequestWithAttributes[_] =>
i.set(NonBlockingJS(div), jscript)
case _ =>
}
}
// scripts that are supposed to go into the <head> tag, thus blocking scripts
def addBlockingJS(div: String)(jscript: Html)(implicit request: Request[_]): Unit = {
request match {
case i: RequestWithAttributes[_] =>
i.set(BlockingJS(div), jscript)
case _ =>
}
}
// scripts that are supposed to go before the </body> tag, non blocking scripts that is
def getNonBlockingJS()(implicit request: Request[_]): Seq[(String, Html)] = {
request match {
case i: RequestWithAttributes[_] =>
i.getAll[NonBlockingJS].toSeq.map {
case (NonBlockingJS(div), jscript: Html) => (div, jscript)
}
case _ => Seq.empty
}
}
}
@(head: Html)(body: Html)(implicit request: Request[_])
<html>
<head>
@head
</head>
<body>
@body
@inlineNonBlockingJS @* we append all of our javascript non blocking code (that were added to the Map up until this line) *@
</body>
package controllers
import play.api.mvc._
import scala.collection.concurrent.TrieMap
import scala.concurrent.Future
import scala.reflect.ClassTag
// Borrowed from: https://github.com/rabitarochan/Play2-ChainAction/blob/master/core/src/main/scala/com/github/rabitarochan/play2/stackableaction/StackableAction.scala
// controllers/StackableAction.scala
abstract class StackableAction extends ActionBuilder[RequestWithAttributes] with StackableFilter {
override def filter[A](request: RequestWithAttributes[A])(f: RequestWithAttributes[A] => Future[Result]): Future[Result] = {
f(request)
}
def invokeBlock[A](req: Request[A], block: RequestWithAttributes[A] => Future[Result]): Future[Result] = {
val reqWA = new RequestWithAttributes(req, new TrieMap[AttributeKey[_], Any]())
filter(reqWA)(block)
}
}
trait StackableFilter {
def filter[A](request: RequestWithAttributes[A])(f: RequestWithAttributes[A] => Future[Result]): Future[Result]
}
trait AttributeKey[A] {
def ->(value: A): Attribute[A] = Attribute(this, value)
}
case class Attribute[A](key: AttributeKey[A], value: A) {
def toTuple: (AttributeKey[A], A) = (key, value)
}
class RequestWithAttributes[A](request: Request[A], attributes: TrieMap[AttributeKey[_], Any]) extends WrappedRequest[A](request) {
def get[B](key: AttributeKey[B]): Option[B] = attributes.get(key).asInstanceOf[Option[B]]
def set[B](key: AttributeKey[B], value: B): RequestWithAttributes[A] = {
attributes.put(key, value)
this
}
def getAll[T](implicit classTag: ClassTag[T]) = {
attributes.filterKeys {
case p: T => true
case _ => false
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment