Add support for lambdas in inverse blocks
This commit is contained in:
@@ -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
|
name in the outer context. There is presently no way to access the variable
|
||||||
from the outer context.
|
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
|
Standards Mode
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|||||||
@@ -224,6 +224,17 @@ public class Mustache
|
|||||||
void execute (Template.Fragment frag, Writer out) throws IOException;
|
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. */
|
/** Reads variables from context objects. */
|
||||||
public interface VariableFetcher
|
public interface VariableFetcher
|
||||||
{
|
{
|
||||||
@@ -773,6 +784,12 @@ public class Mustache
|
|||||||
if (!(Boolean)value) {
|
if (!(Boolean)value) {
|
||||||
executeSegs(tmpl, ctx, out);
|
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)) {
|
} else if (_comp.isFalsey(value)) {
|
||||||
executeSegs(tmpl, ctx, out);
|
executeSegs(tmpl, ctx, out);
|
||||||
} // TODO: fail?
|
} // 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 public void testNonStandardDefaultDelims () {
|
||||||
test(Mustache.compiler().withDelims("<% %>"), "bar", "<%foo%>", new Object() {
|
test(Mustache.compiler().withDelims("<% %>"), "bar", "<%foo%>", new Object() {
|
||||||
String foo = "bar";
|
String foo = "bar";
|
||||||
|
|||||||
Reference in New Issue
Block a user