Simple Jinja-like templating in Scala
(, en)
There are impressive templating engines available for complex challenges. And Scala has
the s"..." construct. If you need something in the middle, probably in an external
file under e.g. src/main/resources, a simple templating function comes in handy. I
cannot say that I’m a big fan of Python’s Jinja,
but the {{...}} syntax is well known and straight forward. Without further ado, the
templating function:
import scala.util.matching.Regex
/**
* Python's Jinja for poor people.
*
* A very simple function to replace variables in text using `` syntax.
*
* @param tmpl
* the template string/text
* @param vars
* a map of variable -> replacement mapping
* @param relaxed
* Don't raise a NoSuchElementException if a variable is not in the var map.
* @return
* the text with variables replaced.
*/
def renderTemplate(tmpl: String, vars: Map[String, String], relaxed: Boolean = false): String =
val pattern: Regex = """\{\{\s*(\w+)\s*\}\}""".r
def handleMatch(m: Regex.Match): String =
val key = m.group(1)
vars.get(key) match
case Some(value) => Regex.quoteReplacement(value)
case None =>
if relaxed then m.group(0)
else throw new NoSuchElementException(s"$key is not in the supplied replacement variables")
pattern.replaceAllIn(tmpl, handleMatch)
Example usage looks like this:
it should "replaces entries" in {
val tmpl = """create table {{ns}}.{{table}}.{{ns}}
|{{ns}}.{{foo}}""".stripMargin
val actual = renderTemplate(tmpl, Map("ns" -> "lhd", "table" -> "core", "foo" -> "bar"))
val expected =
"""create table lhd.core.lhd
|lhd.bar""".stripMargin
assert(actual == expected)
}
That is much better than concatenation in Scala code. If you want to load the file from
the resources folder src/main/resources/template.txt, you can do this:
import scala.io.Source
def fromResource(path: String): String = Source.fromInputStream(getClass.getResourceAsStream(path)).mkString
val data = fromResource("/template.txt")
renderTemplate(data, Map(???))
Have fun!
