Modified compound variables to honor null/default configuration. Closes #10.

Kali/Bertrand convinced me that being consistent with other Mustache
implementations is more important than whatever wacky reasons I was harboring
for keeping compound variables different than singleton sections.
This commit is contained in:
Michael Bayne
2011-10-29 14:38:56 -07:00
parent 37884af44d
commit adf116992c
4 changed files with 40 additions and 44 deletions
+3 -12
View File
@@ -280,18 +280,9 @@ sections. The above examples could also be represented as:
Hello {{#class}}{{name}}{{/class}}!
Note also that one semantic difference exists between nested singleton sections
and compound variables: compound variables do not make use of the default value
and null value configuration. If a null or missing value is encountered while
resolving a compound variable, an exception is always raised.
If you desire for a default value to be used when null or missing values are
encountered, use the somewhat more verbose singleton sections. The use of
sections clearly communicates to someone reading the template that the section
will not be rendered if the object it references is missing or null and allows
for a site-specific default value to be used, as in:
{{#foo}}{{name}}{{/foo}}
{{^foo}}Missing foo!{{/foo}}
and compound variables: after resolving the object for the first component of
the compound variable, parent contexts will not be searched when resolving
subcomponents.
Newline trimming
----------------
@@ -85,9 +85,7 @@ public class Mustache
/** Returns a compiler that will use the given value for any variable that is missing, or
* otherwise resolves to null. This is like {@link #nullValue} except that it returns the
* supplied default for missing keys and existing keys that return null values. Note that
* regardless of whether a null or default value is configured, if the resolution of part
* of a compound key results in a missing or null value, an exception will be raised. */
* supplied default for missing keys and existing keys that return null values. */
public Compiler defaultValue (String defaultValue) {
return new Compiler(this.escapeHTML, this.standardsMode, defaultValue, true,
this.loader, this.collector);
@@ -103,10 +101,7 @@ public class Mustache
* <li>In the case of a {@link Map} being used as a context, all possible accessors are
* assumed to exist (but potentially return null), and no exception will ever be
* raised.</li>
* </ul>
* Note that regardless of whether a null or default value is configured, if the resolution
* of part of a compound key results in a missing or null value, an exception will be
* raised. */
* </ul> */
public Compiler nullValue (String nullValue) {
return new Compiler(this.escapeHTML, this.standardsMode, nullValue, false,
this.loader, this.collector);
@@ -93,13 +93,12 @@ public class Template
Object data = getValue(ctx, comps[0].intern(), line, missingIsNull);
for (int ii = 1; ii < comps.length; ii++) {
if (data == NO_FETCHER_FOUND) {
throw new NullPointerException(
if (!missingIsNull) throw new MustacheException(
"Missing context for compound variable '" + name + "' on line " + line +
". '" + comps[ii - 1] + "' was not found.");
return null;
} else if (data == null) {
throw new NullPointerException(
"Null context for compound variable '" + name + "' on line " + line +
". '" + comps[ii - 1] + "' resolved to null.");
return null;
}
// once we step into a composite key, we drop the ability to query our parent
// contexts; that would be weird and confusing
@@ -214,12 +213,9 @@ public class Template
protected Object checkForMissing (String name, int line, boolean missingIsNull, Object value)
{
if (value == NO_FETCHER_FOUND) {
if (missingIsNull) {
return null;
} else {
throw new MustacheException(
"No method or field with name '" + name + "' on line " + line);
}
if (missingIsNull) return null;
throw new MustacheException(
"No method or field with name '" + name + "' on line " + line);
} else {
return value;
}
@@ -259,7 +259,7 @@ public class MustacheTest
execute(context("things", Arrays.asList("bar", "baz", "bif"))));
}
@Test public void testStructuredVariable () {
@Test public void testCompoundVariable () {
test("hello", "{{foo.bar.baz}}", new Object() {
Object foo () {
return new Object() {
@@ -271,25 +271,39 @@ public class MustacheTest
});
}
@Test(expected=NullPointerException.class)
@Test(expected=MustacheException.class)
public void testNullComponentInCompoundVariable () {
// make sure that even if we have a null value configured, we freak out
test(Mustache.compiler().nullValue("foo"), "unused", "{{foo.bar.baz}}", new Object() {
Object foo () {
return new Object() {
Object bar = null;
};
}
test(Mustache.compiler(), "unused", "{{foo.bar.baz}}", new Object() {
Object foo = new Object() {
Object bar = null;
};
});
}
@Test(expected=NullPointerException.class)
@Test(expected=MustacheException.class)
public void testMissingComponentInCompoundVariable () {
// make sure that even if we have a default value configured, we freak out
test(Mustache.compiler().defaultValue("foo"), "unused", "{{foo.bar.baz}}", new Object() {
Object foo () {
return new Object(); // no bar
}
test(Mustache.compiler(), "unused", "{{foo.bar.baz}}", new Object() {
Object foo = new Object(); // no bar
});
}
public void testNullComponentInCompoundVariableWithDefault () {
test(Mustache.compiler().nullValue("null"), "null", "{{foo.bar.baz}}", new Object() {
Object foo = null;
});
test(Mustache.compiler().nullValue("null"), "null", "{{foo.bar.baz}}", new Object() {
Object foo = new Object() {
Object bar = null;
};
});
}
public void testMissingComponentInCompoundVariableWithDefault () {
test(Mustache.compiler().defaultValue("?"), "?", "{{foo.bar.baz}}", new Object() {
// no foo, no bar
});
test(Mustache.compiler().defaultValue("?"), "?", "{{foo.bar.baz}}", new Object() {
Object foo = new Object(); // no bar
});
}