Skip to content

Commit 42948ac

Browse files
committed
hbs module: add doWith(Handlebars) callback fix #554
1 parent ed45ea0 commit 42948ac

2 files changed

Lines changed: 161 additions & 10 deletions

File tree

jooby-hbs/src/main/java/org/jooby/hbs/Hbs.java

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.LinkedList;
2626
import java.util.Set;
2727
import java.util.function.BiConsumer;
28+
import java.util.function.Consumer;
2829

2930
import org.jooby.Env;
3031
import org.jooby.Jooby;
@@ -54,9 +55,19 @@
5455
import com.typesafe.config.ConfigValueFactory;
5556

5657
/**
57-
* Exposes a {@link Handlebars} and a {@link Renderer}.
58+
* <h1>handlebars</h1>
59+
* <p>
60+
* Logic-less and semantic Mustache templates via
61+
* <a href="https://github.com/jknack/handlebars.java">handlebars.java</a>.
62+
* </p>
63+
*
64+
* <h2>exports</h2>
65+
* <ul>
66+
* <li>{@link Handlebars} object.</li>
67+
* <li>{@link Renderer} object.</li>
68+
* </ul>
5869
*
59-
* <h1>usage</h1>
70+
* <h2>usage</h2>
6071
* <p>
6172
* It is pretty straightforward:
6273
* </p>
@@ -81,7 +92,8 @@
8192
* file extension.
8293
* </p>
8394
*
84-
* <h1>helpers</h1>
95+
* <h2>options</h2>
96+
* <h13helpers</h3>
8597
* <p>
8698
* Simple/basic helpers are add it at startup time:
8799
* </p>
@@ -111,7 +123,7 @@
111123
* helper method.
112124
* </p>
113125
*
114-
* <h1>template loader</h1>
126+
* <h3>template loader</h3>
115127
* <p>
116128
* Templates are loaded from the root of classpath and must end with <code>.html</code>. You can
117129
* change the default template location and extensions too:
@@ -123,7 +135,7 @@
123135
* }
124136
* </pre>
125137
*
126-
* <h1>cache</h1>
138+
* <h3>cache</h3>
127139
* <p>
128140
* Cache is OFF when <code>env=dev</code> (useful for template reloading), otherwise is ON.
129141
* </p>
@@ -154,12 +166,19 @@ public class Hbs implements Jooby.Module {
154166

155167
private final Handlebars hbs;
156168

157-
private BiConsumer<Handlebars, Config> configurer;
169+
private BiConsumer<Handlebars, Config> callback;
158170

159171
private Set<Class<?>> helpers = new HashSet<>();
160172

161173
private Deque<ValueResolver> resolvers = new LinkedList<>();
162174

175+
/**
176+
* Creates a new {@link Hbs} module.
177+
*
178+
* @param prefix Template prefix.
179+
* @param suffix Template suffix.
180+
* @param helpers Optional list of helpers.
181+
*/
163182
public Hbs(final String prefix, final String suffix, final Class<?>... helpers) {
164183
this.hbs = new Handlebars(new ClassPathTemplateLoader(prefix, suffix));
165184
with(helpers);
@@ -173,26 +192,81 @@ public Hbs(final String prefix, final String suffix, final Class<?>... helpers)
173192
this.resolvers.add(FieldValueResolver.INSTANCE);
174193
}
175194

195+
/**
196+
* Creates a new {@link Hbs} module.
197+
*
198+
* @param prefix Template prefix.
199+
* @param helpers Optional list of helpers.
200+
*/
176201
public Hbs(final String prefix, final Class<?>... helpers) {
177202
this(prefix, ".html", helpers);
178203
}
179204

205+
/**
206+
* Creates a new {@link Hbs} module.
207+
*
208+
* @param helpers Optional list of helpers.
209+
*/
180210
public Hbs(final Class<?>... helpers) {
181211
this("/", helpers);
182212
}
183213

