Skip to content

Commit e0f7722

Browse files
authored
Merge pull request #1 from Iterable/create-internal-storage
Create a new field for internal, unresolved storage
2 parents 1cd3eac + ca2937d commit e0f7722

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

handlebars/src/main/java/com/github/jknack/handlebars/Context.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ protected BlockParam(final Context parent, final Map<String, Object> hash) {
7575
this.extendedContext.resolver = parent.resolver;
7676
this.parent = parent;
7777
this.data = parent.data;
78+
this.internalData = parent.internalData;
7879
this.resolver = parent.resolver;
7980
}
8081

@@ -152,6 +153,7 @@ protected PartialCtx(final Context parent, final Object model, final Map<String,
152153
this.extendedContext.extendedContext = new Context(Collections.emptyMap());
153154
this.parent = parent;
154155
this.data = parent.data;
156+
this.internalData = parent.internalData;
155157
this.resolver = parent.resolver;
156158
}
157159

@@ -432,6 +434,11 @@ public Object eval(final ValueResolver resolver, final Context context, final Ob
432434
*/
433435
protected Map<String, Object> data;
434436

437+
/**
438+
* Functions similarly to data, but not resolved during rendering.
439+
*/
440+
protected Map<String, Object> internalData;
441+
435442
/**
436443
* Additional, data can be stored here.
437444
*/
@@ -470,6 +477,7 @@ private static Context root(final Object model) {
470477
root.data.put(INLINE_PARTIALS, partials);
471478
root.data.put(INVOCATION_STACK, new LinkedList<TemplateSource>());
472479
root.data.put("root", model);
480+
root.internalData = new HashMap<>();
473481
return root;
474482
}
475483

@@ -535,6 +543,41 @@ public Context data(final Map<String, ?> attributes) {
535543
return this;
536544
}
537545

546+
/**
547+
* Read the attribute from the internal data storage.
548+
*
549+
* @param name The attribute's name.
550+
* @param <T> Data type.
551+
* @return The attribute value or null.
552+
*/
553+
@SuppressWarnings("unchecked")
554+
public <T> T internalData(final String name) {
555+
return (T) internalData.get(name);
556+
}
557+
558+
/**
559+
* Set an attribute in the internal data storage.
560+
*
561+
* @param name The attribute's name. Required.
562+
* @param value The attribute's value. Required.
563+
* @return This context.
564+
*/
565+
public Context internalData(final String name, final Object value) {
566+
internalData.put(name, value);
567+
return this;
568+
}
569+
570+
/**
571+
* Store the map in the internal data storage.
572+
*
573+
* @param attributes The attributes to add. Required.
574+
* @return This context.
575+
*/
576+
public Context internalData(final Map<String, ?> attributes) {
577+
internalData.putAll(attributes);
578+
return this;
579+
}
580+
538581
/**
539582
* Resolved as '.' or 'this' inside templates.
540583
*
@@ -692,6 +735,9 @@ public void destroy() {
692735
if (data != null) {
693736
data.clear();
694737
}
738+
if (internalData != null) {
739+
internalData.clear();
740+
}
695741
}
696742
if (extendedContext != null) {
697743
extendedContext.destroy();
@@ -790,6 +836,7 @@ private Context newChild(final Object model) {
790836
child.setResolver(this.resolver);
791837
child.parent = this;
792838
child.data = this.data;
839+
child.internalData = this.internalData;
793840
return child;
794841
}
795842

@@ -813,6 +860,7 @@ protected Context newChildContext(final Object model) {
813860
public static Context copy(final Context context, final Object model) {
814861
Context ctx = Context.newContext(model);
815862
ctx.data = context.data;
863+
ctx.internalData = context.internalData;
816864
ctx.resolver = context.resolver;
817865
return ctx;
818866
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.github.jknack.handlebars;
2+
3+
import com.github.jknack.handlebars.context.FieldValueResolver;
4+
import com.github.jknack.handlebars.context.JavaBeanValueResolver;
5+
import com.github.jknack.handlebars.context.MapValueResolver;
6+
import com.github.jknack.handlebars.context.MethodValueResolver;
7+
import java.io.IOException;
8+
9+
import org.junit.Test;
10+
11+
import static org.junit.Assert.assertEquals;
12+
import static org.junit.Assert.assertNull;
13+
14+
public class InternalDataTest extends AbstractTest {
15+
16+
@Override
17+
protected Handlebars newHandlebars() {
18+
Handlebars handlebars = new Handlebars();
19+
handlebars.registerHelper("printFooAndBar", (context, options) ->
20+
String.format("%s %s", options.context.data("foo"), options.context.internalData("bar")));
21+
return handlebars;
22+
}
23+
24+
@Override
25+
protected Object configureContext(final Object model) {
26+
return Context.newBuilder(model)
27+
.resolver(
28+
MapValueResolver.INSTANCE,
29+
JavaBeanValueResolver.INSTANCE,
30+
FieldValueResolver.INSTANCE,
31+
MethodValueResolver.INSTANCE)
32+
.build()
33+
.data("foo", "foo")
34+
.internalData("bar", "bar");
35+
}
36+
37+
@Test
38+
public void dataAvailableForRendering() throws IOException {
39+
shouldCompileTo("{{foo}}", "", "foo");
40+
assertEquals("foo", ((Context)configureContext("")).get("foo"));
41+
}
42+
43+
@Test
44+
public void internalDataNotAvailableForRendering() throws IOException {
45+
shouldCompileTo("{{bar}}", "", "");
46+
shouldCompileTo("{{./bar}}", "", "");
47+
shouldCompileTo("{{../bar}}", "", "");
48+
shouldCompileTo("{{.././bar}}", "", "");
49+
shouldCompileTo("{{this.bar}}", "", "");
50+
shouldCompileTo("{{internalData}}", "", "");
51+
shouldCompileTo("{{internalData.bar}}", "", "");
52+
shouldCompileTo("{{this.internalData}}", "", "");
53+
shouldCompileTo("{{this.internalData.bar}}", "", "");
54+
assertNull(((Context)configureContext("")).get("bar"));
55+
}
56+
57+
@Test
58+
public void helperAbleToAccessInternalData() throws IOException {
59+
shouldCompileTo("{{printFooAndBar}}", "", "foo bar");
60+
}
61+
}

0 commit comments

Comments
 (0)