Skip to content

Commit 11c6378

Browse files
committed
jte: model support
- fix #3602 - ref #3599
1 parent af81aea commit 11c6378

File tree

9 files changed

+218
-10
lines changed

9 files changed

+218
-10
lines changed

docs/asciidoc/modules/jte.adoc

Lines changed: 119 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ https://jte.gg[Jte] Secure and speedy templates for Java and Kotlin.
1717
<plugin>
1818
<groupId>gg.jte</groupId>
1919
<artifactId>jte-maven-plugin</artifactId>
20-
<version>${jte.version}</version>
20+
<version>{jte_version}</version>
2121
<configuration>
2222
<sourceDirectory>${basedir}/src/main/jte</sourceDirectory> <!-- This is the directory where your .jte files are located. -->
2323
<contentType>Html</contentType>
@@ -38,11 +38,11 @@ https://jte.gg[Jte] Secure and speedy templates for Java and Kotlin.
3838
----
3939
plugins {
4040
id 'java'
41-
id 'gg.jte.gradle' version '${jte.version}'
41+
id 'gg.jte.gradle' version '{jte_version}'
4242
}
4343
4444
dependencies {
45-
implementation('gg.jte:jte:${jte.version}')
45+
implementation('gg.jte:jte:{jte_version}')
4646
}
4747
4848
jte {
@@ -70,10 +70,10 @@ NOTE: Complete code generator options are https://github.com/casid/jte/blob/main
7070
import io.jooby.jte.JteModule;
7171
7272
{
73-
install(new JteModule(Paths.of("src", "main", "jte"))); <1>
73+
install(new JteModule(Paths.get("src", "main", "jte"))); <1>
7474
7575
get("/", ctx -> {
76-
return new ModelAndView("hello.jte", Map.of("name", "Jte")); <2>
76+
return new MapModelAndView("hello.jte", Map.of("name", "Jte")); <2>
7777
});
7878
}
7979
----
@@ -84,10 +84,10 @@ import io.jooby.jte.JteModule;
8484
import io.jooby.jte.JteModule
8585
8686
{
87-
install(JteModule(Paths.of("src", "main", "jte"))) <1>
87+
install(JteModule(Paths.get("src", "main", "jte"))) <1>
8888
8989
get("/") {
90-
ModelAndView("hello.jte", Map.of("name", "Jte")) <2>
90+
MapModelAndView("hello.jte", Map.of("name", "Jte")) <2>
9191
}
9292
}
9393
----
@@ -100,13 +100,121 @@ will put all the generated classes in `src/main/jte/jte-classes`.
100100

101101
In production will read the classes from classpath.
102102

103+
=== Models
104+
105+
jte-models is a generator extension for jte that creates a typesafe facade for rendering templates.
106+
107+
1) Add the dependency:
108+
109+
[dependency, groupId="gg.jte", artifactId="jte-models", version="jte.version"]
110+
.
111+
112+
2) Configure code generator
113+
114+
.Maven
115+
[source,xml,role="primary",subs="verbatim,attributes"]
116+
----
117+
<plugin>
118+
<groupId>gg.jte</groupId>
119+
<artifactId>jte-maven-plugin</artifactId>
120+
<version>{jte_version}</version>
121+
<configuration>
122+
<sourceDirectory>${project.basedir}/src/main/jte</sourceDirectory>
123+
<contentType>Html</contentType>
124+
<extensions>
125+
<extension>
126+
<className>gg.jte.models.generator.ModelExtension</className>
127+
</extension>
128+
</extensions>
129+
</configuration>
130+
<executions>
131+
<execution>
132+
<phase>generate-sources</phase>
133+
<goals>
134+
<goal>generate</goal>
135+
</goals>
136+
</execution>
137+
</executions>
138+
<dependencies>
139+
<dependency>
140+
<groupId>gg.jte</groupId>
141+
<artifactId>jte-models</artifactId>
142+
<version>{jte_version}</version>
143+
</dependency>
144+
</dependencies>
145+
</plugin>
146+
----
147+
148+
.Gradle
149+
[source,groovy,role="secondary",subs="verbatim,attributes"]
150+
----
151+
plugins {
152+
id 'gg.jte.gradle' version '{jte_version}'
153+
}
154+
155+
dependencies {
156+
implementation 'gg.jte:jte-runtime:{jte_version}'
157+
jteGenerate 'gg.jte:jte-models:{jte_version}'
158+
}
159+
160+
jte {
161+
generate()
162+
binaryStaticContent = true
163+
jteExtension 'gg.jte.models.generator.ModelExtension'
164+
}
165+
----
166+
167+
==== Usage
168+
169+
.Java
170+
[source,java,role="primary"]
171+
----
172+
import io.jooby.jte.JteModule;
173+
174+
{
175+
install(new JteModule(Paths.get("src", "main", "jte")));
176+
177+
get("/static", ctx -> {
178+
var templates = new StaticTemplates();
179+
return templates.helloWorld("Hi!");
180+
});
181+
182+
get("/dynamic", ctx -> {
183+
var templates = new DynamicTemplates(require(TemplateEngine.class));
184+
return templates.helloWorld("Hi!");
185+
});
186+
}
187+
----
188+
189+
.Kotlin
190+
[source, kt, role="secondary"]
191+
----
192+
import io.jooby.jte.JteModule
193+
194+
{
195+
install(JteModule(Paths.get("src", "main", "jte")))
196+
197+
get("/static") {
198+
val templates = StaticTemplates()
199+
templates.helloWorld("Hi!")
200+
}
201+
202+
get("/dynamic") {
203+
val templates = DynamicTemplates(require(TemplateEngine::class))
204+
templates.helloWorld("Hi!")
205+
}
206+
}
207+
----
208+
209+
More at https://jte.gg/jte-models/[jte-models].
210+
103211
=== Options
104212

