Skip to content

Instantly share code, notes, and snippets.

@hexx
Created March 1, 2013 18:51
Show Gist options
  • Save hexx/5066853 to your computer and use it in GitHub Desktop.
Save hexx/5066853 to your computer and use it in GitHub Desktop.
HtmlDSL using Dynamic
import scala.language.dynamics
import scala.util.DynamicVariable
sealed trait Node {
def toXml: String
}
case class ElementNode(name: String, attributes: Map[String, String], children: List[Node]) extends Node {
def toXml = {
val as = attributes.mapValues("\"" + _ + "\"")
s"""<${name}${as.map { case (l, r) => s" $l=$r" }.mkString}>${children.map(_.toXml).mkString}</${name}>"""
}
}
case class TextNode(text: String) extends Node {
def toXml = text
}
trait HtmlDSL {
protected val nodes = new DynamicVariable[List[Node]](null)
protected val attributes = new DynamicVariable[List[(String, String)]](null)
object % extends Dynamic{
def selectDynamic(name: String) = applyDynamic(name){}
def applyDynamic(name: String)(body: => Unit): Unit = {
val (ns, as) =
nodes.withValue(List()) {
attributes.withValue(List()) {
body
(nodes.value.reverse, attributes.value.toMap)
}
}
nodes.value = ElementNode(name, as, ns) :: nodes.value
}
}
def $(as: (String, String)*) = attributes.value = attributes.value ++ as
def t(text: String) = nodes.value = TextNode(text) :: nodes.value
def dsl(body: => Unit) = nodes.withValue(List()) {
body
nodes.value.head
}
}
object Sample extends HtmlDSL {
val html = dsl {
%html {
%head {
%link($("rel" -> "stylesheet", "href" -> "style.css"))
%title {
t("タイトルだよ")
}
}
%body {
%p {
t("本文だよ")
}
}
}
}
def print = println(html.toXml)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment