diff --git a/README.md b/README.md
index 248fe0b..6d0e20d 100644
--- a/README.md
+++ b/README.md
@@ -132,6 +132,47 @@ If you wish to make use of partials (e.g. `{{>subtmpl}}`) you must provide a
The above snippet will load `new File(templateDir, "subtmpl")` when compiling
the template.
+Lambdas
+-------
+
+JMustache implements lambdas by passing you a `Template.Fragment` instance
+which you can use to execute the fragment of the template that was passed to
+the lambda. You can decorate the results of the fragment execution, as shown in
+the standard Mustache documentation on lambdas:
+
+ String tmpl = "{{#bold}}{{name}} is awesome.{{/bold}}";
+ Mustache.compiler().compile(tmpl).execute(new Object() {
+ Mustache.Lambda bold = new Mustache.Lambda() {
+ public void execute (Template.Fragment frag, Writer out) throws IOException {
+ out.write("");
+ frag.execute(out);
+ out.write("");
+ }
+ };
+ });
+ // result:
+ Willy is awsome.
+
+You can also obtain the results of the fragment execution to do things like
+internationalization or caching:
+
+ Object ctx = new Object() {
+ Mustache.Lambda i18n = new Mustache.Lambda() {
+ public void execute (Template.Fragment frag, Writer out) throws IOException {
+ String key = frag.execute();
+ String text = // look up key in i18n system
+ out.write(text);
+ }
+ };
+ };
+ // template might look something like:
+
{{#i18n}}title{{/i18n}
+ {{#i18n}}welcome_msg{{/i18n}}
+
+Currently there is no support for "unexecuting" the template and obtaining the
+original Mustache template text contained in the section. File a feature
+request with a sane use case if you have one.
+
Default Values
--------------
diff --git a/src/main/java/com/samskivert/mustache/Mustache.java b/src/main/java/com/samskivert/mustache/Mustache.java
index e95e237..37e5cc6 100644
--- a/src/main/java/com/samskivert/mustache/Mustache.java
+++ b/src/main/java/com/samskivert/mustache/Mustache.java
@@ -150,7 +150,19 @@ public class Mustache
{
/** Returns a reader for the template with the supplied name.
* @throws Exception if the template could not be loaded for any reason. */
- public Reader getTemplate (String name) throws Exception;
+ Reader getTemplate (String name) throws Exception;
+ }
+
+ /** Used to handle lambdas. */
+ public interface Lambda
+ {
+ /** Executes this lambda on the supplied template fragment. The lambda should write its
+ * results to {@code out}.
+ *
+ * @param tmpl the fragment of the template that was passed to the lambda.
+ * @param out the writer to which the lambda should write its output.
+ */
+ void execute (Template.Fragment frag, Writer out) throws IOException;
}
/** Used to read variables from values. */
@@ -640,6 +652,12 @@ public class Mustache
if ((Boolean)value) {
executeSegs(tmpl, ctx, out);
}
+ } else if (value instanceof Lambda) {
+ try {
+ ((Lambda)value).execute(tmpl.createFragment(_segs, ctx), out);
+ } catch (IOException ioe) {
+ throw new MustacheException(ioe);
+ }
} else if (_compiler.emptyStringIsFalse && "".equals(value)) {
// omit the section
} else {
diff --git a/src/main/java/com/samskivert/mustache/Template.java b/src/main/java/com/samskivert/mustache/Template.java
index 445c42d..86123ff 100644
--- a/src/main/java/com/samskivert/mustache/Template.java
+++ b/src/main/java/com/samskivert/mustache/Template.java
@@ -36,6 +36,22 @@ import java.util.Map;
*/
public class Template
{
+ /**
+ * Encapsulates a fragment of a template that is passed to a lambda. The fragment is bound to
+ * the variable context that was in effect at the time the lambda was called.
+ */
+ public abstract class Fragment {
+ /** Executes this template fragment, writing its result to {@code out}. */
+ public abstract void execute (Writer out);
+
+ /** Executes this template fragment and returns is result as a string. */
+ public String execute () {
+ StringWriter out = new StringWriter();
+ execute(out);
+ return out.toString();
+ }
+ }
+
/**
* Executes this template with the given context, returning the results as a string.
* @throws MustacheException if an error occurs while executing or writing the template.
@@ -73,7 +89,7 @@ public class Template
{
_segs = segs;
_compiler = compiler;
- _fcache = _compiler.collector.createFetcherCache();
+ _fcache = compiler.collector.createFetcherCache();
}
protected void executeSegs (Context ctx, Writer out) throws MustacheException
@@ -83,6 +99,17 @@ public class Template
}
}
+ protected Fragment createFragment (final Segment[] segs, final Context ctx)
+ {
+ return new Fragment() {
+ @Override public void execute (Writer out) {
+ for (Segment seg : segs) {
+ seg.execute(Template.this, ctx, out);
+ }
+ }
+ };
+ }
+
/**
* Called by executing segments to obtain the value of the specified variable in the supplied
* context.
diff --git a/src/test/java/com/samskivert/mustache/MustacheTest.java b/src/test/java/com/samskivert/mustache/MustacheTest.java
index d032a97..ab5c1e5 100644
--- a/src/test/java/com/samskivert/mustache/MustacheTest.java
+++ b/src/test/java/com/samskivert/mustache/MustacheTest.java
@@ -8,8 +8,10 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
+import java.io.Writer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -484,6 +486,32 @@ public class MustacheTest
});
}
+ @Test public void testLambda1 () {
+ test("Willy is awesome.", "{{#bold}}{{name}} is awesome.{{/bold}}",
+ context("name", "Willy", "bold", new Mustache.Lambda() {
+ public void execute (Template.Fragment frag, Writer out) throws IOException {
+ out.write("");
+ frag.execute(out);
+ out.write("");
+ }
+ }));
+ }
+
+ @Test public void testLambda2 () {
+ test("Slug bug potato!", "{{#l}}1{{/l}} {{#l}}2{{/l}} {{#l}}{{three}}{{/l}}",
+ context("three", "3", "l", new Mustache.Lambda() {
+ public void execute (Template.Fragment frag, Writer out) throws IOException {
+ out.write(lookup(frag.execute()));
+ }
+ protected String lookup (String contents) {
+ if (contents.equals("1")) return "Slug";
+ else if (contents.equals("2")) return "bug";
+ else if (contents.equals("3")) return "potato!";
+ else return "Who was that man?";
+ }
+ }));
+ }
+
protected void test (Mustache.Compiler compiler, String expected, String template, Object ctx)
{
assertEquals(expected, compiler.compile(template).execute(ctx));