From 21c85ea93b919d6e5b7af0dac0d8510f80052683 Mon Sep 17 00:00:00 2001 From: Cedric Champeau Date: Sun, 5 Mar 2023 15:10:46 +0100 Subject: [PATCH] Add support for extra template directories By default, the template was using `src/main/templates` as the source directory for templates. This has the benefit of properly isolating the templates from the application classpath: the framework is then responsible for injecting them to the app runtime in whatever suitable form: either untouched as sources (in development mode), or precompiled (for production). With this change, it is also possible to use `src/main/resources/templates` as a source directory. It _may_ feel more natural to users (although I would disagree), but it has the drawback that the jars have to be configured to _exclude_ those resources, because by default whatever is in `src/main/resources` MUST be included in a jar (independently of the run mode). It is also possible for the user to configure additional source directories should they want to. --- app/src/main/java/hello/App.java | 3 +- app/src/main/resources/templates/world.html | 11 +++++ .../rife2/gradle/PrecompileTemplates.java | 40 +++++++++++-------- .../com/uwyn/rife2/gradle/Rife2Plugin.java | 24 ++++++++--- .../src/main/resources/templates/world.html | 11 +++++ .../uwyn/rife2/gradle/PackagingTest.groovy | 3 ++ .../gradle/TemplateCompilationTest.groovy | 1 + 7 files changed, 70 insertions(+), 23 deletions(-) create mode 100644 app/src/main/resources/templates/world.html create mode 100644 build-logic/src/test-projects/minimal/src/main/resources/templates/world.html diff --git a/app/src/main/java/hello/App.java b/app/src/main/java/hello/App.java index a7bed73..f83ec43 100644 --- a/app/src/main/java/hello/App.java +++ b/app/src/main/java/hello/App.java @@ -6,6 +6,7 @@ public class App extends Site { public void setup() { var hello = get("/hello", c -> c.print(c.template("hello"))); get("/", c -> c.redirect(hello)); + get("/world", c -> c.print(c.template("world"))); } public static void main(String[] args) { @@ -13,4 +14,4 @@ public class App extends Site { .staticResourceBase("src/main/webapp") .start(new App()); } -} \ No newline at end of file +} diff --git a/app/src/main/resources/templates/world.html b/app/src/main/resources/templates/world.html new file mode 100644 index 0000000..92637b1 --- /dev/null +++ b/app/src/main/resources/templates/world.html @@ -0,0 +1,11 @@ + + + + + <!--v title-->Hello<!--/v--> + + + +

Hello World from src/main/resources/templates

+ + diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java index 8b9ab9a..eac8e6c 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/PrecompileTemplates.java @@ -23,7 +23,7 @@ import org.gradle.api.provider.Property; import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.PathSensitive; @@ -32,6 +32,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.process.ExecOperations; import javax.inject.Inject; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; @@ -41,9 +42,9 @@ public abstract class PrecompileTemplates extends DefaultTask { @Classpath public abstract ConfigurableFileCollection getClasspath(); - @InputDirectory + @InputFiles @PathSensitive(PathSensitivity.RELATIVE) - public abstract DirectoryProperty getTemplatesDirectory(); + public abstract ConfigurableFileCollection getTemplatesDirectories(); @Input public abstract ListProperty getTypes(); @@ -65,22 +66,27 @@ public abstract class PrecompileTemplates extends DefaultTask { @TaskAction public void precompileTemplates() { for (var type : getTypes().get()) { - getExecOperations().javaexec(javaexec -> { - javaexec.setClasspath(getClasspath()); - javaexec.getMainClass().set("rife.template.TemplateDeployer"); - List args = new ArrayList<>(); - if (getVerbose().isPresent() && Boolean.TRUE.equals(getVerbose().get())) { - args.add("-verbose"); + getTemplatesDirectories().getFiles().forEach(dir -> { + if (Files.exists(dir.toPath())) { + getExecOperations().javaexec(javaexec -> { + javaexec.setClasspath(getClasspath()); + javaexec.getMainClass().set("rife.template.TemplateDeployer"); + List args = new ArrayList<>(); + if (getVerbose().isPresent() && Boolean.TRUE.equals(getVerbose().get())) { + args.add("-verbose"); + } + args.add("-t"); + args.add(type.identifier()); + args.add("-d"); + args.add(getOutputDirectory().get().getAsFile().getPath()); + args.add("-encoding"); + args.add(getEncoding().orElse("UTF-8").get()); + args.add(dir.getPath()); + javaexec.args(args); + }); } - args.add("-t"); - args.add(type.identifier()); - args.add("-d"); - args.add(getOutputDirectory().get().getAsFile().getPath()); - args.add("-encoding"); - args.add(getEncoding().orElse("UTF-8").get()); - args.add(getTemplatesDirectory().get().getAsFile().getPath()); - javaexec.args(args); }); + } } } diff --git a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java index 2777742..c7054e0 100644 --- a/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java +++ b/build-logic/src/main/java/com/uwyn/rife2/gradle/Rife2Plugin.java @@ -15,7 +15,8 @@ */ package com.uwyn.rife2.gradle; -import org.gradle.api.*; +import org.gradle.api.Plugin; +import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.dsl.DependencyHandler; @@ -38,10 +39,11 @@ import org.gradle.api.tasks.testing.Test; import org.gradle.process.CommandLineArgumentProvider; import java.util.Collections; +import java.util.List; import java.util.Locale; public class Rife2Plugin implements Plugin { - public static final String DEFAULT_TEMPLATES_DIR = "src/main/templates"; + public static final List DEFAULT_TEMPLATES_DIRS = List.of("src/main/resources/templates", "src/main/templates"); public static final String DEFAULT_GENERATED_RIFE2_CLASSES_DIR = "generated/classes/rife2"; public static final String RIFE2_GROUP = "rife2"; public static final String WEBAPP_SRCDIR = "src/main/webapp"; @@ -114,7 +116,14 @@ public class Rife2Plugin implements Plugin { private static void bundlePrecompiledTemplatesIntoJarFile(TaskContainer tasks, TaskProvider precompileTemplatesTask) { - tasks.named("jar", Jar.class, jar -> jar.from(precompileTemplatesTask)); + tasks.named("jar", Jar.class, jar -> { + jar.from(precompileTemplatesTask); + // This isn't great because it needs to be hardcoded, in order to avoid the templates + // declared in `src/main/resources/templates` to be included in the jar file. + // which means that if for whatever reason the user also uses the same directory for + // something else, it will be excluded from the jar file. + jar.exclude("templates"); + }); } private void createRife2DevelopmentOnlyConfiguration(Project project, @@ -125,7 +134,7 @@ public class Rife2Plugin implements Plugin { conf.setCanBeConsumed(false); conf.setCanBeResolved(false); }); - rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(DEFAULT_TEMPLATES_DIR))); + DEFAULT_TEMPLATES_DIRS.stream().forEachOrdered(dir -> rife2DevelopmentOnly.getDependencies().add(dependencies.create(project.files(dir)))); configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(rife2DevelopmentOnly); } @@ -149,6 +158,11 @@ public class Rife2Plugin implements Plugin { .filter(f -> f.getAsFile().getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) .map(project::zipTree) .toList())); + // This isn't great because it needs to be hardcoded, in order to avoid the templates + // declared in `src/main/resources/templates` to be included in the jar file. + // which means that if for whatever reason the user also uses the same directory for + // something else, it will be excluded from the jar file. + jar.exclude("templates"); plugins.withId("application", unused -> jar.manifest(manifest -> manifest.getAttributes().put("Main-Class", rife2Extension.getUberMainClass().get())) ); @@ -223,7 +237,7 @@ public class Rife2Plugin implements Plugin { task.getVerbose().convention(true); task.getClasspath().from(rife2CompilerClasspath); task.getTypes().convention(rife2Extension.getPrecompiledTemplateTypes()); - task.getTemplatesDirectory().set(project.getLayout().getProjectDirectory().dir(DEFAULT_TEMPLATES_DIR)); + DEFAULT_TEMPLATES_DIRS.stream().forEachOrdered(dir -> task.getTemplatesDirectories().from(project.getLayout().getProjectDirectory().dir(dir))); task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(DEFAULT_GENERATED_RIFE2_CLASSES_DIR)); }); } diff --git a/build-logic/src/test-projects/minimal/src/main/resources/templates/world.html b/build-logic/src/test-projects/minimal/src/main/resources/templates/world.html new file mode 100644 index 0000000..59ff81b --- /dev/null +++ b/build-logic/src/test-projects/minimal/src/main/resources/templates/world.html @@ -0,0 +1,11 @@ + + + + + <!--v title-->Hello<!--/v--> + + + +

Hello World

+ + diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy index e08ff3c..e3aeb36 100644 --- a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy +++ b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/PackagingTest.groovy @@ -25,6 +25,9 @@ class PackagingTest extends AbstractFunctionalTest { } } assert Files.exists(fs.getPath("/rife/template/html/hello.class")) + assert Files.exists(fs.getPath("/rife/template/html/world.class")) + assert !Files.exists(fs.getPath("/templates/hello.html")) + assert !Files.exists(fs.getPath("/templates/world.html")) } where: diff --git a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy index 9d2571e..b935f04 100644 --- a/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy +++ b/build-logic/src/test/groovy/com/uwyn/rife2/gradle/TemplateCompilationTest.groovy @@ -42,6 +42,7 @@ class TemplateCompilationTest extends AbstractFunctionalTest { then: "template sources must be present in the classpath" outputContains("Classpath entry: src/main/templates") + outputContains("Classpath entry: src/main/resources/templates") } def "compiles templates when running #task"() {