Add support for lambdas in inverse blocks

This commit is contained in:
robbytx
2014-03-10 17:06:21 -05:00
parent aafa5c0a73
commit 2251ac1ed2
3 changed files with 85 additions and 0 deletions
+42
View File
@@ -420,6 +420,48 @@ Note that if a variable _is_ defined in an inner context, it shadows the same
name in the outer context. There is presently no way to access the variable
from the outer context.
Invertable Lambdas
------------------
For some applications, it may be useful for lambdas to be executed for an
inverse section rather than having the section omitted altogether. This allows
for proper conditional substitution when statically translating templates into
other languages or contexts:
String template = "{{#condition}}result if true{{/condition}}\n{{^condition}}result if false{{/condition}}";
Mustache.compiler().compile(template).execute(new Object() {
Mustache.InvertableLambda condition = new Mustache.InvertableLambda() {
@Override
public void execute(Template.Fragment frag, Writer out)
throws IOException {
// this method is executed when the lambda is referenced in a normal section
out.write("if (condition) {console.log(\"");
out.write(toJavaScriptLiteral(frag.execute()));
out.write("\")}");
}
@Override
public void executeInverse(Template.Fragment frag, Writer out)
throws IOException {
// this method is executed when the lambda is referenced in an inverse section
out.write("if (!condition) {console.log(\"");
out.write(toJavaScriptLiteral(frag.execute()));
out.write("\")}");
}
private String toJavaScriptLiteral(String execute) {
// note: this is NOT a complete implementation of JavaScript string literal escaping
return execute.replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\\\"");
}
};
});
// results:
// if (condition) {console.log("result if true")}
// if (!condition) {console.log("result if false")}
Of course, you are not limited strictly to conditional substitution -- you can use an
InvertableLambda whenever you need a single function with two modes of operation.
Standards Mode
--------------
@@ -224,6 +224,17 @@ public class Mustache
void execute (Template.Fragment frag, Writer out) throws IOException;
}
public interface InvertableLambda extends Lambda
{
/** Executes this lambda on the supplied template fragment, when the lambda is used in an
* inverse section. The lambda should write its results to {@code out}.
*
* @param frag the fragment of the template that was passed to the lambda.
* @param out the writer to which the lambda should write its output.
*/
void executeInverse (Template.Fragment frag, Writer out) throws IOException;
}
/** Reads variables from context objects. */
public interface VariableFetcher
{
@@ -773,6 +784,12 @@ public class Mustache
if (!(Boolean)value) {
executeSegs(tmpl, ctx, out);
}
} else if (value instanceof InvertableLambda) {
try {
((InvertableLambda)value).executeInverse(tmpl.createFragment(_segs, ctx), out);
} catch (IOException ioe) {
throw new MustacheException(ioe);
}
} else if (_comp.isFalsey(value)) {
executeSegs(tmpl, ctx, out);
} // TODO: fail?
@@ -629,6 +629,32 @@ public class MustacheTest
}));
}
@Test public void testInvertableLambda () {
test("positive = positive, negative = negative, simple lambdas do still work",
"{{#invertable}}positive{{/invertable}}, {{^invertable}}negative{{/invertable}}, simple lambdas do {{^simple}}NOT {{/simple}}still work",
context("invertable", new Mustache.InvertableLambda() {
@Override
public void execute(Template.Fragment frag, Writer out)
throws IOException {
out.write("positive = ");
frag.execute(out);
}
@Override
public void executeInverse(Template.Fragment frag, Writer out)
throws IOException {
out.write("negative = ");
frag.execute(out);
}
}, "simple", new Mustache.Lambda() {
@Override
public void execute(Template.Fragment frag, Writer out)
throws IOException {
frag.execute(out);
}
}));
}
@Test public void testNonStandardDefaultDelims () {
test(Mustache.compiler().withDelims("<% %>"), "bar", "<%foo%>", new Object() {
String foo = "bar";