Skip to content

Commit

Permalink
Merge pull request #671 from hexagonkt/develop
Browse files Browse the repository at this point in the history
Add jte template adapter
  • Loading branch information
jaguililla committed Oct 23, 2023
2 parents 44fe263 + 01e3e7e commit 55b1301
Show file tree
Hide file tree
Showing 18 changed files with 323 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ package-lock.json
# System Files
.DS_Store
Thumbs.db

# TODO Delete this when the folder is no longer generated
jte-classes/
5 changes: 3 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ import org.gradle.api.tasks.wrapper.Wrapper.DistributionType.ALL
*/

plugins {
kotlin("jvm") version("1.9.10") apply(false)
kotlin("jvm") version("1.9.20-RC") apply(false)

id("idea")
id("eclipse")
id("project-report")
id("org.jetbrains.dokka") version("1.9.10")
id("com.github.jk1.dependency-license-report") version("2.5")
id("org.jetbrains.kotlinx.binary-compatibility-validator") version("0.13.2")
id("org.graalvm.buildtools.native") version("0.9.27") apply(false)
id("org.graalvm.buildtools.native") version("0.9.28") apply(false)
id("io.gitlab.arturbosch.detekt") version("1.23.1") apply(false)
id("me.champeau.jmh") version("0.7.1") apply(false)
}
Expand Down Expand Up @@ -160,6 +160,7 @@ apiValidation {
"rest",
"rest_tools",
"web",
"templates_jte",
)
)
}
9 changes: 6 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ org.gradle.warning.mode=all
org.gradle.console=plain

# Gradle
version=3.3.1
version=3.4.0
group=com.hexagonkt
description=The atoms of your platform

Expand All @@ -31,15 +31,15 @@ logoSmall=assets/img/logo.svg
iconsDirectory=content

# VERSIONS
kotlinVersion=1.9.10
kotlinVersion=1.9.20-RC
dokkaVersion=1.9.10
mockkVersion=1.13.8
junitVersion=5.10.0
gatlingVersion=3.9.5
jmhVersion=1.37
mkdocsMaterialVersion=9.4.6
mermaidDokkaVersion=0.4.4
nativeToolsVersion=0.9.27
nativeToolsVersion=0.9.28

# http_server_netty
nettyVersion=4.1.100.Final
Expand All @@ -66,6 +66,9 @@ dslJsonVersion=2.0.2
# templates_freemarker
freemarkerVersion=2.3.32

# templates_jte
jteVersion=3.1.3

# templates_pebble
pebbleVersion=3.2.1

Expand Down
3 changes: 2 additions & 1 deletion site/pages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Ports with their provided implementations (Adapters).
|-------------------------|-------------------------------------------------------|
| [HTTP Server] | [Netty], [Netty Epoll], [Jetty], [Servlet], [Helidon] |
| [HTTP Client] | [Jetty][Jetty Client] |
| [Templates] | [Pebble], [FreeMarker], [Rocker] |
| [Templates] | [Pebble], [FreeMarker], [Rocker], [jte] |
| [Serialization Formats] | [JSON], [YAML], [CSV], [XML], [TOML] |

[HTTP Server]: /http_server
Expand All @@ -159,6 +159,7 @@ Ports with their provided implementations (Adapters).
[Pebble]: /templates_pebble
[FreeMarker]: /templates_freemarker
[Rocker]: /templates_rocker
[jte]: /templates_jte
[Serialization Formats]: /core/#serialization
[JSON]: /api/serialization_jackson_json/com.hexagonkt.serialization.jackson.json/-json
[YAML]: /api/serialization_jackson_yaml/com.hexagonkt.serialization.jackson.yaml/-yaml
Expand Down
2 changes: 0 additions & 2 deletions templates/templates/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

# Module templates

This port provides a common interface for rendering templates with multiple different template
engines.

Expand Down Expand Up @@ -35,5 +34,4 @@ and `_now_` variables) are added to the context automatically. Check the code be
[TemplateManager]: /api/templates/com.hexagonkt.templates/-template-manager/index.html

# Package com.hexagonkt.templates

Feature implementation code.
2 changes: 0 additions & 2 deletions templates/templates_freemarker/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

# Module templates_freemarker

This module provides an adapter for the templates Port supporting the Apache [FreeMarker] template
engine.

