Added basic lambda support.

This commit is contained in:
Michael Bayne
2013-02-15 16:38:36 -08:00
parent be21def381
commit acbddd07a2
4 changed files with 116 additions and 2 deletions
+41
View File
@@ -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("<b>");
frag.execute(out);
out.write("</b>");
}
};
});
// result:
<b>Willy is awsome.</b>
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:
<h2>{{#i18n}}title{{/i18n}</h2>
{{#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
--------------
@@ -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 {
@@ -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.
@@ -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("<b>Willy is awesome.</b>", "{{#bold}}{{name}} is awesome.{{/bold}}",
context("name", "Willy", "bold", new Mustache.Lambda() {
public void execute (Template.Fragment frag, Writer out) throws IOException {
out.write("<b>");
frag.execute(out);
out.write("</b>");
}
}));
}
@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));