Skip to content

Commit

Permalink
Template engines are broken
Browse files Browse the repository at this point in the history
- Add custom exception
- Remove eager filtering so fails with better error message
- fix  #3562
  • Loading branch information
jknack committed Oct 19, 2024
1 parent badd7ae commit 372430a
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 21 deletions.
17 changes: 17 additions & 0 deletions jooby/src/main/java/io/jooby/ModelAndView.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
Expand All @@ -19,6 +21,21 @@
*/
public class ModelAndView<T> {

/** Thrown by template engine when they are not capable of rendering a {@link ModelAndView}. */
public static class UnsupportedModelAndView extends IllegalArgumentException {
/**
* Constructor.
*
* @param supported List of supported model implementation.
*/
public UnsupportedModelAndView(Class<?>... supported) {
super(
"Only "
+ Set.of(supported).stream().map(Class::getName).collect(Collectors.joining(", "))
+ " are supported");
}
}

/** View name. */
private final String view;

Expand Down
2 changes: 1 addition & 1 deletion jooby/src/main/java/io/jooby/TemplateEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public interface TemplateEngine extends MessageEncoder {
* @return Rendered template.
* @throws Exception If something goes wrong.
*/
DataBuffer render(Context ctx, ModelAndView modelAndView) throws Exception;
DataBuffer render(Context ctx, ModelAndView<?> modelAndView) throws Exception;

@Override
default DataBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public List<String> extensions() {
}

@Override
public DataBuffer render(Context ctx, ModelAndView modelAndView) throws Exception {
public DataBuffer render(Context ctx, ModelAndView<?> modelAndView) throws Exception {
var buffer = ctx.getBufferFactory().allocateBuffer();
var template = freemarker.getTemplate(modelAndView.getView());
var writer = buffer.asWriter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public List<String> extensions() {
}

@Override
public DataBuffer render(Context ctx, ModelAndView modelAndView) throws Exception {
public DataBuffer render(Context ctx, ModelAndView<?> modelAndView) throws Exception {
var template = handlebars.compile(modelAndView.getView());
var engineModel =
com.github.jknack.handlebars.Context.newBuilder(modelAndView.getModel())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public List<String> extensions() {
}

@Override
public DataBuffer render(Context ctx, ModelAndView modelAndView) {
public DataBuffer render(Context ctx, ModelAndView<?> modelAndView) {
var buffer = ctx.getBufferFactory().allocateBuffer();
var output = new DataBufferOutput(buffer, StandardCharsets.UTF_8);
var attributes = ctx.getAttributes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,7 @@ public List<String> extensions() {
}

@Override
public boolean supports(@NonNull ModelAndView modelAndView) {
return TemplateEngine.super.supports(modelAndView) && modelAndView instanceof MapModelAndView;
}

@Override
public DataBuffer render(Context ctx, ModelAndView modelAndView) throws Exception {
public DataBuffer render(Context ctx, ModelAndView<?> modelAndView) throws Exception {
if (modelAndView instanceof MapModelAndView mapModelAndView) {
var buffer = ctx.getBufferFactory().allocateBuffer();
var template = engine.getTemplate(modelAndView.getView());
Expand All @@ -52,8 +47,7 @@ public DataBuffer render(Context ctx, ModelAndView modelAndView) throws Exceptio
template.evaluate(buffer.asWriter(), model, locale);
return buffer;
} else {
throw new IllegalArgumentException(
"Only " + MapModelAndView.class.getName() + " are supported");
throw new ModelAndView.UnsupportedModelAndView(MapModelAndView.class);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;

import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -59,6 +61,19 @@ public void render() throws Exception {
assertEquals("Hello foo bar var!", output.toString(StandardCharsets.UTF_8));
}

@Test
public void shouldUnsupportedModelAndView() {
PebbleEngine.Builder builder =
PebbleModule.create()
.build(new Environment(getClass().getClassLoader(), ConfigFactory.empty()));
PebbleTemplateEngine engine =
new PebbleTemplateEngine(builder, Collections.singletonList(".peb"));
MockContext ctx = new MockContext();
assertThrows(
ModelAndView.UnsupportedModelAndView.class,
() -> engine.render(ctx, new ModelAndView<>("index.peb", Map.of("foo", "bar"))));
}

@Test
public void renderFileSystem() throws Exception {
PebbleEngine.Builder builder =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,7 @@ public List<String> extensions() {
}

@Override
public boolean supports(@NonNull ModelAndView modelAndView) {
return io.jooby.TemplateEngine.super.supports(modelAndView)
&& modelAndView instanceof MapModelAndView;
}

@Override
public DataBuffer render(io.jooby.Context ctx, ModelAndView modelAndView) {
public @NonNull DataBuffer render(io.jooby.Context ctx, ModelAndView<?> modelAndView) {
if (modelAndView instanceof MapModelAndView mapModelAndView) {
Map<String, Object> model = new HashMap<>(ctx.getAttributes());
model.putAll(mapModelAndView.getModel());
Expand All @@ -59,8 +53,7 @@ public DataBuffer render(io.jooby.Context ctx, ModelAndView modelAndView) {
templateEngine.process(templateName, context, buffer.asWriter());
return buffer;
} else {
throw new IllegalArgumentException(
"Only " + MapModelAndView.class.getName() + " are supported");
throw new ModelAndView.UnsupportedModelAndView(MapModelAndView.class);
}
}
}
5 changes: 5 additions & 0 deletions tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
<artifactId>jooby-handlebars</artifactId>
<version>${jooby.version}</version>
</dependency>
<dependency>
<groupId>io.jooby</groupId>
<artifactId>jooby-pebble</artifactId>
<version>${jooby.version}</version>
</dependency>
<dependency>
<groupId>io.jooby</groupId>
<artifactId>jooby-freemarker</artifactId>
Expand Down
52 changes: 52 additions & 0 deletions tests/src/test/java/io/jooby/i3562/Issue3562.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Jooby https://jooby.io
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
package io.jooby.i3562;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Map;

import io.jooby.ModelAndView;
import io.jooby.junit.ServerTest;
import io.jooby.junit.ServerTestRunner;
import io.jooby.pebble.PebbleModule;
import io.jooby.thymeleaf.ThymeleafModule;

public class Issue3562 {
@ServerTest
public void unsupportedModelAndView(ServerTestRunner runner) {
runner
.define(
app -> {
app.install(new ThymeleafModule());
app.install(new PebbleModule());

app.get(
"/3562/thymeleaf",
ctx -> new ModelAndView<>("index.html", Map.of("name", "thymeleaf")));
app.get(
"/3562/pebble",
ctx -> new ModelAndView<>("index.pebble", Map.of("name", "Pebble")));

app.error((ctx, cause, status) -> ctx.send(cause.getMessage()));
})
.ready(
client -> {
client.get(
"/3562/thymeleaf",
rsp -> {
assertEquals(
"Only io.jooby.MapModelAndView are supported", rsp.body().string().trim());
});
client.get(
"/3562/pebble",
rsp -> {
assertEquals(
"Only io.jooby.MapModelAndView are supported", rsp.body().string().trim());
});
});
}
}
8 changes: 8 additions & 0 deletions tests/src/test/java/io/jooby/test/TemplateEngineTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.jooby.handlebars.HandlebarsModule;
import io.jooby.junit.ServerTest;
import io.jooby.junit.ServerTestRunner;
import io.jooby.pebble.PebbleModule;
import io.jooby.thymeleaf.ThymeleafModule;

public class TemplateEngineTest {
Expand All @@ -23,10 +24,12 @@ public void templateEngines(ServerTestRunner runner) {
app.install(new ThymeleafModule());
app.install(new HandlebarsModule());
app.install(new FreemarkerModule());
app.install(new PebbleModule());

app.get("/1", ctx -> ModelAndView.map("index.hbs").put("name", "Handlebars"));
app.get("/2", ctx -> ModelAndView.map("index.ftl").put("name", "Freemarker"));
app.get("/3", ctx -> ModelAndView.map("index.html").put("name", "Thymeleaf"));
app.get("/4", ctx -> ModelAndView.map("index.pebble").put("name", "Pebble"));
})
.ready(
client -> {
Expand Down Expand Up @@ -54,6 +57,11 @@ public void templateEngines(ServerTestRunner runner) {
+ "</html>",
rsp.body().string().replace("\r", "").trim());
});
client.get(
"/4",
rsp -> {
assertEquals("Hello Pebble!", rsp.body().string().trim());
});
});
}

Expand Down
1 change: 1 addition & 0 deletions tests/src/test/resources/views/index.pebble
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello {{name}}!

0 comments on commit 372430a

Please sign in to comment.