Mustache.java ============= **Mustache.java** is a derivative of [mustache.js](http://mustache.github.com/mustache.5.html). There is a Google Group for support and questions: Request for contributions: - Real world benchmarks that matter - currently benchmarking based on Twitter templates - Documentation - Bug reports / fixes - API feedback - Optimizations Documentation: - [Mustache.js manual](http://mustache.github.com/mustache.5.html) - Passes all of the `mustache` [specification tests](https://github.com/mustache/spec) 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 an `ExecutorService` is configured - Template inheritance is supported by this implementation, see (eg. `{{ com.github.spullara.mustache.java compiler 0.8.8 ``` Example template file: {{#items}} Name: {{name}} Price: {{price}} {{#features}} Feature: {{description}} {{/features}} {{/items}} Might be powered by some backing code: ```java public class Context { List 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 features) { this.name = name; this.price = price; this.features = features; } String name, price; List 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: ```java 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`. ```java Callable description() throws InterruptedException { return new Callable() { @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: ```java 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 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 features) { this.name = name; this.price = price; this.features = features; } String name, price; List 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: ```java public static void main(String[] args) throws IOException { HashMap scopes = new HashMap(); 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(); } ```