105213
==== Custom class directory
106214

107215
If you prefer a custom directory for compiled templates you need to do use:
108216

109-
install(new JteModule(Paths.of("src", "main", "jte"), Paths.of("compiled-templates")));
217+
install(new JteModule(Paths.get("src", "main", "jte"), Paths.get("compiled-templates")));
110218

111219
Also, you need to configure Maven or Gradle to generate templates classes:
112220

@@ -156,5 +264,8 @@ You need to make sure to copy the `compiled-templates` folder as part of your de
156264

157265
It is possible to provide your own/custom template engine:
158266

267+
[source,java]
268+
----
159269
var templateEngine = TemplateEngine.create(...) or TemplateEngine.createPrecompiled(..)
160270
install(new JteModule(templateEngine));
271+
----

docs/src/main/java/io/jooby/adoc/Dependencies.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import java.nio.file.Path;
1313
import java.nio.file.Paths;
1414
import java.util.*;
15+
import java.util.function.BiConsumer;
16+
import java.util.function.Consumer;
1517
import java.util.stream.Stream;
1618

1719
import org.jsoup.Jsoup;
@@ -38,13 +40,16 @@ public String toString() {
3840

3941
private Map<String, Dependency> dependencyMap = new TreeMap<>();
4042

43+
private Map<String, String> properties = new TreeMap<>();
44+
4145
private static final Dependencies instance = new Dependencies();
4246

4347
private Dependencies() {
4448
try {
4549
for (Document pom : pomList()) {
4650
collectDependencies(pom, pom.select("dependencyManagement").select("dependencies"));
4751
collectDependencies(pom, pom.select("dependencies"));
52+
properties(pom, properties::putIfAbsent);
4853
}
4954
} catch (IOException x) {
5055
throw SneakyThrows.propagate(x);
@@ -70,6 +75,10 @@ public static Dependencies.Dependency get(String artifactId) {
7075
return dep;
7176
}
7277

78+
public static String version(String property) {
79+
return instance.properties.getOrDefault(property, property);
80+
}
81+
7382
private List<Document> pomList() throws IOException {
7483
List<Document> poms = new ArrayList<>();
7584
Document jooby =
@@ -122,4 +131,8 @@ private static String findVersion(Document pom, String artifactId, String versio
122131
}
123132
return versionRef;
124133
}
134+
135+
private static void properties(Document pom, BiConsumer<String, String> properties) {
136+
pom.select("properties > *").forEach(e -> properties.accept(e.tagName(), e.text()));
137+
}
125138
}

docs/src/main/java/io/jooby/adoc/DependencyProcessor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public Object process(StructuralNode parent, Reader reader, Map<String, Object>
3434
throw new IllegalArgumentException("Dependency without version: " + groupId + ":" + Arrays.toString(artifactId));
3535
}
3636
}
37+
} else {
38+
version = Dependencies.version(version);
3739
}
3840
maven(
3941
groupId,

modules/jooby-jte/pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,14 @@
2323
<dependency>
2424
<groupId>gg.jte</groupId>
2525
<artifactId>jte</artifactId>
26-
<version>3.1.15</version>
26+
<version>${jte.version}</version>
27+
</dependency>
28+
29+
<dependency>
30+
<groupId>gg.jte</groupId>
31+
<artifactId>jte-models</artifactId>
32+
<version>${jte.version}</version>
33+
<optional>true</optional>
2734
</dependency>
2835

2936
<!-- Test dependencies -->
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.jte;
7+
8+
import java.nio.charset.StandardCharsets;
9+
10+
import edu.umd.cs.findbugs.annotations.NonNull;
11+
import edu.umd.cs.findbugs.annotations.Nullable;
12+
import gg.jte.models.runtime.JteModel;
13+
import io.jooby.Context;
14+
import io.jooby.buffer.DataBuffer;
15+
16+
public class JteModelEncoder implements io.jooby.MessageEncoder {
17+
@Nullable @Override
18+
public DataBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
19+
if (value instanceof JteModel jte) {
20+
var buffer = ctx.getBufferFactory().allocateBuffer();
21+
var output = new DataBufferOutput(buffer, StandardCharsets.UTF_8);
22+
jte.render(output);
23+
return buffer;
24+
}
25+
return null;
26+
}
27+
}

