Mustache.java is a derivative of mustache.js.
There is a Google Group for support and questions: http://groups.google.com/group/mustachejava
Travis CI: https://travis-ci.org/spullara/mustache.java
Largest production deployment of Mustache.java:
- Twitter (the web site, email, syndicated widgets, etc)
Thanks to YourKit for many performance improvements:
YourKit is kindly supporting the mustache.java open source project with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. Take a look at YourKit's leading software products:
Request for contributions:
- Real world benchmarks that matter - currently benchmarking based on Twitter templates
- Documentation
- Bug reports / fixes
- API feedback
- Optimizations
Documentation:
- Javadocs
- Mustache.js manual
- Passes all of the
mustache
specification tests modulo whitespace differences - Biggest difference between mustache.js and mustache.java is optional concurrent evaluation
- Data is provided by objects in an array of scopes and are accessed via non-private fields, methods or maps
- Any
Iterable
can be used for list-like behaviors - Returning a
Callable
allows for concurrent evaluation if anExecutorService
is configured - Template inheritance is supported by this implementation, see mustache/spec#38 (eg.
{{<super}}{{$content}}...{{/content}}{{/super}}
) - Additional functions/lambdas (eg.
{{#func1}}...{{/func1}}
) are implemented usingFunction
from Google Guava (post-substitution) - Use
TemplateFunction
if you want mustache.java to reparse the results of your function/lambda (pre-substitution) - Both default and manually configured classpath based and file system based template roots are supported
- An invokedynamic version is in development but currently is no faster than the default reflection based system
- The
handlebar
server will render templates + json data for quick mockups of templates by designers - Completely pluggable system for overriding almost all the behavior in the compilation and rendering process
- You can pull out sample data from live systems using the
CapturingMustacheVisitor
for mocks and tests - The DecoratedCollection can provide first / last / index for elements in a collection
Performance:
- See the
com.github.mustachejavabenchmarks
package in thecompiler
module - Compiles 4000+ timeline.html templates per second per core
- Renders 3000+ of 50 tweet timelines per second per core on 2011 Macbook Pro / MacPro hardware
- New codegen module generates code for guards and mustaches
Build suggestions:
- Don't build, use Maven dependencies
- If you must build but not test:
- git clone https://github.com/spullara/mustache.java.git
- set your JAVA_HOME to a JDK 7 JDK
- mvn -DskipTests clean install
- If you must build and test but not benchmark:
- CI=1 mvn clean install
- If you must build, test and benchmark:
- mvn clean install
Maven dependency information (ie. for most common cases you will just need the compiler
module):
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.8.17</version>
</dependency>
Example template file:
{{#items}}
Name: {{name}}
Price: {{price}}
{{#features}}
Feature: {{description}}
{{/features}}
{{/items}}
Might be powered by some backing code:
public class Context {
List<Item> items() {
return Arrays.asList(
new Item("Item 1", "$19.99", Arrays.asList(new Feature("New!"), new Feature("Awesome!"))),
new Item("Item 2", "$29.99", Arrays.asList(new Feature("Old."), new Feature("Ugly.")))
);
}
static class Item {
Item(String name, String price, List<Feature> features) {
this.name = name;
this.price = price;
this.features = features;
}
String name, price;
List<Feature> features;
}
static class Feature {
Feature(String description) {
this.description = description;
}
String description;
}
}
And would result in:
Name: Item 1
Price: $19.99
Feature: New!
Feature: Awesome!
Name: Item 2
Price: $29.99
Feature: Old.
Feature: Ugly.
Evaluation of the template proceeds serially. For instance, if you have blocking code within one of your callbacks you the system will pause while executing them:
static class Feature {
Feature(String description) {
this.description = description;
}
String description() throws InterruptedException {
Thread.sleep(1000);
return description;
}
}
If you change description to return a Callable
instead it will automatically be executed in a separate
thread if you have provided an ExecutorService
when you created your MustacheFactory
.
Callable<String> description() throws InterruptedException {
return new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return description;
}
};
}
This enables scheduled tasks, streaming behavior and asynchronous i/o. Check out the example
module in order
to see a complete end-to-end example:
package mustachejava;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.List;
public class Example {
List<Item> items() {
return Arrays.asList(
new Item("Item 1", "$19.99", Arrays.asList(new Feature("New!"), new Feature("Awesome!"))),
new Item("Item 2", "$29.99", Arrays.asList(new Feature("Old."), new Feature("Ugly.")))
);
}
static class Item {
Item(String name, String price, List<Feature> features) {
this.name = name;
this.price = price;
this.features = features;
}
String name, price;
List<Feature> features;
}
static class Feature {
Feature(String description) {
this.description = description;
}
String description;
}
public static void main(String[] args) throws IOException {
MustacheFactory mf = new DefaultMustacheFactory();
Mustache mustache = mf.compile("template.mustache");
mustache.execute(new PrintWriter(System.out), new Example()).flush();
}
}
An alternative approach for providing variables would be to use a Map object, like:
public static void main(String[] args) throws IOException {
HashMap<String, Object> scopes = new HashMap<String, Object>();
scopes.put("name", "Mustache");
scopes.put("feature", new Feature("Perfect!"));
Writer writer = new OutputStreamWriter(System.out);
MustacheFactory mf = new DefaultMustacheFactory();
Mustache mustache = mf.compile(new StringReader("{{name}}, {{feature.description}}!"), "example");
mustache.execute(writer, scopes);
writer.flush();
}