184-
public Hbs doWith(final BiConsumer<Handlebars, Config> configurer) {
185-
this.configurer = requireNonNull(configurer, "Configurer is required.");
214+
/**
215+
* Set a handlebars callback. Usage:
216+
*
217+
* <pre>{@code
218+
* {
219+
* use(new Hbs().doWith((hbs, conf) -> {
220+
* ...
221+
* });
222+
* }
223+
* }</pre>
224+
*
225+
* @param callback Configurer callback.
226+
* @return This module.
227+
*/
228+
public Hbs doWith(final BiConsumer<Handlebars, Config> callback) {
229+
this.callback = requireNonNull(callback, "Configurer is required.");
186230
return this;
187231
}
188232

233+
/**
234+
* Set a handlebars callback.
235+
*
236+
* <pre>{@code
237+
* {
238+
* use(new Hbs().doWith((hbs, conf) -> {
239+
* ...
240+
* });
241+
* }
242+
* }</pre>
243+
* @param callback Configurer callback.
244+
* @return This module.
245+
*/
246+
public Hbs doWith(final Consumer<Handlebars> callback) {
247+
requireNonNull(callback, "Configurer is required.");
248+
return doWith((hbs, conf) -> callback.accept(hbs));
249+
}
250+
251+
/**
252+
* Append one or more helper classes.
253+
*
254+
* @param helper Helper class.
255+
* @return This module.
256+
*/
189257
public Hbs with(final Class<?>... helper) {
190258
for (Class<?> h : helper) {
191259
helpers.add(h);
192260
}
193261
return this;
194262
}
195263

264+
/**
265+
* Append a {@link ValueResolver}.
266+
*
267+
* @param resolver Resolver.
268+
* @return This module.
269+
*/
196270
public Hbs with(final ValueResolver resolver) {
197271
requireNonNull(resolver, "Value resolver is required.");
198272
this.resolvers.addFirst(resolver);
@@ -213,8 +287,8 @@ public void configure(final Env env, final Config config, final Binder binder) {
213287
.build()));
214288
}
215289

216-
if (configurer != null) {
217-
configurer.accept(hbs, config);
290+
if (callback != null) {
291+
callback.accept(hbs, config);
218292
}
219293

220294
/** XSS */
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package org.jooby.hbs;
2+
3+
import static org.easymock.EasyMock.expect;
4+
import static org.easymock.EasyMock.isA;
5+
6+
import org.jooby.Env;
7+
import org.jooby.Renderer;
8+
import org.jooby.internal.hbs.HbsEngine;
9+
import org.jooby.internal.hbs.HbsHelpers;
10+
import org.jooby.test.MockUnit;
11+
import org.junit.Test;
12+
import org.junit.runner.RunWith;
13+
import org.powermock.core.classloader.annotations.PrepareForTest;
14+
import org.powermock.modules.junit4.PowerMockRunner;
15+
16+
import com.github.jknack.handlebars.Handlebars;
17+
import com.google.inject.Binder;
18+
import com.google.inject.binder.AnnotatedBindingBuilder;
19+
import com.google.inject.binder.LinkedBindingBuilder;
20+
import com.google.inject.multibindings.Multibinder;
21+
import com.google.inject.name.Names;
22+
import com.typesafe.config.Config;
23+
24+
@RunWith(PowerMockRunner.class)
25+
@PrepareForTest({Hbs.class, Multibinder.class })
26+
public class Issue554 {
27+
28+
@SuppressWarnings("unchecked")
29+
@Test
30+
public void doWith() throws Exception {
31+
new MockUnit(Env.class, Config.class, Binder.class)
32+
.expect(unit -> {
33+
Env env = unit.get(Env.class);
34+
expect(env.name()).andReturn("dev");
35+
})
36+
.expect(unit -> {
37+
AnnotatedBindingBuilder<Handlebars> hABB = unit.mock(AnnotatedBindingBuilder.class);
38+
hABB.toInstance(isA(Handlebars.class));
39+
40+
Binder binder = unit.get(Binder.class);
41+
expect(binder.bind(Handlebars.class)).andReturn(hABB);
42+
})
43+
.expect(unit -> {
44+
Binder binder = unit.get(Binder.class);
45+
46+
Multibinder<Object> mbinder = unit.mock(Multibinder.class);
47+
48+
unit.mockStatic(Multibinder.class);
49+
expect(Multibinder.newSetBinder(binder, Object.class, Names.named("hbs.helpers")))
50+
.andReturn(mbinder);
51+
52+
LinkedBindingBuilder<Renderer> fLBB = unit.mock(LinkedBindingBuilder.class);
53+
fLBB.toInstance(isA(HbsEngine.class));
54+
55+
Multibinder<Renderer> mfbinder = unit.mock(Multibinder.class);
56+
expect(mfbinder.addBinding()).andReturn(fLBB);
57+
expect(Multibinder.newSetBinder(binder, Renderer.class))
58+
.andReturn(mfbinder);
59+
60+
AnnotatedBindingBuilder<HbsHelpers> hhABB = unit.mock(AnnotatedBindingBuilder.class);
61+
hhABB.asEagerSingleton();
62+
63+
expect(binder.bind(HbsHelpers.class)).andReturn(hhABB);
64+
})
65+
.expect(unit -> {
66+
67+
})
68+
.run(unit -> {
69+
new Hbs()
70+
.doWith(hbs -> {
71+
hbs.setStartDelimiter("<<");
72+
hbs.setEndDelimiter("<<");
73+
})
74+
.configure(unit.get(Env.class), unit.get(Config.class), unit.get(Binder.class));
75+
});
76+
}
77+
}

0 commit comments

Comments
 (0)