From cbc3bcf94072f5ea9ff899aa0cfe5991191a1f74 Mon Sep 17 00:00:00 2001 From: Jan Bessai Date: Wed, 23 Apr 2025 18:12:54 +0200 Subject: [PATCH 1/7] Tests pass with new versions --- README.md | 2 +- build.sbt | 47 ++++++++++--------- project/build.properties | 2 +- project/plugins.sbt | 13 ++--- .../persistable/JavaPersistable.scala | 5 +- .../persistable/ResourcePersistable.scala | 2 +- .../combinators/templating/twirl/Java.scala | 9 ++-- .../combinators/templating/twirl/Python.scala | 1 - .../templating/JavaTemplateTest.scala.java | 8 ++-- .../persistable/JavaPersistableTest.scala | 9 ++-- .../persistable/PersistableTest.scala | 7 +-- .../PythonWithPathPersistableTest.scala | 5 +- .../persistable/ResourcePersistableTest.scala | 9 ++-- .../persistable/TempDirectoryFixture.scala | 7 +-- .../templating/twirl/JavaTest.scala | 10 ++-- .../templating/twirl/PythonTest.scala | 3 +- 16 files changed, 72 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index e598bb9..b14e1ae 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ You can search for released versions [here](http://search.maven.org/#search%7Cga To obtain the latest unreleased development version, clone the repository and run `sbt publishLocal`. -Currently, Scala 2.11, 2.12 and 2.13 are supported. +Currently, Scala 2.13, 3.3 and 3.6 are supported. ## Examples diff --git a/build.sbt b/build.sbt index 6b4edeb..1b00240 100644 --- a/build.sbt +++ b/build.sbt @@ -4,24 +4,28 @@ import sbt.Resolver lazy val commonSettings = Seq( organization := "org.combinators", - scalaVersion := "2.13.1", - crossScalaVersions := Seq("2.11.12", "2.12.10", scalaVersion.value), + scalaVersion := "3.6.4", + crossScalaVersions := Seq("2.13.16", "3.3.5", scalaVersion.value), - resolvers ++= Seq( - Resolver.sonatypeRepo("releases"), - Resolver.typesafeRepo("releases"), - Resolver.sonatypeRepo("snapshots"), - Resolver.typesafeRepo("snapshots") - ), + resolvers ++= Resolver.sonatypeOssRepos("releases"), + resolvers += Resolver.typesafeRepo("releases"), - headerLicense := Some(HeaderLicense.ALv2("2017-2019", "Jan Bessai")), + headerLicense := Some(HeaderLicense.ALv2("2017-2025", "Jan Bessai")), scalacOptions ++= Seq( "-unchecked", "-deprecation", "-feature", "-language:implicitConversions" - ) + ), + scalacOptions ++= { + if (scalaVersion.value >= "2.13.14" && scalaVersion.value < "3.0") Seq( + // "-Xsource:3", + "-Xsource:3-cross" + ) + else Nil + }, + ThisBuild/scapegoatVersion := "3.1.8" ) ++ publishSettings lazy val root = (Project(id = "templating", base = file("."))) @@ -31,16 +35,15 @@ lazy val root = (Project(id = "templating", base = file("."))) .settings( moduleName := "templating", libraryDependencies ++= Seq( - "org.scalactic" %% "scalactic" % "3.0.8" % "test", - "org.scalatest" %% "scalatest" % "3.0.8" % "test", - "com.github.javaparser" % "javaparser-core" % "3.14.14", - "org.apache.commons" % "commons-text" % "1.8", - "commons-io" % "commons-io" % "2.6" % "test", - "org.scala-lang.modules" %% "scala-collection-compat" % "2.1.2" + "org.scalactic" %% "scalactic" % "3.2.19" % "test", + "org.scalatest" %% "scalatest" % "3.2.19" % "test", + "com.github.javaparser" % "javaparser-core" % "3.26.4", + "org.apache.commons" % "commons-text" % "1.13.1", + "commons-io" % "commons-io" % "2.19.0" % "test" ), - sourceDirectories in (Test, TwirlKeys.compileTemplates) += sourceDirectory.value / "test" / "java-templates", - resourceDirectories in Test += sourceDirectory.value / "resources", + Test / TwirlKeys.compileTemplates / sourceDirectories += sourceDirectory.value / "test" / "java-templates", + Test / resourceDirectories += sourceDirectory.value / "resources", TwirlKeys.templateImports := Seq(), TwirlKeys.templateFormats += ("java" -> "org.combinators.templating.twirl.JavaFormat"), TwirlKeys.templateImports += "scala.collection.immutable._", @@ -53,7 +56,7 @@ lazy val root = (Project(id = "templating", base = file("."))) TwirlKeys.templateImports += "com.github.javaparser.ast.stmt._", TwirlKeys.templateImports += "com.github.javaparser.ast.`type`._", - sourceDirectories in (Test, TwirlKeys.compileTemplates) += sourceDirectory.value / "test" / "python-templates", + Test / TwirlKeys.compileTemplates / sourceDirectories += sourceDirectory.value / "test" / "python-templates", TwirlKeys.templateFormats += ("py" -> "org.combinators.templating.twirl.PythonFormat"), TwirlKeys.templateImports += "org.combinators.templating.twirl.Python" ) @@ -64,13 +67,11 @@ lazy val publishSettings = Seq( licenses := Seq("Apache 2" -> url("http://www.apache.org/licenses/LICENSE-2.0.txt")), scmInfo := Some(ScmInfo(url("https://www.github.com/combinators/templating"), "scm:git:git@github.com:combinators/templating.git")), developers := List( - Developer("JanBessai", "Jan Bessai", "jan.bessai@tu-dortmund.de", url("http://janbessai.github.io")), + Developer("JanBessai", "Jan Bessai", "jan.bessai@tu-dortmund.de", url("http://noprotocol.net")), Developer("heineman", "George T. Heineman", "heineman@wpi.edu", url("http://www.cs.wpi.edu/~heineman")), Developer("BorisDuedder", "Boris Düdder", "boris.d@di.ku.dk", url("http://duedder.net")) ), - - pgpPublicRing := file("travis/local.pubring.asc"), - pgpSecretRing := file("travis/local.secring.asc"), + publishTo := sonatypePublishToBundle.value, ) lazy val noPublishSettings = Seq( diff --git a/project/build.properties b/project/build.properties index dca663d..53bb739 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.2.8 +sbt.version = 1.10.11 diff --git a/project/plugins.sbt b/project/plugins.sbt index fad8e57..ee7a024 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,7 +1,8 @@ logLevel := Level.Warn -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.10") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.0") -addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.7") -addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.4.2") -addSbtPlugin("ch.epfl.scala" % "sbt-release-early" % "2.1.1") -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.2.0") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.1") +addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.15") +addSbtPlugin("org.playframework.twirl" % "sbt-twirl" % "2.0.8") +addSbtPlugin("io.shiftleft" % "sbt-ci-release-early" % "2.0.49") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.10.0") +addSbtPlugin("com.sksamuel.scapegoat" %% "sbt-scapegoat" % "1.2.12") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") diff --git a/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala b/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala index 9b677f9..47d2b81 100644 --- a/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala @@ -23,7 +23,6 @@ import com.github.javaparser.ast.expr.{Name, NameExpr} import com.github.javaparser.ast.visitor.GenericVisitorAdapter import scala.collection.immutable._ -import scala.collection.compat._ import scala.jdk.CollectionConverters._ trait JavaPersistableInstances { @@ -51,7 +50,7 @@ trait JavaPersistableInstances { } val clsName = s"${compilationUnit.getTypes.asScala.head.getName}.java" val fullPath = "src" +: "main" +: "java" +: pkg :+ clsName - Paths.get(fullPath.head, fullPath.tail : _*) + Paths.get(fullPath.head, fullPath.tail*) } } } @@ -59,4 +58,4 @@ trait JavaPersistableInstances { object JavaPersistable extends JavaPersistableInstances { type Aux[TT] = Persistable { type T = TT } def apply[T](implicit persistable: Aux[T]): Aux[T] = persistable -} \ No newline at end of file +} diff --git a/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala b/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala index be0a627..109a098 100644 --- a/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala @@ -24,7 +24,7 @@ import java.nio.file.{Files, Path, Paths} * @param persistTo the name of the file where to store the resource. * @param classToLoadResource the class which will be used to load the resource (@see */ -case class BundledResource(name: String, persistTo: Path, classToLoadResource: Class[_] = getClass) +case class BundledResource(name: String, persistTo: Path, classToLoadResource: Class[?] = classOf[BundledResource]) trait ResourcePersistableInstances { def bundledResourceInstance: ResourcePersistable.Aux = new Persistable { diff --git a/src/main/scala/org/combinators/templating/twirl/Java.scala b/src/main/scala/org/combinators/templating/twirl/Java.scala index 955b3ea..cdb7a67 100644 --- a/src/main/scala/org/combinators/templating/twirl/Java.scala +++ b/src/main/scala/org/combinators/templating/twirl/Java.scala @@ -27,7 +27,6 @@ import com.github.javaparser.ast.{CompilationUnit, ImportDeclaration, Node} import org.apache.commons.text.StringEscapeUtils import play.twirl.api.{BufferedContent, Format, Formats} -import scala.collection.compat._ import scala.collection.immutable._ import scala.jdk.CollectionConverters._ @@ -67,10 +66,10 @@ class Java private(elements: Seq[Java], text: String) extends BufferedContent[Ja def nameExpression(): NameExpr = expression() /** Parses this element as a class body declaration (e.g. a method or a field). */ - def classBodyDeclaration(): BodyDeclaration[_] = StaticJavaParser.parseBodyDeclaration(fullText) + def classBodyDeclaration(): BodyDeclaration[?] = StaticJavaParser.parseBodyDeclaration(fullText) /** Parses this element as multiple class body declarations. */ - def classBodyDeclarations(): Seq[BodyDeclaration[_]] = + def classBodyDeclarations(): Seq[BodyDeclaration[?]] = StaticJavaParser.parse(s"class C { $fullText }").getTypes.asScala.head.getMembers.asScala.to(Seq) /** Parses this element as multiple field declarations. */ @@ -86,7 +85,7 @@ class Java private(elements: Seq[Java], text: String) extends BufferedContent[Ja classBodyDeclarations().map(_.asInstanceOf[ConstructorDeclaration]) /** Parses this element as an interface body declaration (e.g. a method signature). */ - def interfaceBodyDeclaration(): BodyDeclaration[_] = StaticJavaParser.parseBodyDeclaration(fullText) + def interfaceBodyDeclaration(): BodyDeclaration[?] = StaticJavaParser.parseBodyDeclaration(fullText) /** Parses this element as a type (e.g. the in X foo = (X)bar). */ def tpe(): Type = StaticJavaParser.parseType(fullText) @@ -106,7 +105,7 @@ object Java { def apply(node: Node): Java = Java(node.toString) /** Creates a Java fragment with initial content from the asts `nodes`. */ - def apply(nodes: Seq[Node]): Java = new Java(Seq(nodes map apply : _*)) + def apply(nodes: Seq[Node]): Java = new Java(Seq((nodes map apply)*)) } object JavaFormat extends Format[Java] { diff --git a/src/main/scala/org/combinators/templating/twirl/Python.scala b/src/main/scala/org/combinators/templating/twirl/Python.scala index 5ecf52b..62b028f 100644 --- a/src/main/scala/org/combinators/templating/twirl/Python.scala +++ b/src/main/scala/org/combinators/templating/twirl/Python.scala @@ -20,7 +20,6 @@ import org.apache.commons.text.StringEscapeUtils import play.twirl.api.{BufferedContent, Format, Formats} import scala.collection.immutable -import scala.collection.compat._ /** * A Python fragment. diff --git a/src/test/java-templates/org/combinators/templating/JavaTemplateTest.scala.java b/src/test/java-templates/org/combinators/templating/JavaTemplateTest.scala.java index b037f76..a9a8f06 100644 --- a/src/test/java-templates/org/combinators/templating/JavaTemplateTest.scala.java +++ b/src/test/java-templates/org/combinators/templating/JavaTemplateTest.scala.java @@ -5,12 +5,12 @@ someString: Expression, qualifiedName: Name, nameExpression: NameExpr, - singleDecl: BodyDeclaration[_], - multiDecls: Seq[BodyDeclaration[_]], + singleDecl: BodyDeclaration[?], + multiDecls: Seq[BodyDeclaration[?]], fieldDeclarations: Seq[FieldDeclaration], methodDeclarations: Seq[MethodDeclaration], constructors: Seq[ConstructorDeclaration], - interfaceMethod: BodyDeclaration[_], + interfaceMethod: BodyDeclaration[?], tpe: Type) @Java(imp) @@ -44,4 +44,4 @@ public static void messUpEnviornment() { public interface FooI { @Java(interfaceMethod) -} \ No newline at end of file +} diff --git a/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala index 59c0620..94bdceb 100644 --- a/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala @@ -7,8 +7,9 @@ import JavaPersistable._ import org.combinators.templating.twirl.Java import org.scalatest._ +import funspec._ -class JavaPersistableTest extends FunSpec { +class JavaPersistableTest extends AnyFunSpec { val className = "Foo" val classText = s"import whatever; class $className {}" val persistableInstance: Persistable.Aux[CompilationUnit] = JavaPersistable.apply @@ -19,7 +20,7 @@ class JavaPersistableTest extends FunSpec { it(s"should use 'src/main/java/$className.java' as path") { val expectedPathComponents = pathPrefix :+ s"$className.java" assert(persistableInstance.path(Java(classText).compilationUnit()) - == Paths.get(expectedPathComponents.head, expectedPathComponents.tail:_*)) + == Paths.get(expectedPathComponents.head, expectedPathComponents.tail*)) } } describe("when it contains multiple classes and interfaces") { @@ -27,7 +28,7 @@ class JavaPersistableTest extends FunSpec { val otherClassText = "interface FooI {}\n class Bar {}" val expectedPathComponents = pathPrefix :+ s"$className.java" assert(persistableInstance.path(Java(Seq(classText, otherClassText).mkString("\n")).compilationUnit()) - == Paths.get(expectedPathComponents.head, expectedPathComponents.tail:_*)) + == Paths.get(expectedPathComponents.head, expectedPathComponents.tail*)) } } describe("when it contains a package declaration") { @@ -36,7 +37,7 @@ class JavaPersistableTest extends FunSpec { val packagesText = s"package ${packages.mkString(".")};" val expectedPathComponents = pathPrefix ++ packages :+ s"$className.java" assert(persistableInstance.path(Java(Seq(packagesText, classText).mkString("\n")).compilationUnit()) - == Paths.get(expectedPathComponents.head, expectedPathComponents.tail:_*)) + == Paths.get(expectedPathComponents.head, expectedPathComponents.tail*)) } } it("should store text that parses back to an equal CompilationUnit") { diff --git a/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala index f8fb4ed..1b73f9b 100644 --- a/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala @@ -3,15 +3,16 @@ package org.combinators.templating.persistable import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths} import org.scalatest._ +import funspec._ -class PersistableTest extends fixture.FunSpec with TempDirectoryFixture with GivenWhenThen { +class PersistableTest extends FixtureAnyFunSpec with TempDirectoryFixture with GivenWhenThen { val persistable: Persistable.Aux[String] = new Persistable { override type T = String override def rawText(elem: T): Array[Byte] = elem.getBytes override def path(elem: T): Path = Paths.get(elem) } describe("Persisting a String to a file named by its content") { - it("should create a file named by its content") { tmpDir: Path => + it("should create a file named by its content") { (tmpDir: Path) => Given("an element to persist") val elementToPersist = "test" @@ -34,7 +35,7 @@ class PersistableTest extends fixture.FunSpec with TempDirectoryFixture with Giv } describe("Persisting an element that specifies a subdirectory") { - it("should create a subdirectory") { tmpDir: Path => + it("should create a subdirectory") { (tmpDir: Path) => Given("an element to persist") val elementToPersistInSubdir = Paths.get("foo", "test").toString diff --git a/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala index 3cc9828..661f4b2 100644 --- a/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala @@ -5,12 +5,13 @@ import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths} import PythonWithPathPersistable._ import org.combinators.templating.twirl.Python import org.scalatest._ +import funspec._ -class PythonWithPathPersistableTest extends fixture.FunSpec with TempDirectoryFixture with GivenWhenThen { +class PythonWithPathPersistableTest extends FixtureAnyFunSpec with TempDirectoryFixture with GivenWhenThen { val persistable: PythonWithPathPersistable.Aux[PythonWithPath] = PythonWithPathPersistable.apply describe("Persisting a piece of Python code to a file") { - it("should create a file named by its content") { tmpDir: Path => + it("should create a file named by its content") { (tmpDir: Path) => Given("an element to persist") val elementToPersist: PythonWithPath = PythonWithPath( diff --git a/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala index 60c7f1a..fe0d6be 100644 --- a/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala @@ -3,16 +3,17 @@ package org.combinators.templating.persistable import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths} import org.scalatest._ +import funspec._ -class ResourcePersistableTest extends fixture.FunSpec with TempDirectoryFixture with GivenWhenThen { +class ResourcePersistableTest extends FixtureAnyFunSpec with TempDirectoryFixture with GivenWhenThen { val persistableInstance: Persistable.Aux[BundledResource] = ResourcePersistable.apply describe("Persisting a picture from the resources folder") { - it("should create a file named by its content") { tmpDir: Path => + it("should create a file named by its content") { (tmpDir: Path) => Given("a resource to persist") val elementToPersist: BundledResource = - BundledResource("res/GammaMtau.png", Paths.get("test", "path", "picture.png"), getClass) + BundledResource("res/GammaMtau.png", Paths.get("test", "path", "picture.png"), getClass()) When("persisting it") persistableInstance.persist(tmpDir.toAbsolutePath, elementToPersist) @@ -32,4 +33,4 @@ class ResourcePersistableTest extends fixture.FunSpec with TempDirectoryFixture persistableInstance.persistOverwriting(tmpDir.toAbsolutePath, elementToPersist) } } -} \ No newline at end of file +} diff --git a/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala b/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala index 55baeb0..0bd9816 100644 --- a/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala +++ b/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala @@ -3,11 +3,12 @@ package org.combinators.templating.persistable import java.nio.file.{Files, Path} import org.apache.commons.io.FileUtils -import org.scalatest.{Outcome, fixture} +import org.scalatest.{Outcome, funspec} +import funspec._ -trait TempDirectoryFixture { self: fixture.FunSpec => +trait TempDirectoryFixture { self: FixtureAnyFunSpec => type FixtureParam = Path - def withFixture(test: OneArgTest): Outcome = { + override def withFixture(test: OneArgTest): Outcome = { val tmpDir = Files.createTempDirectory("inhabitants") try withFixture(test.toNoArgTest(tmpDir)) finally FileUtils.deleteDirectory(tmpDir.toFile) diff --git a/src/test/scala/org/combinators/templating/twirl/JavaTest.scala b/src/test/scala/org/combinators/templating/twirl/JavaTest.scala index 868208d..9b48187 100644 --- a/src/test/scala/org/combinators/templating/twirl/JavaTest.scala +++ b/src/test/scala/org/combinators/templating/twirl/JavaTest.scala @@ -6,10 +6,10 @@ import com.github.javaparser.ast.body.{BodyDeclaration, ConstructorDeclaration, import com.github.javaparser.ast.expr._ import com.github.javaparser.ast.stmt.Statement import org.scalatest._ +import funspec._ import scala.collection.immutable._ -import scala.collection.compat._ -class JavaTest extends FunSpec { +class JavaTest extends AnyFunSpec { val expected: String = """ |import bar.Bar; @@ -111,14 +111,14 @@ class JavaTest extends FunSpec { | super(zs); | System.disco(); |}""".stripMargin).constructors() - val singleDecl: BodyDeclaration[_] = + val singleDecl: BodyDeclaration[?] = Java( s""" |public static void main(String[] args) { | System.out.println("The application has crashed"); | System.out.println("Never run this application again"); |}""".stripMargin).classBodyDeclaration() - val multiDecls: Seq[BodyDeclaration[_]] = + val multiDecls: Seq[BodyDeclaration[?]] = Java( s""" |public static volatile String gargh = "GAAAAAAARRRG!!"; @@ -135,7 +135,7 @@ class JavaTest extends FunSpec { | System.out.println(withThisArg); | hiddenEffect(); // I break my contract }:-> | }""".stripMargin).methodDeclarations() - val interfaceMethod: BodyDeclaration[_] = + val interfaceMethod: BodyDeclaration[?] = Java("public default void doFoos(YYY$_ZZZ x) { System.out.println(x.toString()); }").interfaceBodyDeclaration() describe("Rendering a Java template with lots of stuff and strings in it") { diff --git a/src/test/scala/org/combinators/templating/twirl/PythonTest.scala b/src/test/scala/org/combinators/templating/twirl/PythonTest.scala index 01084e1..f374830 100644 --- a/src/test/scala/org/combinators/templating/twirl/PythonTest.scala +++ b/src/test/scala/org/combinators/templating/twirl/PythonTest.scala @@ -1,8 +1,9 @@ package org.combinators.templating.twirl import org.scalatest._ +import funspec._ -class PythonTest extends FunSpec { +class PythonTest extends AnyFunSpec { val expected: String = """ |class Foo(object): From cfa0dd5e731a9812a1371542763532cf0c0c5795 Mon Sep 17 00:00:00 2001 From: Jan Bessai Date: Wed, 23 Apr 2025 18:13:36 +0200 Subject: [PATCH 2/7] Update headers --- .../templating/persistable/JavaPersistable.scala | 2 +- .../templating/persistable/Persistable.scala | 2 +- .../persistable/PythonWithPathPersistable.scala | 2 +- .../persistable/ResourcePersistable.scala | 2 +- .../org/combinators/templating/twirl/Java.scala | 2 +- .../combinators/templating/twirl/Python.scala | 2 +- .../persistable/JavaPersistableTest.scala | 16 ++++++++++++++++ .../templating/persistable/PersistableTest.scala | 16 ++++++++++++++++ .../PythonWithPathPersistableTest.scala | 16 ++++++++++++++++ .../persistable/ResourcePersistableTest.scala | 16 ++++++++++++++++ .../persistable/TempDirectoryFixture.scala | 16 ++++++++++++++++ .../combinators/templating/twirl/JavaTest.scala | 16 ++++++++++++++++ .../templating/twirl/PythonTest.scala | 16 ++++++++++++++++ 13 files changed, 118 insertions(+), 6 deletions(-) diff --git a/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala b/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala index 47d2b81..3daa1a0 100644 --- a/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 Jan Bessai + * Copyright 2017-2025 Jan Bessai * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/scala/org/combinators/templating/persistable/Persistable.scala b/src/main/scala/org/combinators/templating/persistable/Persistable.scala index 88e80b8..a15731d 100644 --- a/src/main/scala/org/combinators/templating/persistable/Persistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/Persistable.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 Jan Bessai + * Copyright 2017-2025 Jan Bessai * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/scala/org/combinators/templating/persistable/PythonWithPathPersistable.scala b/src/main/scala/org/combinators/templating/persistable/PythonWithPathPersistable.scala index ab09a4c..05fd019 100644 --- a/src/main/scala/org/combinators/templating/persistable/PythonWithPathPersistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/PythonWithPathPersistable.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 Jan Bessai + * Copyright 2017-2025 Jan Bessai * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala b/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala index 109a098..15122ed 100644 --- a/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 Jan Bessai + * Copyright 2017-2025 Jan Bessai * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/scala/org/combinators/templating/twirl/Java.scala b/src/main/scala/org/combinators/templating/twirl/Java.scala index cdb7a67..36f77d1 100644 --- a/src/main/scala/org/combinators/templating/twirl/Java.scala +++ b/src/main/scala/org/combinators/templating/twirl/Java.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 Jan Bessai + * Copyright 2017-2025 Jan Bessai * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/scala/org/combinators/templating/twirl/Python.scala b/src/main/scala/org/combinators/templating/twirl/Python.scala index 62b028f..2470ce2 100644 --- a/src/main/scala/org/combinators/templating/twirl/Python.scala +++ b/src/main/scala/org/combinators/templating/twirl/Python.scala @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 Jan Bessai + * Copyright 2017-2025 Jan Bessai * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala index 94bdceb..c8fcc7a 100644 --- a/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2025 Jan Bessai + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.combinators.templating.persistable import java.nio.file.Paths diff --git a/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala index 1b73f9b..8f9fe92 100644 --- a/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2025 Jan Bessai + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.combinators.templating.persistable import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths} diff --git a/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala index 661f4b2..8c8ae50 100644 --- a/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2025 Jan Bessai + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.combinators.templating.persistable import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths} diff --git a/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala index fe0d6be..7a9dc28 100644 --- a/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2025 Jan Bessai + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.combinators.templating.persistable import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths} diff --git a/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala b/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala index 0bd9816..ee489cf 100644 --- a/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala +++ b/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2025 Jan Bessai + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.combinators.templating.persistable import java.nio.file.{Files, Path} diff --git a/src/test/scala/org/combinators/templating/twirl/JavaTest.scala b/src/test/scala/org/combinators/templating/twirl/JavaTest.scala index 9b48187..1ff44f4 100644 --- a/src/test/scala/org/combinators/templating/twirl/JavaTest.scala +++ b/src/test/scala/org/combinators/templating/twirl/JavaTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2025 Jan Bessai + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.combinators.templating.twirl import com.github.javaparser.ast.ImportDeclaration diff --git a/src/test/scala/org/combinators/templating/twirl/PythonTest.scala b/src/test/scala/org/combinators/templating/twirl/PythonTest.scala index f374830..344e516 100644 --- a/src/test/scala/org/combinators/templating/twirl/PythonTest.scala +++ b/src/test/scala/org/combinators/templating/twirl/PythonTest.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2017-2025 Jan Bessai + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.combinators.templating.twirl import org.scalatest._ From a7e89f9a9ccc84db5f52d57fa4b5af34fe1acfce Mon Sep 17 00:00:00 2001 From: Jan Bessai Date: Wed, 23 Apr 2025 18:16:20 +0200 Subject: [PATCH 3/7] Remove old travis CI --- .travis.yml | 30 ------------------------------ travis/secrets.tar.enc | Bin 20496 -> 0 bytes 2 files changed, 30 deletions(-) delete mode 100644 .travis.yml delete mode 100644 travis/secrets.tar.enc diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6891398..0000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -sudo: false -language: scala -stages: - - test - - name: deploy - if: branch = master AND NOT type IN (pull_request) - -script: sbt +test - -jobs: - include: - - dist: trusty - stage: test - jdk: oraclejdk8 - - stage: test - jdk: openjdk11 - script: sbt clean test && - sbt coverage ++2.12.10 test && - sbt ++2.12.10 coverageReport coveralls - - stage: deploy - jdk: openjdk11 - script: | - openssl aes-256-cbc -K $encrypted_edd93504cc75_key -iv $encrypted_edd93504cc75_iv -in travis/secrets.tar.enc -out travis/local.secrets.tar -d && - tar xv -C travis -f travis/local.secrets.tar && - sbt +releaseEarly -env: - global: - - secure: oaQO0OktBq1UUw490+zw/bioSFmfkHz9X7sLpoOCT9NzAPwHOXDNIaoYubu+HXbgvSfsId+x4QR2wT1cH+njBMwvImQWrkfWODTAxfSVEuqThWlVKTmfYCoNcbMpFOIQYpSWqx7pb8VeNMr1p2Ql6ov3lNvUpRNvIwxbfUBCYy3mL/H4/1XFDy7emSmgr/I56Pmg6woMi7T7zoEFMozKPQG2rkB3/yU53CtRWMTDU4p3R33bFP5+Q314jkLN/x8E6p83DXSlHw5CNCybrndvBN2Ugylpn5XbINyW78yEc1uDpMDjgQ6/nFldFLBsZ9jVJNOLFHwxEcPCHKS22SVZ50mpek5zvUDHGKGodl2Ct1J2dO/kV3Mk15y/ndiapUJvZ/JLUwbgNmF1zzTsrvWdGiHnPOz4MWfkUAe5L8pAVNs0wul0AihbkcthD2hQu3EnDxjx1Mf0yjov58orv7q0Euf7Wnmzmu9orx+KBbCX7UFs6SsFZiM8jbyEy50K+Evmub1Qd+UYc/xspboM6vidKd61+O2YTnQ26MsFV0cn6GvkphqsbjfSep/cvm9gOtYiwo959v7MX8ApMXU28hK01rf0BlnBBVuXd8cNuTW7B6SAQM1HRxjkfze6VukGYQFe8RLuuhqfbAXCpui1lcKaQf2G9jTrse0+ROszqqLEaTU= - - secure: BNveA1KQHK2DWg4Q4Oogvo3BDZP17zHjMiEf1JzIt06dB3QZ4ZYONTOPgXvL3XrFGJZe40pnAmrQezlrpRK2X8JPNbYpYh7aHB6AGQmJzHU1BNVI/0GuN+y0jUJaM8oH83z2S4sm8BZA015YIWmV8P0OELcTNxglgOwj9yo0SB5Xrd7qeAfmho9Lg1pN42LhuZK8LzxsTtrInR9k2dYKIsIwEkrNSqycqOVs1mLGX++wvj3qGf4a2GibswhQ3k1rG5jfeT9HjHDzFdWlOvn4jH8TasA3yBoNCgUEA8lIR9fSKGkbS72o4X5OsOLP6qykobcLAIK7SAFOXRY1xhJESFgLBIQQa09XmI0T3YLglXIdz78m/4stQ4firF249Ph3Uj2656uiiwGgCNku6kRDNj9HN5y/RtsZaoNC304nrXYpIhMPNFuekfcmB7fWhczsU0aXzfRBvjrXWq+jpFoKh2En6WFDEeMaDTmbGdN4Qyb1zL1Yw2lXdg2Blxm24H+E5J4FHnCSJU81xmAzR4LgBEqQdGJW0n31mMgXG9NNJEPjo1hYn/OvXDIQOhg3IO0OENX3PS8UxtlhX1/fMyLVN/JmHdXUR1r0b/v/NtKrYlZgveJ5EB162NYGy8QKT2K2iDjk1nkBJ8GFNocaXWmAjsmAb5A2lBYMZYULMrLTTjM= - - secure: DKrbKFUrrnFC7JTgDWf0w8KIqT/3dHJphVKv4UkvvLJ7vuCWYQ+82QRzey/LFGCGq4nS545qWyZBZDXHQbBukJZuC+rqilcXRoE05QP42PuWhDJ00f68U5ciE5D5o9S7qGwo2zKFxt7g8pGuebQBb0BYrV9ilAbCF7wVGdWfkjq4h65nPcMJNaXoq7sDcIDXdnKbYBE5KFjglv5coRkyLm+LSyPbPClt+fnCJq6lYFXTqoecWOWMiCQdnCmanWB4Av/AYfuGOt6nIR8WTpHOT/KMkvKv/lNJ2p+mCRe22Th9eKQZvep/XPtwaueYO/c0zpc/dz7XEapXhzdoEGDhLRZfiuy1fXyHuMb4vhLYJBZ3d1t3hN5IkK/1RFWF6PCdoRYnxrMYbMT2uJTA3sVVA8YBQw99OkV9Y/N/SSAkSkJnCuaCeiLPb/OMZlxLY6hs93HbLDBDp935wRL8UZnsLA/bq6/1i7USF4HwI2aZQbhzbbqSXnVbtPvdTQEx4dORWenJraIBl+6LGKA93vimOxi8GkqAbAlgIeUvPH24t4yvHbX+AHCLpHTL7sS6Fl1gc3VLJrzPs21tliNOA3DYjXvNkYZRsdppGXR3nlCuUFlN9hd6pf0WJtYQUrjVPPJ7Nthx2J1y/o5HWAxHpNB9829djuG+nFvXPWJHfYZeNKI= diff --git a/travis/secrets.tar.enc b/travis/secrets.tar.enc deleted file mode 100644 index a3bac9efc1fdde677a3657b9c2c0862d4c4ca608..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20496 zcmV(rK<>YnTfcj69P+^-IvDr+U1ObVwS{xOb%2|{Ystz6hz8AFw-VdArR&fnW$_qm z2U?|p_4Oq4YC~6ZBrgu3Ul~{D^HrpP%m>62CqNuZ@_0fC8t^;IN3%>aHo|ZR8*Oy@ z1!YM6^+0m=T;x*pDi$rp*nNlEe>v_A0G=@#uPFJcYTkYApb{GgB{8ldN{fA-ylJ`E z`5%wie3`gJ))Ny^B9+u4VtDR9wS zFg`J>#}kmox!0d;L?bJe_1jF@kf_D

U&sD-~l5p(a9z&T=^^b!R_4*}WxA;pg+I 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 Date: Wed, 23 Apr 2025 18:35:55 +0200 Subject: [PATCH 4/7] Add github CI Script --- .github/workflows/test_and_release.yml | 81 ++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 .github/workflows/test_and_release.yml diff --git a/.github/workflows/test_and_release.yml b/.github/workflows/test_and_release.yml new file mode 100644 index 0000000..6725587 --- /dev/null +++ b/.github/workflows/test_and_release.yml @@ -0,0 +1,81 @@ +name: Test code, update coverage, and release master branch + +on: [ push, pull_request ] + +jobs: + test: + strategy: + matrix: + os: [ windows-latest, ubuntu-latest ] + java: [ '11', '17', '21' ] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4.2.2 + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: ${{ matrix.java }} + - name: Setup sbt launcher + uses: sbt/setup-sbt@v1.1.7 + - name: Run tests + shell: bash + run: sbt +test + coverage: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v4.2.2 + - name: Setup JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + - name: Setup sbt launcher + uses: sbt/setup-sbt@v1.1.7 + - name: Analyze coverage + run: sbt clean coverage +test + - name: Update coverage report + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_FLAG_NAME: Scala ${{ matrix.scala }} + run: sbt coverageReport coveralls + release: + runs-on: ubuntu-latest + needs: [ test, coverage ] + steps: + - uses: actions/checkout@v4.2.2 + - name: Check if we are head of master + id: check_head_of_master + run: | + git fetch origin master && + MASTER=`git rev-parse origin/master` && + echo "::set-output name=head_of_master::$MASTER" && + CURRENT=`git rev-list -n 1 ${{ github.ref }} || echo "NOT_MASTER"` && + echo "::set-output name=current_job_ref::$CURRENT" + - name: Set up JDK + if: steps.check_head_of_master.outputs.head_of_master == steps.check_head_of_master.outputs.current_job_ref + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + - name: Set up SBT + if: steps.check_head_of_master.outputs.head_of_master == steps.check_head_of_master.outputs.current_job_ref + uses: sbt/setup-sbt@v1.1.7 + - name: Build and release + if: steps.check_head_of_master.outputs.head_of_master == steps.check_head_of_master.outputs.current_job_ref + env: + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONA_USER: ${{ secrets.SONATYPE_USERNAME }} + SONA_PASS: ${{ secrets.SONATYPE_PASSWORD }} + GPG_PRIVATE: ${{ secrets.GPG_PRIVATE }} + PGP_PASS: ${{ secrets.PGP_PASS }} + CI: github + run: | + git fetch --prune --unshallow --tags && + export GPG_TTY=$(tty) && + echo $GPG_PRIVATE | base64 -d | gpg --passphrase=$PGP_PASS --yes --batch --pinentry-mode loopback --import && + export PATH=`pwd`/.github/bin:$PATH && + sbt + test ciReleaseTagNextVersion ciReleaseSonatype + From 7552226101899e1655f37cc0741f2014cf1ead09 Mon Sep 17 00:00:00 2001 From: Jan Bessai Date: Wed, 23 Apr 2025 18:40:56 +0200 Subject: [PATCH 5/7] Scalafmt the source --- .scalafmt.conf | 10 +++ .../persistable/JavaPersistable.scala | 24 +++--- .../templating/persistable/Persistable.scala | 25 ++++--- .../PythonWithPathPersistable.scala | 4 +- .../persistable/ResourcePersistable.scala | 19 +++-- .../combinators/templating/twirl/Java.scala | 53 +++++++++---- .../combinators/templating/twirl/Python.scala | 28 +++---- .../persistable/JavaPersistableTest.scala | 44 ++++++++--- .../persistable/PersistableTest.scala | 38 +++++++--- .../PythonWithPathPersistableTest.scala | 28 +++++-- .../persistable/ResourcePersistableTest.scala | 34 +++++++-- .../persistable/TempDirectoryFixture.scala | 2 +- .../templating/twirl/JavaTest.scala | 74 ++++++++++--------- .../templating/twirl/PythonTest.scala | 9 +-- 14 files changed, 266 insertions(+), 126 deletions(-) create mode 100644 .scalafmt.conf diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..ca9f6ca --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,10 @@ +version = 3.9.4 +rewrite.rules = [ AvoidInfix, SortImports ] +runner.dialect = scala36 +project { + git = true + excludeFilters = [ + plugin/src/sbt-test + ] + layout = StandardConvention +} diff --git a/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala b/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala index 3daa1a0..cb6b008 100644 --- a/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/JavaPersistable.scala @@ -26,25 +26,29 @@ import scala.collection.immutable._ import scala.jdk.CollectionConverters._ trait JavaPersistableInstances { - /** Persistable instance for a compilation unit. - * Derives path and file names from the package and the name of the first declared type. + + /** Persistable instance for a compilation unit. Derives path and file names + * from the package and the name of the first declared type. */ implicit def compilationUnitInstance: JavaPersistable.Aux[CompilationUnit] = new Persistable { type T = CompilationUnit - override def rawText(compilationUnit: CompilationUnit) = compilationUnit.toString.getBytes + override def rawText(compilationUnit: CompilationUnit) = + compilationUnit.toString.getBytes override def path(compilationUnit: CompilationUnit) = { val pkg: Seq[String] = compilationUnit.getPackageDeclaration.orElse(null) match { case null => Seq.empty case somePkg => - somePkg.accept(new GenericVisitorAdapter[Seq[String], Unit] { - override def visit(name: NameExpr, arg: Unit): Seq[String] = Seq(name.getNameAsString) - override def visit(name: Name, arg: Unit): Seq[String] = - Option(name.getQualifier.orElse(null)) - .map((q: Name) => q.accept(this, arg)) - .getOrElse(Seq.empty[String]) :+ name.getIdentifier - }, + somePkg.accept( + new GenericVisitorAdapter[Seq[String], Unit] { + override def visit(name: NameExpr, arg: Unit): Seq[String] = + Seq(name.getNameAsString) + override def visit(name: Name, arg: Unit): Seq[String] = + Option(name.getQualifier.orElse(null)) + .map((q: Name) => q.accept(this, arg)) + .getOrElse(Seq.empty[String]) :+ name.getIdentifier + }, () ) } diff --git a/src/main/scala/org/combinators/templating/persistable/Persistable.scala b/src/main/scala/org/combinators/templating/persistable/Persistable.scala index a15731d..cc99e8c 100644 --- a/src/main/scala/org/combinators/templating/persistable/Persistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/Persistable.scala @@ -21,24 +21,27 @@ import java.io.File /** Type class for persistable objects (inhabitants). */ trait Persistable { + /** The type of the object to persist */ type T + /** Serialized representation of the object */ def rawText(elem: T): Array[Byte] - /** Path where to store the object `elem` (relative to some later specified root) */ + + /** Path where to store the object `elem` (relative to some later specified + * root) + */ def path(elem: T): Path - /** - * Computes the full path where to place `elem` relative to `basePath`. + /** Computes the full path where to place `elem` relative to `basePath`. */ def fullPath(basePath: Path, elem: T): Path = { basePath.resolve(path(elem)) } - - /** - * Persists this object to an object dependent path under `basePath` and returns the persisted file. - * Overwrites any pre-existing files under `basePath` / `path`. + /** Persists this object to an object dependent path under `basePath` and + * returns the persisted file. Overwrites any pre-existing files under + * `basePath` / `path`. */ def persistOverwriting(basePath: Path, elem: T): File = { val fp = fullPath(basePath, elem) @@ -48,9 +51,9 @@ trait Persistable { fp.toFile } - /** - * Persists this object to an object dependent path under `basePath` and returns the persisted file. - * Throws an `FileAlreadyExistsException` if the file already exists. + /** Persists this object to an object dependent path under `basePath` and + * returns the persisted file. Throws an `FileAlreadyExistsException` if the + * file already exists. */ def persist(basePath: Path, elem: T): File = { val fp = fullPath(basePath, elem) @@ -64,4 +67,4 @@ object Persistable { type Aux[TT] = Persistable { type T = TT } def apply[T](implicit persistable: Aux[T]): Aux[T] = persistable -} \ No newline at end of file +} diff --git a/src/main/scala/org/combinators/templating/persistable/PythonWithPathPersistable.scala b/src/main/scala/org/combinators/templating/persistable/PythonWithPathPersistable.scala index 05fd019..058989e 100644 --- a/src/main/scala/org/combinators/templating/persistable/PythonWithPathPersistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/PythonWithPathPersistable.scala @@ -24,8 +24,10 @@ import org.combinators.templating.twirl.Python case class PythonWithPath(code: Python, persistTo: Path) trait PythonWithPathPersistableInstances { + /** Persistable instance for [PythonWithPath]. */ - implicit def pythonWithPathPersistable: PythonWithPathPersistable.Aux[PythonWithPath] = new Persistable { + implicit def pythonWithPathPersistable + : PythonWithPathPersistable.Aux[PythonWithPath] = new Persistable { type T = PythonWithPath def rawText(elem: PythonWithPath): Array[Byte] = elem.code.getCode.getBytes def path(elem: PythonWithPath): Path = elem.persistTo diff --git a/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala b/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala index 15122ed..7ad640b 100644 --- a/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala +++ b/src/main/scala/org/combinators/templating/persistable/ResourcePersistable.scala @@ -20,18 +20,27 @@ import java.nio.file.{Files, Path, Paths} /** A persistable resource on the classpath. * - * @param name the name by which to locate the resource within the classpath. - * @param persistTo the name of the file where to store the resource. - * @param classToLoadResource the class which will be used to load the resource (@see + * @param name + * the name by which to locate the resource within the classpath. + * @param persistTo + * the name of the file where to store the resource. + * @param classToLoadResource + * the class which will be used to load the resource (@see */ -case class BundledResource(name: String, persistTo: Path, classToLoadResource: Class[?] = classOf[BundledResource]) +case class BundledResource( + name: String, + persistTo: Path, + classToLoadResource: Class[?] = classOf[BundledResource] +) trait ResourcePersistableInstances { def bundledResourceInstance: ResourcePersistable.Aux = new Persistable { override type T = BundledResource override def path(elem: BundledResource): Path = elem.persistTo override def rawText(elem: BundledResource): Array[Byte] = - Files.readAllBytes(Paths.get(elem.classToLoadResource.getResource(elem.name).toURI)) + Files.readAllBytes( + Paths.get(elem.classToLoadResource.getResource(elem.name).toURI) + ) } } diff --git a/src/main/scala/org/combinators/templating/twirl/Java.scala b/src/main/scala/org/combinators/templating/twirl/Java.scala index 36f77d1..7dfba27 100644 --- a/src/main/scala/org/combinators/templating/twirl/Java.scala +++ b/src/main/scala/org/combinators/templating/twirl/Java.scala @@ -20,7 +20,12 @@ import java.io.StringReader import com.github.javaparser._ import com.github.javaparser.ast.`type`.Type -import com.github.javaparser.ast.body.{BodyDeclaration, ConstructorDeclaration, FieldDeclaration, MethodDeclaration} +import com.github.javaparser.ast.body.{ + BodyDeclaration, + ConstructorDeclaration, + FieldDeclaration, + MethodDeclaration +} import com.github.javaparser.ast.expr.{Expression, Name, NameExpr, SimpleName} import com.github.javaparser.ast.stmt.Statement import com.github.javaparser.ast.{CompilationUnit, ImportDeclaration, Node} @@ -31,7 +36,8 @@ import scala.collection.immutable._ import scala.jdk.CollectionConverters._ /** A Java fragment. */ -class Java private(elements: Seq[Java], text: String) extends BufferedContent[Java](elements, text) { +class Java private (elements: Seq[Java], text: String) + extends BufferedContent[Java](elements, text) { def this(text: String) = this(Nil, Formats.safe(text)) def this(elements: Seq[Java]) = this(elements, "") @@ -44,7 +50,8 @@ class Java private(elements: Seq[Java], text: String) extends BufferedContent[Ja def compilationUnit(): CompilationUnit = StaticJavaParser.parse(fullText) /** Parses an import declaration. */ - def importDeclaration(): ImportDeclaration = StaticJavaParser.parseImport(fullText) + def importDeclaration(): ImportDeclaration = + StaticJavaParser.parseImport(fullText) /** Parses this element as a single statement. */ def statement(): Statement = StaticJavaParser.parseStatement(fullText) @@ -54,7 +61,8 @@ class Java private(elements: Seq[Java], text: String) extends BufferedContent[Ja StaticJavaParser.parseBlock(s"{ $fullText }").getStatements.asScala.to(Seq) /** Parses this element as an expression. */ - def expression[T <: Expression](): T = StaticJavaParser.parseExpression[T](fullText) + def expression[T <: Expression](): T = + StaticJavaParser.parseExpression[T](fullText) /** Parses this element as a (potentially qualified) name. */ def name(): Name = StaticJavaParser.parseName(fullText) @@ -65,12 +73,22 @@ class Java private(elements: Seq[Java], text: String) extends BufferedContent[Ja /** Parses this element as a (unqualified) name expression. */ def nameExpression(): NameExpr = expression() - /** Parses this element as a class body declaration (e.g. a method or a field). */ - def classBodyDeclaration(): BodyDeclaration[?] = StaticJavaParser.parseBodyDeclaration(fullText) + /** Parses this element as a class body declaration (e.g. a method or a + * field). + */ + def classBodyDeclaration(): BodyDeclaration[?] = + StaticJavaParser.parseBodyDeclaration(fullText) /** Parses this element as multiple class body declarations. */ def classBodyDeclarations(): Seq[BodyDeclaration[?]] = - StaticJavaParser.parse(s"class C { $fullText }").getTypes.asScala.head.getMembers.asScala.to(Seq) + StaticJavaParser + .parse(s"class C { $fullText }") + .getTypes + .asScala + .head + .getMembers + .asScala + .to(Seq) /** Parses this element as multiple field declarations. */ def fieldDeclarations(): Seq[FieldDeclaration] = @@ -84,8 +102,11 @@ class Java private(elements: Seq[Java], text: String) extends BufferedContent[Ja def constructors(): Seq[ConstructorDeclaration] = classBodyDeclarations().map(_.asInstanceOf[ConstructorDeclaration]) - /** Parses this element as an interface body declaration (e.g. a method signature). */ - def interfaceBodyDeclaration(): BodyDeclaration[?] = StaticJavaParser.parseBodyDeclaration(fullText) + /** Parses this element as an interface body declaration (e.g. a method + * signature). + */ + def interfaceBodyDeclaration(): BodyDeclaration[?] = + StaticJavaParser.parseBodyDeclaration(fullText) /** Parses this element as a type (e.g. the in X foo = (X)bar). */ def tpe(): Type = StaticJavaParser.parseType(fullText) @@ -93,10 +114,13 @@ class Java private(elements: Seq[Java], text: String) extends BufferedContent[Ja /** Helper for Java utility methods. */ object Java { + /** Creates a Java fragment with initial content specified. */ def apply(text: String): Java = new Java(text) - /** Creates a Java fragment with initial content from the given `text` separated by `separator`. */ + /** Creates a Java fragment with initial content from the given `text` + * separated by `separator`. + */ def apply(text: Seq[String], separator: String = ";"): Java = { apply(text.mkString(separator)) } @@ -105,19 +129,22 @@ object Java { def apply(node: Node): Java = Java(node.toString) /** Creates a Java fragment with initial content from the asts `nodes`. */ - def apply(nodes: Seq[Node]): Java = new Java(Seq((nodes map apply)*)) + def apply(nodes: Seq[Node]): Java = new Java(Seq((nodes.map(apply))*)) } object JavaFormat extends Format[Java] { + /** Integrates `text` without performing any escaping process. * - * @param text Text to integrate + * @param text + * Text to integrate */ def raw(text: String): Java = Java(text) /** Escapes `text` using Java String rules. * - * @param text Text to integrate + * @param text + * Text to integrate */ def escape(text: String): Java = Java(StringEscapeUtils.escapeJava(text)) diff --git a/src/main/scala/org/combinators/templating/twirl/Python.scala b/src/main/scala/org/combinators/templating/twirl/Python.scala index 2470ce2..5296c01 100644 --- a/src/main/scala/org/combinators/templating/twirl/Python.scala +++ b/src/main/scala/org/combinators/templating/twirl/Python.scala @@ -21,10 +21,10 @@ import play.twirl.api.{BufferedContent, Format, Formats} import scala.collection.immutable -/** - * A Python fragment. +/** A Python fragment. */ -class Python private(elements: immutable.Seq[Python], text: String) extends BufferedContent[Python](elements, text) { +class Python private (elements: immutable.Seq[Python], text: String) + extends BufferedContent[Python](elements, text) { def this(text: String) = this(Nil, Formats.safe(text)) def this(elements: immutable.Seq[Python]) = this(elements, "") @@ -48,33 +48,36 @@ class Python private(elements: immutable.Seq[Python], text: String) extends Buff def getCode: String = fullText } -/** - * Helper for Python utility methods. +/** Helper for Python utility methods. */ object Python { + /** Creates a Python fragment with initial content specified. */ def apply(text: String): Python = { new Python(text) } - /** Creates a Python fragment with initial content from the given `text` separated by `separator`. */ + /** Creates a Python fragment with initial content from the given `text` + * separated by `separator`. + */ def apply(text: Seq[String], separator: String = ";"): Python = { apply(text.mkString(separator)) } } object PythonFormat extends Format[Python] { - /** - * Integrates `text` without performing any escaping process. + + /** Integrates `text` without performing any escaping process. * - * @param text Text to integrate + * @param text + * Text to integrate */ def raw(text: String): Python = Python(text) - /** - * Escapes `text` using Python String rules. + /** Escapes `text` using Python String rules. * - * @param text Text to integrate + * @param text + * Text to integrate */ def escape(text: String): Python = Python(StringEscapeUtils.escapeJava(text)) @@ -85,4 +88,3 @@ object PythonFormat extends Format[Python] { def fill(elements: immutable.Seq[Python]): Python = new Python(elements) } - diff --git a/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala index c8fcc7a..7d289c3 100644 --- a/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/JavaPersistableTest.scala @@ -28,38 +28,62 @@ import funspec._ class JavaPersistableTest extends AnyFunSpec { val className = "Foo" val classText = s"import whatever; class $className {}" - val persistableInstance: Persistable.Aux[CompilationUnit] = JavaPersistable.apply + val persistableInstance: Persistable.Aux[CompilationUnit] = + JavaPersistable.apply val pathPrefix = Seq("src", "main", "java") describe("Persisting a Java compilation unit") { describe(s"when it contains only one class $className and no package") { it(s"should use 'src/main/java/$className.java' as path") { val expectedPathComponents = pathPrefix :+ s"$className.java" - assert(persistableInstance.path(Java(classText).compilationUnit()) - == Paths.get(expectedPathComponents.head, expectedPathComponents.tail*)) + assert( + persistableInstance.path(Java(classText).compilationUnit()) + == Paths.get( + expectedPathComponents.head, + expectedPathComponents.tail* + ) + ) } } describe("when it contains multiple classes and interfaces") { it("should use the name of the first class to determine the path") { val otherClassText = "interface FooI {}\n class Bar {}" val expectedPathComponents = pathPrefix :+ s"$className.java" - assert(persistableInstance.path(Java(Seq(classText, otherClassText).mkString("\n")).compilationUnit()) - == Paths.get(expectedPathComponents.head, expectedPathComponents.tail*)) + assert( + persistableInstance.path( + Java(Seq(classText, otherClassText).mkString("\n")) + .compilationUnit() + ) + == Paths.get( + expectedPathComponents.head, + expectedPathComponents.tail* + ) + ) } } describe("when it contains a package declaration") { it("should prefix the path with a directory structure for the package") { val packages = Seq("foo", "bar") val packagesText = s"package ${packages.mkString(".")};" - val expectedPathComponents = pathPrefix ++ packages :+ s"$className.java" - assert(persistableInstance.path(Java(Seq(packagesText, classText).mkString("\n")).compilationUnit()) - == Paths.get(expectedPathComponents.head, expectedPathComponents.tail*)) + val expectedPathComponents = + pathPrefix ++ packages :+ s"$className.java" + assert( + persistableInstance.path( + Java(Seq(packagesText, classText).mkString("\n")).compilationUnit() + ) + == Paths.get( + expectedPathComponents.head, + expectedPathComponents.tail* + ) + ) } } it("should store text that parses back to an equal CompilationUnit") { val originalUnit = Java(classText).compilationUnit() - assert(Java(new String(persistableInstance.rawText(originalUnit))).compilationUnit() == originalUnit) + assert( + Java(new String(persistableInstance.rawText(originalUnit))) + .compilationUnit() == originalUnit + ) } } } - diff --git a/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala index 8f9fe92..19ec9f8 100644 --- a/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/PersistableTest.scala @@ -21,7 +21,10 @@ import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths} import org.scalatest._ import funspec._ -class PersistableTest extends FixtureAnyFunSpec with TempDirectoryFixture with GivenWhenThen { +class PersistableTest + extends FixtureAnyFunSpec + with TempDirectoryFixture + with GivenWhenThen { val persistable: Persistable.Aux[String] = new Persistable { override type T = String override def rawText(elem: T): Array[Byte] = elem.getBytes @@ -40,12 +43,18 @@ class PersistableTest extends FixtureAnyFunSpec with TempDirectoryFixture with G assert(Files.exists(expectedPath)) And("the file should have exactly the specified contents") - assert(Files.readAllBytes(expectedPath).sameElements(elementToPersist.getBytes)) + assert( + Files.readAllBytes(expectedPath).sameElements(elementToPersist.getBytes) + ) And("trying to persist the same element again should raise an exception") - assertThrows[FileAlreadyExistsException](persistable.persist(tmpDir.toAbsolutePath, elementToPersist)) + assertThrows[FileAlreadyExistsException]( + persistable.persist(tmpDir.toAbsolutePath, elementToPersist) + ) - Then("it should not produce the exception when overwrite semantics is specified") + Then( + "it should not produce the exception when overwrite semantics is specified" + ) persistable.persistOverwriting(tmpDir.toAbsolutePath, elementToPersist) } } @@ -68,13 +77,24 @@ class PersistableTest extends FixtureAnyFunSpec with TempDirectoryFixture with G assert(Files.exists(expectedFileInSubdir)) And("the file should have exactly the specified contents") - assert(Files.readAllBytes(expectedFileInSubdir).sameElements(elementToPersistInSubdir.getBytes)) + assert( + Files + .readAllBytes(expectedFileInSubdir) + .sameElements(elementToPersistInSubdir.getBytes) + ) And("trying to persist the same element again should raise an exception") - assertThrows[FileAlreadyExistsException](persistable.persist(tmpDir.toAbsolutePath, elementToPersistInSubdir)) - - Then("it should not produce the exception when overwrite semantics is specified") - persistable.persistOverwriting(tmpDir.toAbsolutePath, elementToPersistInSubdir) + assertThrows[FileAlreadyExistsException]( + persistable.persist(tmpDir.toAbsolutePath, elementToPersistInSubdir) + ) + + Then( + "it should not produce the exception when overwrite semantics is specified" + ) + persistable.persistOverwriting( + tmpDir.toAbsolutePath, + elementToPersistInSubdir + ) } } } diff --git a/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala index 8c8ae50..7d31579 100644 --- a/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/PythonWithPathPersistableTest.scala @@ -23,8 +23,12 @@ import org.combinators.templating.twirl.Python import org.scalatest._ import funspec._ -class PythonWithPathPersistableTest extends FixtureAnyFunSpec with TempDirectoryFixture with GivenWhenThen { - val persistable: PythonWithPathPersistable.Aux[PythonWithPath] = PythonWithPathPersistable.apply +class PythonWithPathPersistableTest + extends FixtureAnyFunSpec + with TempDirectoryFixture + with GivenWhenThen { + val persistable: PythonWithPathPersistable.Aux[PythonWithPath] = + PythonWithPathPersistable.apply describe("Persisting a piece of Python code to a file") { it("should create a file named by its content") { (tmpDir: Path) => @@ -34,22 +38,32 @@ class PythonWithPathPersistableTest extends FixtureAnyFunSpec with TempDirectory Python(s"""class Foo: | pass """.stripMargin), - Paths.get("bar/test.py")) + Paths.get("bar/test.py") + ) When("persisting it") persistable.persist(tmpDir.toAbsolutePath, elementToPersist) Then("its corresponding file should exist") - val expectedPath = tmpDir.toAbsolutePath.resolve(elementToPersist.persistTo) + val expectedPath = + tmpDir.toAbsolutePath.resolve(elementToPersist.persistTo) assert(Files.exists(expectedPath)) And("the file should have exactly the specified contents") - assert(Files.readAllBytes(expectedPath).sameElements(elementToPersist.code.getCode.getBytes)) + assert( + Files + .readAllBytes(expectedPath) + .sameElements(elementToPersist.code.getCode.getBytes) + ) And("trying to persist the same element again should raise an exception") - assertThrows[FileAlreadyExistsException](persistable.persist(tmpDir.toAbsolutePath, elementToPersist)) + assertThrows[FileAlreadyExistsException]( + persistable.persist(tmpDir.toAbsolutePath, elementToPersist) + ) - Then("it should not produce the exception when overwrite semantics is specified") + Then( + "it should not produce the exception when overwrite semantics is specified" + ) persistable.persistOverwriting(tmpDir.toAbsolutePath, elementToPersist) } } diff --git a/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala b/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala index 7a9dc28..8154662 100644 --- a/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala +++ b/src/test/scala/org/combinators/templating/persistable/ResourcePersistableTest.scala @@ -21,32 +21,50 @@ import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths} import org.scalatest._ import funspec._ -class ResourcePersistableTest extends FixtureAnyFunSpec with TempDirectoryFixture with GivenWhenThen { +class ResourcePersistableTest + extends FixtureAnyFunSpec + with TempDirectoryFixture + with GivenWhenThen { - val persistableInstance: Persistable.Aux[BundledResource] = ResourcePersistable.apply + val persistableInstance: Persistable.Aux[BundledResource] = + ResourcePersistable.apply describe("Persisting a picture from the resources folder") { it("should create a file named by its content") { (tmpDir: Path) => Given("a resource to persist") val elementToPersist: BundledResource = - BundledResource("res/GammaMtau.png", Paths.get("test", "path", "picture.png"), getClass()) + BundledResource( + "res/GammaMtau.png", + Paths.get("test", "path", "picture.png"), + getClass() + ) When("persisting it") persistableInstance.persist(tmpDir.toAbsolutePath, elementToPersist) Then("its corresponding file should exist") - val expectedPath: Path = tmpDir.toAbsolutePath.resolve(elementToPersist.persistTo) + val expectedPath: Path = + tmpDir.toAbsolutePath.resolve(elementToPersist.persistTo) assert(Files.exists(expectedPath)) And("the file should have exactly the specified contents") - val resourceBytes = Files.readAllBytes(Paths.get(getClass.getResource("res/GammaMtau.png").toURI)) + val resourceBytes = Files.readAllBytes( + Paths.get(getClass.getResource("res/GammaMtau.png").toURI) + ) assert(Files.readAllBytes(expectedPath).sameElements(resourceBytes)) And("trying to persist the same element again should raise an exception") - assertThrows[FileAlreadyExistsException](persistableInstance.persist(tmpDir.toAbsolutePath, elementToPersist)) + assertThrows[FileAlreadyExistsException]( + persistableInstance.persist(tmpDir.toAbsolutePath, elementToPersist) + ) - Then("it should not produce the exception when overwrite semantics is specified") - persistableInstance.persistOverwriting(tmpDir.toAbsolutePath, elementToPersist) + Then( + "it should not produce the exception when overwrite semantics is specified" + ) + persistableInstance.persistOverwriting( + tmpDir.toAbsolutePath, + elementToPersist + ) } } } diff --git a/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala b/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala index ee489cf..38984a8 100644 --- a/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala +++ b/src/test/scala/org/combinators/templating/persistable/TempDirectoryFixture.scala @@ -19,7 +19,7 @@ package org.combinators.templating.persistable import java.nio.file.{Files, Path} import org.apache.commons.io.FileUtils -import org.scalatest.{Outcome, funspec} +import org.scalatest.{funspec, Outcome} import funspec._ trait TempDirectoryFixture { self: FixtureAnyFunSpec => diff --git a/src/test/scala/org/combinators/templating/twirl/JavaTest.scala b/src/test/scala/org/combinators/templating/twirl/JavaTest.scala index 1ff44f4..5dd01d6 100644 --- a/src/test/scala/org/combinators/templating/twirl/JavaTest.scala +++ b/src/test/scala/org/combinators/templating/twirl/JavaTest.scala @@ -18,7 +18,12 @@ package org.combinators.templating.twirl import com.github.javaparser.ast.ImportDeclaration import com.github.javaparser.ast.`type`.Type -import com.github.javaparser.ast.body.{BodyDeclaration, ConstructorDeclaration, FieldDeclaration, MethodDeclaration} +import com.github.javaparser.ast.body.{ + BodyDeclaration, + ConstructorDeclaration, + FieldDeclaration, + MethodDeclaration +} import com.github.javaparser.ast.expr._ import com.github.javaparser.ast.stmt.Statement import org.scalatest._ @@ -94,28 +99,28 @@ class JavaTest extends AnyFunSpec { |} """.stripMargin - val importDecl: ImportDeclaration = Java("import bar.Bar;").importDeclaration() + val importDecl: ImportDeclaration = + Java("import bar.Bar;").importDeclaration() val className: SimpleName = Java("YYY$_ZZZ").simpleName() - val singleStatement: Statement = Java(s"""System.out.println("never do this in practice!");""").statement() - val multiStatements: Seq[Statement] = Java( - s""" + val singleStatement: Statement = + Java(s"""System.out.println("never do this in practice!");""").statement() + val multiStatements: Seq[Statement] = Java(s""" |int x = 1; // I don't know |for (int y = 10; y > x; y--) { | System.out.println("why I do this"); |}""".stripMargin).statements() - val someString: StringLiteralExpr = Java(""""Ei cän \" dö Ünicöð€"""").expression() + val someString: StringLiteralExpr = + Java(""""Ei cän \" dö Ünicöð€"""").expression() val tpe: Type = Java("Hululu.Lalala[][]").tpe() val fieldDeclarations: Seq[FieldDeclaration] = - Java( - s""" + Java(s""" |static X xoo = null; |Y yoo = new Yoo(); |Z[] zoos;""".stripMargin).fieldDeclarations() val name: Name = Java("some.EnvironmentClass.xoo").name() val nameExpr: NameExpr = Java("xoo").nameExpression() val constructors: Seq[ConstructorDeclaration] = - Java( - s""" + Java(s""" |/** By default do crazyness. */ |public YYY$$_ZZZ() { | System.disco(); @@ -128,22 +133,19 @@ class JavaTest extends AnyFunSpec { | System.disco(); |}""".stripMargin).constructors() val singleDecl: BodyDeclaration[?] = - Java( - s""" + Java(s""" |public static void main(String[] args) { | System.out.println("The application has crashed"); | System.out.println("Never run this application again"); |}""".stripMargin).classBodyDeclaration() val multiDecls: Seq[BodyDeclaration[?]] = - Java( - s""" + Java(s""" |public static volatile String gargh = "GAAAAAAARRRG!!"; |@nobodyunderstands public int getReason() { return reason; } |public int reason = 42;""".stripMargin).classBodyDeclarations() val methodDecls: Seq[MethodDeclaration] = - Java( - s""" + Java(s""" |private void hiddenEffect() { | System.evil(); | } @@ -152,28 +154,34 @@ class JavaTest extends AnyFunSpec { | hiddenEffect(); // I break my contract }:-> | }""".stripMargin).methodDeclarations() val interfaceMethod: BodyDeclaration[?] = - Java("public default void doFoos(YYY$_ZZZ x) { System.out.println(x.toString()); }").interfaceBodyDeclaration() + Java( + "public default void doFoos(YYY$_ZZZ x) { System.out.println(x.toString()); }" + ).interfaceBodyDeclaration() describe("Rendering a Java template with lots of stuff and strings in it") { - val rendered: JavaFormat.Appendable = org.combinators.templating.java.JavaTemplateTest.render( - imp = importDecl, - className = className, - singleStatement = singleStatement, - multiStatements = multiStatements, - someString = someString, - qualifiedName = name, - nameExpression = nameExpr, - singleDecl = singleDecl, - multiDecls = multiDecls, - fieldDeclarations = fieldDeclarations, - methodDeclarations = methodDecls, - constructors = constructors, - interfaceMethod = interfaceMethod, - tpe = tpe) + val rendered: JavaFormat.Appendable = + org.combinators.templating.java.JavaTemplateTest.render( + imp = importDecl, + className = className, + singleStatement = singleStatement, + multiStatements = multiStatements, + someString = someString, + qualifiedName = name, + nameExpression = nameExpr, + singleDecl = singleDecl, + multiDecls = multiDecls, + fieldDeclarations = fieldDeclarations, + methodDeclarations = methodDecls, + constructors = constructors, + interfaceMethod = interfaceMethod, + tpe = tpe + ) describe("when parsing it") { val parsedResult = rendered.compilationUnit() it("should be equal to the expected result") { - assert(Java(expected).compilationUnit().toString == parsedResult.toString) + assert( + Java(expected).compilationUnit().toString == parsedResult.toString + ) } } } diff --git a/src/test/scala/org/combinators/templating/twirl/PythonTest.scala b/src/test/scala/org/combinators/templating/twirl/PythonTest.scala index 344e516..2b63879 100644 --- a/src/test/scala/org/combinators/templating/twirl/PythonTest.scala +++ b/src/test/scala/org/combinators/templating/twirl/PythonTest.scala @@ -43,14 +43,12 @@ class PythonTest extends AnyFunSpec { val clsName: Python = Python("Foo") val body: Python = - Python( - """self.blah = "eggs!" + Python("""self.blah = "eggs!" |if true: | pass |else: | pass""".stripMargin) - val bodyTight: Python = Python( - """if false: + val bodyTight: Python = Python("""if false: | pass |else: | pass""".stripMargin) @@ -61,7 +59,8 @@ class PythonTest extends AnyFunSpec { clsName = clsName, text = text, body = body, - bodyTight = bodyTight) + bodyTight = bodyTight + ) it("should produce exactly the expected substitution") { assert(expected.trim == rendered.getCode.trim) } From fe193a6ae41d488774d6e8303c0d2332286004d2 Mon Sep 17 00:00:00 2001 From: Heineman Date: Tue, 29 Apr 2025 12:02:07 -0400 Subject: [PATCH 6/7] convert backslash N into System.lineSeparator() --- src/main/scala/org/combinators/templating/twirl/Python.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/combinators/templating/twirl/Python.scala b/src/main/scala/org/combinators/templating/twirl/Python.scala index 5296c01..72aa743 100644 --- a/src/main/scala/org/combinators/templating/twirl/Python.scala +++ b/src/main/scala/org/combinators/templating/twirl/Python.scala @@ -35,13 +35,13 @@ class Python private (elements: immutable.Seq[Python], text: String) /** Indents this fragment by 4 spaces. */ def indent: Python = { - Python(fullText.linesIterator.map(l => s" $l").mkString("\n")) + Python(fullText.linesIterator.map(l => s" $l").mkString(System.lineSeparator())) } /** Indents everything except the first line in this fragment by 4 spaces. */ def indentExceptFirst: Python = { val lines: Seq[String] = fullText.linesIterator.to(Seq) - Python((lines.head +: lines.tail.map(l => s" $l")).mkString("\n")) + Python((lines.head +: lines.tail.map(l => s" $l")).mkString(System.lineSeparator())) } /** Returns the code of this fragment as a String. */ From 07ea044773b066f799a7e513fe820dd61ba0daa8 Mon Sep 17 00:00:00 2001 From: Jan Bessai Date: Tue, 29 Apr 2025 18:21:26 +0200 Subject: [PATCH 7/7] Fix PGP environment variable name --- .github/workflows/test_and_release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_and_release.yml b/.github/workflows/test_and_release.yml index 6725587..183cd6c 100644 --- a/.github/workflows/test_and_release.yml +++ b/.github/workflows/test_and_release.yml @@ -69,13 +69,13 @@ jobs: SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} SONA_USER: ${{ secrets.SONATYPE_USERNAME }} SONA_PASS: ${{ secrets.SONATYPE_PASSWORD }} - GPG_PRIVATE: ${{ secrets.GPG_PRIVATE }} + PGP_SECRET: ${{ secrets.PGP_SECRET }} PGP_PASS: ${{ secrets.PGP_PASS }} CI: github run: | git fetch --prune --unshallow --tags && export GPG_TTY=$(tty) && - echo $GPG_PRIVATE | base64 -d | gpg --passphrase=$PGP_PASS --yes --batch --pinentry-mode loopback --import && + echo $PGP_SECRET | base64 -d | gpg --passphrase=$PGP_PASS --yes --batch --pinentry-mode loopback --import && export PATH=`pwd`/.github/bin:$PATH && sbt + test ciReleaseTagNextVersion ciReleaseSonatype