modules/jooby-jte/src/main/java/io/jooby/jte/JteModule.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222
import io.jooby.Jooby;
2323
import io.jooby.MediaType;
2424
import io.jooby.ServiceRegistry;
25+
import io.jooby.internal.jte.JteModelEncoder;
2526

2627
/**
27-
* Jte templates: https://jte.gg.
28+
* Jte templates: https://jooby.io/modules/jte
2829
*
2930
* <pre>
3031
* </pre>
@@ -85,7 +86,10 @@ public void install(@NonNull Jooby application) {
8586

8687
ServiceRegistry services = application.getServices();
8788
services.put(TemplateEngine.class, templateEngine);
89+
// model and view
8890
application.encoder(MediaType.html, new JteTemplateEngine(templateEngine));
91+
// jte models
92+
application.encoder(new JteModelEncoder());
8993
}
9094

9195
/**

modules/jooby-jte/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111
requires static com.github.spotbugs.annotations;
1212
requires gg.jte;
1313
requires gg.jte.runtime;
14+
requires static gg.jte.models;
1415
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.jte;
7+
8+
import static org.mockito.ArgumentMatchers.isA;
9+
import static org.mockito.Mockito.*;
10+
11+
import java.util.Map;
12+
13+
import org.junit.jupiter.api.Test;
14+
15+
import gg.jte.TemplateOutput;
16+
import gg.jte.models.runtime.JteModel;
17+
import io.jooby.Context;
18+
import io.jooby.buffer.DataBuffer;
19+
import io.jooby.buffer.DataBufferFactory;
20+
import io.jooby.internal.jte.JteModelEncoder;
21+
22+
public class Issue3602 {
23+
24+
@Test
25+
public void shouldRenderJteModel() throws Exception {
26+
var bufferFactory = mock(DataBufferFactory.class);
27+
var buffer = mock(DataBuffer.class);
28+
when(bufferFactory.allocateBuffer()).thenReturn(buffer);
29+
30+
var attributes = Map.<String, Object>of("foo", 1);
31+
var ctx = mock(Context.class);
32+
when(ctx.getBufferFactory()).thenReturn(bufferFactory);
33+
when(ctx.getAttributes()).thenReturn(attributes);
34+
35+
var model = mock(JteModel.class);
36+
37+
var engine = new JteModelEncoder();
38+
engine.encode(ctx, model);
39+
40+
verify(model, times(1)).render(isA(TemplateOutput.class));
41+
}
42+
}

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<yasson.version>3.0.4</yasson.version>
3030
<rocker.version>2.1.0</rocker.version>
3131
<thymeleaf.version>3.1.3.RELEASE</thymeleaf.version>
32+
<jte.version>3.1.15</jte.version>
3233

3334
<!-- data -->
3435
<HikariCP.version>6.2.1</HikariCP.version>

0 commit comments

Comments
 (0)