$30 off During Our Annual Pro Sale. View Details »

Scandalbars

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

Mark Wunsch

September 30, 2013
Tweet

More Decks by Mark Wunsch

Other Decks in Programming

Transcript

  1. Scandalbars
    Mark Wunsch
    @markwunsch

    View Slide

  2. View Slide

  3. View Slide

  4. Agenda
    • What is this whole templating thing all
    about?
    • Why even bother?
    • How did you do this crazy thing?

    View Slide

  5. The World Wide Web
    Client Server
    HTTP

    View Slide

  6. The World Wide Web
    Client Server
    HTTP

    View Slide

  7. The World Wide Web
    Client Server
    HTTP

    View Slide

  8. The World Wide Web
    Client Server
    HTTP
    HTML

    View Slide

  9. The World Wide Web
    Client Server
    HTTP

    View Slide

  10. The World Wide Web
    Client Server
    HTTP

    View Slide

  11. The World Wide Web
    Client Server
    HTTP
    JSON

    View Slide

  12. handlebarsjs.com

    View Slide

  13. SINGLE-PAGE APPS

    View Slide

  14. SINGLE-PAGE APPS
    jsappsfailinghorrifically.tumblr.com

    View Slide

  15. View Slide

  16. View Slide

  17. Client Server
    HTTP

    View Slide

  18. Client Server
    HTTP

    View Slide

  19. Client Server
    HTTP
    HTML

    View Slide

  20. Client Server
    HTTP
    HTML
    Accept: application/json

    View Slide

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

    View Slide

  22. Templating

    View Slide






  23. ${brandName}



    ${brandName}
    brandName="${brandName}" tooltipLocation="$
    {favoriteToolTipLoc}"/>



    View Slide

  24. }
    mustache.github.io

    View Slide

  25. scalate.fusesource.org

    View Slide

  26. This page intentionally left blank.

    View Slide

  27. handlebarsjs.com

    View Slide

  28. // Template
    <br/><div class="entry"><br/><h1>{{title}}</h1><br/><div class="body"><br/>{{body}}<br/></div><br/></div><br/>
    // 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

    My New Post

    This is my first post!


    View Slide

  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

    View Slide

  30. recursive
    descent
    into
    madness

    View Slide

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

    View Slide

  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); }

    View Slide

  33. λ

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  38. scala.util.parsing

    View Slide

  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

    View Slide

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

    View Slide

  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); }
    ;

    View Slide

  42. def mustache: Parser[Mustache] = {
    mustachify(pad(inMustache)) ^^ { mustacheable(_) } |
    mustachify("&" ~> pad(inMustache)) ^^ { mustacheable(_, true) } |
    mustachify("{" ~> pad(inMustache) <~ "}") ^^ { mustacheable(_, true) }
    }

    View Slide

  43. // Template
    val template = """
    Hello, my name is {{name}}. I am from {{hometown}}. I have {{kids.length}} kids:

    {{#kids}}{{name}} is {{age}}{{/kids}}

    """
    // 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 =
    "
    Hello, my name is Alan. I am from Somewhere, TX. I have 2 kids:

    Jimmy is 12Sally is 4

    "

    View Slide

  44. trait Handlebars {
    def apply[T](context: T): String
    }

    View Slide

  45. JSON

    View Slide

  46. types

    View Slide

  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.

    View Slide

  48. An Existential Crisis

    View Slide

  49. View Slide

  50. github.com/mwunsch/handlebars.scala

    View Slide

  51. {{thanks}}

    View Slide