I need to pick a Mustache rendering engine for a Scala project of mine. Seems like the only two choices are Mustache-Java and Scalate? Are there any comparisons? Which one is the more stable/performant of the two?
Mustache Scalate vs Mustache Java
2.7k views Asked by Nej Kutcharian AtThere are 5 answers
I was using mustache as part of scalatra-scalate. This is the only sensible choice for me as I'm already invested in Scalatra. Given the choice, I'd try mustache-java thoroughly. The Scalate engines are (still?) somewhat quirky and immature.
A few examples I ran into:
- if you modify the default delimiters {{=<% %>=}} you will have to add spaces around your delimiters from then on, otherwise the parser chokes
- if you want to plug custom handling for missing keys, you're stuck
- sometimes the rendering hangs for minutes at a time for no obvious reason (this was with Jade, not Mustache, but it's rather scary anyway)
If you're not doing complex things mustache works fine though, and Scalate adds some nifty features like default templates and such that might help you.
I just went through this ordeal and in as much as I wanted to use scalate since I'm using Scala, I ended up using Mustache-Java for two reasons:
- There is no trivial way to load template from
/resources
dir in classpath (or even just the normal Java stream objects). This seems very basic and I'm baffled why this is not available OOB. - Similar to #1, there is no trivial way to load template from a simple
String
. You also need to write a custom loader if you want to do it. Why?
This is based on their documentation. Scalate looks very powerful and flexible if you're using it to generate server-side web pages, but for simple use cases, it incurs too much baggage.
One thing I discovered in Mustache-Java, is that you need to convert the data into Java map or it doesn't work (at least with Scala 2.12):
"Mustache test" should "succeed" in {
import scala.collection.JavaConverters._
val template = "Hello, {{name}}"
val mf = new DefaultMustacheFactory()
val mustache = mf.compile(new StringReader(template), "sample")
val map = Map("name" -> "Joe")
val sw = new StringWriter()
mustache.execute(sw, map.asJava) // if you don't use .asJava here it's not going to work
assert(sw.toString.contains("Joe"))
}
Inverted sections work:
"Mustache test exclusion" should "succeed" in {
import scala.collection.JavaConverters._
val template = "Hello, {{^exclude}}am I excluded?{{/exclude}}"
val mf = new DefaultMustacheFactory()
val mustache = mf.compile(new StringReader(template), "sample")
val map = Map("exclude" -> true)
val sw = new StringWriter()
mustache.execute(sw, map.asJava)
assert(!sw.toString.contains("excluded"))
}
Finally, regarding lists: case classes are supported but for any list fields, they have to be the Java types. The following variations should work:
case class Plan(name: String,
currency: String,
price: String)
"Mustache test with object context with list" should "succeed" in {
import scala.collection.JavaConverters._
import java.util.{List => JavaList}
val template =
"""{{#plans}}
|Name: {{name}}
|Price: {{currency}}{{price}}
|{{/plans}}""".stripMargin
val mf = new DefaultMustacheFactory()
val mustache = mf.compile(new StringReader(template), "sample")
// note the Java list type here
case class Plans(plans: JavaList[Plan])
val plans = Plans(Seq(Plan("essential", "$", "30")).asJava)
val sw = new StringWriter()
mustache.execute(sw, plans)
val list = Seq(Plan("essential", "$", "30")).asJava
mustache.execute(sw, Map("plans" -> list).asJava)
assert(sw.toString.contains("Name"))
}
"Mustache test with list" should "succeed" in {
import scala.collection.JavaConverters._
val template =
"""{{#plans}}
|Name: {{name}}
|Price: {{currency}}{{price}}
|{{/plans}}""".stripMargin
val mf = new DefaultMustacheFactory()
val mustache = mf.compile(new StringReader(template), "sample")
val sw = new StringWriter()
// do not forget .asJava here
val list = Seq(Plan("essential", "$", "30")).asJava
// do not forget .asJava here
mustache.execute(sw, Map("plans" -> list).asJava)
assert(sw.toString.contains("Name"))
}
I just went through this same process (Mustache Scalate or Mustache Java). I ended up going with Mustache Java, and it's working fine.
Why Mustache Java? Because all I wanted was Mustache templates. Scalate has more than just this support, and I didn't want to add more "stuff" to my code base but only use part of its functionality.
Scalate makes it easy to render Mustache templates and I'd rather use a relatively intuitive Scala lib than a Java lib.
Suppose you have the following simple_example.mustache
template:
I like {{programming_language}}
The code is {{code_description}}
You can render the template with this code:
import org.fusesource.scalate.TemplateEngine
val sourceDataPath = os.pwd/"simple_example.mustache".toString
val engine = new TemplateEngine
val someAttributes = Map(
"programming_language" -> "Scala",
"code_description" -> "pretty"
)
engine.layout(sourceDataPath, someAttributes)
Here's the result:
I like Scala
The code is pretty
A benefit of Mustache-Java is that Java compiles very quickly relative to Scala.