Expand Down Expand Up @@ -31,5 +30,4 @@ For usage instructions, refer to the [Templates Port documentation](/templates/)
```

# Package com.hexagonkt.templates.freemarker

Classes that implement the Templates Port interface with the [FreeMarker] engine.
63 changes: 63 additions & 0 deletions templates/templates_jte/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

# Module templates_jte
[jte] template engine adapter for Hexagon.

For usage instructions, refer to the [Templates Port documentation](/templates/).

[jte]: https://jte.gg

### Install the Dependency

=== "build.gradle"

```groovy
repositories {
mavenCentral()
}

implementation("com.hexagonkt:templates_jte:$hexagonVersion")
```

=== "pom.xml"

```xml
<dependency>
<groupId>com.hexagonkt</groupId>
<artifactId>templates_jte</artifactId>
<version>$hexagonVersion</version>
</dependency>
```

## Use the Adapter
In order to use this adapter you need to set up a build plugin to compile the templates. To do so in
Gradle, add the following lines to `build.gradle.kts`:

```kotlin
plugins {
id("gg.jte.gradle") version("3.1.3")
}

dependencies {
"jteGenerate"("gg.jte:jte-native-resources:$jteVersion")
}

tasks.named("compileKotlin") { dependsOn("generateJte") }

jte {
sourceDirectory.set(projectDir.resolve("src/main/resources/templates").toPath())
contentType.set(gg.jte.ContentType.Html)

jteExtension("gg.jte.nativeimage.NativeResourcesExtension")

generate()
}
```

# TODO
* Don't create `jte-classes` directory
* Generate template classes only for tests
* Test file loaded templates
* Test plain test templates

# Package com.hexagonkt.templates.jte
Classes that implement the Templates Port interface with the [jte] engine.
46 changes: 46 additions & 0 deletions templates/templates_jte/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import gg.jte.ContentType.Html

plugins {
id("java-library")
id("gg.jte.gradle") version("3.1.3")
}

apply(from = "$rootDir/gradle/kotlin.gradle")
apply(from = "$rootDir/gradle/publish.gradle")
apply(from = "$rootDir/gradle/dokka.gradle")
apply(from = "$rootDir/gradle/native.gradle")
apply(from = "$rootDir/gradle/detekt.gradle")

description = "Template processor adapter for 'jte'."

dependencies {
val jteVersion = properties["jteVersion"]

"api"(project(":templates:templates"))
"api"("gg.jte:jte:$jteVersion")

"testImplementation"(project(":templates:templates_test"))
"testImplementation"(project(":serialization:serialization_jackson_json"))

"jteGenerate"("gg.jte:jte-native-resources:$jteVersion")
}

tasks.named("compileKotlin") { dependsOn("generateJte") }
tasks.named("processResources") { dependsOn("processTestResources") }
tasks.named("detektMain") { dependsOn("compileTestKotlin") }
tasks.named("sourcesJar") { dependsOn("compileTestKotlin") }

// TODO Remove when settings prevent this directory from being created (check .gitignore also)
tasks.named<Delete>("clean") {
delete("jte-classes")
}

jte {
sourceDirectory.set(projectDir.resolve("src/test/resources/templates").toPath())
targetDirectory.set(projectDir.resolve("build/classes/kotlin/test").toPath())
contentType.set(Html)

jteExtension("gg.jte.nativeimage.NativeResourcesExtension")

generate()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.hexagonkt.templates.jte

import com.hexagonkt.core.media.MediaType
import com.hexagonkt.core.media.TEXT_HTML
import com.hexagonkt.core.media.TEXT_PLAIN
import com.hexagonkt.templates.TemplatePort
import gg.jte.CodeResolver
import gg.jte.ContentType
import gg.jte.TemplateEngine
import gg.jte.TemplateOutput
import gg.jte.output.StringOutput
import gg.jte.resolve.DirectoryCodeResolver
import gg.jte.resolve.ResourceCodeResolver
import java.net.URL
import java.nio.file.Path
import java.util.*

class JteAdapter(
mediaType: MediaType,
resolverBase: URL? = null,
precompiled: Boolean = false
) : TemplatePort {

private companion object {
val allowedTypes: String = setOf(TEXT_HTML, TEXT_PLAIN).joinToString(", ") { it.fullType }
}

private val contentType = when (mediaType) {
TEXT_HTML -> ContentType.Html
TEXT_PLAIN -> ContentType.Plain
else ->
error("Unsupported media type not in: $allowedTypes (${mediaType.fullType})")
}

private val resolver: CodeResolver =
when (resolverBase?.protocol) {
"classpath" -> ResourceCodeResolver(resolverBase.path)
"file" -> DirectoryCodeResolver(Path.of(resolverBase.path))
null -> ResourceCodeResolver("")
else -> error("Invalid base schema not in: classpath, file (${resolverBase.protocol})")
}

private val templateEngine: TemplateEngine =
if (precompiled) {
if (resolverBase === null) {
TemplateEngine.createPrecompiled(contentType)
}
else {
val protocol = resolverBase.protocol
check(protocol == "classpath") {
"Precompiled base must be classpath URLs ($protocol)"
}
TemplateEngine.createPrecompiled(Path.of(resolverBase.path), contentType)
}
}
else {
TemplateEngine.create(resolver, contentType)
}

override fun render(url: URL, context: Map<String, *>, locale: Locale): String {
val output: TemplateOutput = StringOutput()
templateEngine.render(url.path, context, output)
return output.toString()
}

override fun render(
name: String, templates: Map<String, String>, context: Map<String, *>, locale: Locale
): String =
throw UnsupportedOperationException("jte does not support memory templates")
}
11 changes: 11 additions & 0 deletions templates/templates_jte/src/main/kotlin/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

module com.hexagonkt.templates_jte {

requires transitive kotlin.stdlib;
requires transitive com.hexagonkt.templates;

requires gg.jte;
requires gg.jte.runtime;

exports com.hexagonkt.templates.jte;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.hexagonkt.templates.jte

import com.hexagonkt.core.media.TEXT_CSS
import com.hexagonkt.core.media.TEXT_HTML
import com.hexagonkt.core.urlOf
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.DisabledInNativeImage
import java.time.LocalDateTime
import java.util.Locale
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

internal class JteAdapterTest {

private val locale = Locale.getDefault()

@Test
@DisabledInNativeImage
fun `Dates are converted properly`() {
val context = mapOf("localDate" to LocalDateTime.of(2000, 12, 31, 23, 45))
val resource = "classpath:templates/test.jte"
val html = JteAdapter(TEXT_HTML).render(urlOf(resource), context, locale)
assert(html.contains("23:45"))
assert(html.contains("2000"))
assert(html.contains("31"))
}

@Test fun `Dates are converted properly with precompiled templates`() {
val context = mapOf("localDate" to LocalDateTime.of(2000, 12, 31, 23, 45))
val resource = "classpath:test.jte"
val adapter = JteAdapter(TEXT_HTML, precompiled = true)
val html = adapter.render(urlOf(resource), context, locale)
assert(html.contains("23:45"))
assert(html.contains("2000"))
assert(html.contains("31"))
}

@Test fun `Literal templates are not supported`() {
val context = mapOf("localDate" to LocalDateTime.of(2000, 12, 31, 23, 45))
val e = assertFailsWith<UnsupportedOperationException> {
JteAdapter(TEXT_HTML).render("template code", context, locale)
}
assertEquals("jte does not support memory templates", e.message)
}

@Test fun `Invalid jte adapters throw exceptions on creation`() {
assertIllegalState("Unsupported media type not in: text/html, text/plain (text/css)") {
JteAdapter(TEXT_CSS)
}
assertIllegalState("Invalid base schema not in: classpath, file (http)") {
JteAdapter(TEXT_HTML, urlOf("http://example.com"))
}
assertIllegalState("Precompiled base must be classpath URLs (file)") {
JteAdapter(TEXT_HTML, urlOf("file://example.com"), true)
}
}

private inline fun assertIllegalState(message: String, block: () -> Unit) {
assertEquals(message, assertFailsWith(IllegalStateException::class, block).message)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.hexagonkt.templates.jte

import com.hexagonkt.core.media.TEXT_HTML
import com.hexagonkt.core.urlOf
import com.hexagonkt.templates.test.TemplateAdapterTest

internal class JteTemplateAdapterPrecompiledBaseTest :
TemplateAdapterTest(
urlOf("classpath:test.jte"),
JteAdapter(TEXT_HTML, urlOf("classpath:/"), precompiled = true)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.hexagonkt.templates.jte

import com.hexagonkt.core.media.TEXT_HTML
import com.hexagonkt.core.urlOf
import com.hexagonkt.templates.test.TemplateAdapterTest

internal class JteTemplateAdapterPrecompiledTest :
TemplateAdapterTest(urlOf("classpath:test.jte"), JteAdapter(TEXT_HTML, precompiled = true))

0 comments on commit 55b1301

Please sign in to comment.