Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scandalbars

63e2f6b0de6ae817af2e185b82aa05c2?s=47 Mark Wunsch
September 30, 2013

 Scandalbars

At Gilt (gilt.com), we built a Scala implementation of the Handlebars templating language (originally written in JavaScript) to use as our go-to language for building reusable web views. This talk will cover why we would ever attempt such a thing and how we went about doing it.

See: http://tech.gilt.com/post/59708050425/handlebars-scala-a-handlebars-for-scala

63e2f6b0de6ae817af2e185b82aa05c2?s=128

Mark Wunsch

September 30, 2013
Tweet

Transcript

  1. Scandalbars Mark Wunsch @markwunsch

  2. None
  3. None
  4. Agenda • What is this whole templating thing all about?

    • Why even bother? • How did you do this crazy thing?
  5. The World Wide Web Client Server HTTP

  6. The World Wide Web Client Server HTTP

  7. The World Wide Web Client Server HTTP

  8. The World Wide Web Client Server HTTP HTML

  9. The World Wide Web Client Server HTTP

  10. The World Wide Web Client Server HTTP

  11. The World Wide Web Client Server HTTP JSON

  12. handlebarsjs.com

  13. SINGLE-PAGE APPS

  14. SINGLE-PAGE APPS jsappsfailinghorrifically.tumblr.com

  15. None
  16. None
  17. Client Server HTTP

  18. Client Server HTTP

  19. Client Server HTTP HTML

  20. Client Server HTTP HTML Accept: application/json

  21. Client Server HTTP HTML Accept: application/json JSON

  22. Templating

  23. <hgroup class="look-name"> <c:if test="${!hideBrandName}"> <h2 class="brand-name"> <c:choose> <c:when test="${!empty detailedBrandOpt

    && detailedBrandOpt.isPresent}"> <a class="primary" href="/brand/$ {detailedBrandOpt.get.urlKey}">${brandName}</a> <gilt:favorite brandId="${brandId}" brandName="$ {brandName}" tooltipLocation="${favoriteToolTipLoc}"/> </c:when> <c:otherwise> <div class="primary">${brandName} <gilt:favorite brandId="${brandId}" brandName="${brandName}" tooltipLocation="$ {favoriteToolTipLoc}"/> </div> </c:otherwise> </c:choose>
  24. } mustache.github.io

  25. scalate.fusesource.org

  26. This page intentionally left blank.

  27. handlebarsjs.com

  28. // Template <script id="entry-template" type="text/x-handlebars-template"> <div class="entry"> <h1>{{title}}</h1> <div class="body">

    {{body}} </div> </div> </script> // Code var source = $("#entry-template").html(); var template = Handlebars.compile(source); var context = {title: "My New Post", body: "This is my first post!"} var html = template(context); // Result <div class="entry"> <h1>My New Post</h1> <div class="body"> This is my first post! </div> </div>
  29. “Maybe I’ll just write Handlebars in Scala,” I finally said

    to Tom. We were at a bar at the time, and I was a bit tipsy, but there was a “yeah, sure, do that” vibe to our conversation. http://tech.gilt.com/post/59708050425/handlebars-scala-a-handlebars-for-scala
  30. recursive descent into madness

  31. http://zaach.github.io/jison/

  32. %start root %% root : program EOF { return $1;

    } ; program : simpleInverse statements { $$ = new yy.ProgramNode([], $2); } | statements simpleInverse statements { $$ = new yy.ProgramNode($1, $3); } | statements simpleInverse { $$ = new yy.ProgramNode($1, []); } | statements { $$ = new yy.ProgramNode($1); } | simpleInverse { $$ = new yy.ProgramNode([], []); } | "" { $$ = new yy.ProgramNode([]); } ; statements : statement { $$ = [$1]; } | statements statement { $1.push($2); $$ = $1; } ; statement : openInverse program closeBlock { $$ = new yy.BlockNode($1, $2.inverse, $2, $3); } | openBlock program closeBlock { $$ = new yy.BlockNode($1, $2, $2.inverse, $3); } | mustache { $$ = $1; } | partial { $$ = $1; } | CONTENT { $$ = new yy.ContentNode($1); } | COMMENT { $$ = new yy.CommentNode($1); }
  33. λ

  34. Parser Combinators http://eprints.nottingham.ac.uk/221/1/parsing.pdf

  35. abstract class Parser[+T] extends (Input) 㱺 ParseResult[T]

  36. def *: Parser[List[T]] def >>[U]:(fq: (T) => Parser[U]): Parser[U] def

    ^^[U]:(f: (T) => U): Parser[U] def |[U>: T](q:=> Parser[U]): Parser[U] def ~[U](q:=> Parser[U]): Parser[~[T,U]] Combinators
  37. def *: Parser[List[T]] def >>[U]:(fq: (T) => Parser[U]): Parser[U] def

    ^^[U]:(f: (T) => U): Parser[U] def |[U>: T](q:=> Parser[U]): Parser[U] def ~[U](q:=> Parser[U]): Parser[~[T,U]] Combinators
  38. scala.util.parsing

  39. def ident: Parser[String] // Anything that is a valid Java

    identifier, according the The Java Language Spec. def wholeNumber: Parser[String] // An integer, without sign or with a negative sign. def stringLiteral: Parser[String] // Double quotes (") enclosing a sequence of... implicit def regex(r: Regex): Parser[String] implicit def literal(s: String): Parser[String] scala.util.parsing.combinator.JavaTokenParsers
  40. def statements = rep1(statement) def statement = { inverseBlock |

    block | mustache | partial | CONTENT ^^ { Content(_) } | comment }
  41. def statements = rep1(statement) def statement = { inverseBlock |

    block | mustache | partial | CONTENT ^^ { Content(_) } | comment } statements : statement { $$ = [$1]; } | statements statement { $1.push($2); $$ = $1; } ; statement : openInverse program closeBlock { $$ = new yy.BlockNode($1, $2.inverse, $2, $3); } | openBlock program closeBlock { $$ = new yy.BlockNode($1, $2, $2.inverse, $3); } | mustache { $$ = $1; } | partial { $$ = $1; } | CONTENT { $$ = new yy.ContentNode($1); } | COMMENT { $$ = new yy.CommentNode($1); } ;
  42. def mustache: Parser[Mustache] = { mustachify(pad(inMustache)) ^^ { mustacheable(_) }

    | mustachify("&" ~> pad(inMustache)) ^^ { mustacheable(_, true) } | mustachify("{" ~> pad(inMustache) <~ "}") ^^ { mustacheable(_, true) } }
  43. // Template val template = """ <p>Hello, my name is

    {{name}}. I am from {{hometown}}. I have {{kids.length}} kids:</p> <ul> {{#kids}}<li>{{name}} is {{age}}</li>{{/kids}} </ul> """ // Object object Guy { val name = "Alan" val hometown = "Somewhere, TX" val kids = Seq(Map( "name" -> "Jimmy", "age -> "12" ), Map( "name" -> "Sally", "age" -> "4" )) } // Result scala> val t = Handlebars(template) t: com.gilt.handlebars.Handlebars = com.gilt.handlebars.Handlebars@496d864e scala> t(Guy) res0: String = " <p>Hello, my name is Alan. I am from Somewhere, TX. I have 2 kids:</p> <ul> <li>Jimmy is 12</li><li>Sally is 4</li> </ul> "
  44. trait Handlebars { def apply[T](context: T): String }

  45. JSON

  46. types

  47. Implicit conversions will not work in a template. Because Handlebars.scala

    makes heavy use of reflection. Bummer, I know. This leads me too... Handlebars.scala makes heavy use of reflection. This means that there could be unexpected behavior. Method overloading will behave in bizarre ways. There is likely a performance penalty. I'm not sophisticated enough in the arts of the JVM to know the implications of this.
  48. An Existential Crisis

  49. None
  50. github.com/mwunsch/handlebars.scala

  51. {{thanks}}