From a2e3232527bc666ba4486b7c551e51c3cd13446e Mon Sep 17 00:00:00 2001 From: Oliver Tupran Date: Sun, 4 Feb 2024 12:31:29 +0100 Subject: [PATCH] 2.0.0-RC01 Cross compilation 2.12 & 2.12 - Cross compilation on Scala 2.12 and Scala 2.13 - Removed the `config-z` module - Removed `TraversableOps` as it will be deprecated in future Scala versions --- README.md | 46 +-- RELEASE-NOTES.md | 24 ++ build.sbt | 23 +- config-z/docs/configuration-framework.md | 246 ----------- .../scala/org/tupol/configz/configz.scala | 384 ------------------ .../resources/MockRunnable/application.conf | 6 - .../test/resources/io/sample-text.resource | 2 - config-z/src/test/resources/reference.conf | 12 - .../scala/examples/MyComplexExample.scala | 114 ------ .../test/scala/examples/MySimpleExample.scala | 89 ---- .../configz/CompositeExtractorSpec.scala | 56 --- .../tupol/configz/EitherExtractorSpec.scala | 46 --- .../org/tupol/configz/MapExtractorSpec.scala | 341 ---------------- .../tupol/configz/OptionExtractorSpec.scala | 111 ----- .../configz/PrimitiveExtractorSpec.scala | 196 --------- .../org/tupol/configz/RichConfigSpec.scala | 25 -- .../org/tupol/configz/SeqExtractorSpec.scala | 138 ------- .../configz/StringToRangeExtractorSpec.scala | 25 -- .../org/tupol/configz/StringToRangeProp.scala | 56 --- .../org/tupol/configz/StringToRangeSpec.scala | 54 --- .../scala/org/tupol/utils/CollectionOps.scala | 6 - .../scala/org/tupol/utils/EitherOps.scala | 20 +- .../scala/org/tupol/utils/EitherUtils.scala | 8 +- .../scala/org/tupol/utils/FutureOps.scala | 8 +- .../scala/org/tupol/utils/FutureUtils.scala | 27 +- .../main/scala/org/tupol/utils/TryOps.scala | 18 +- .../main/scala/org/tupol/utils/TryUtils.scala | 8 +- .../scala/org/tupol/utils/implicits.scala | 11 +- .../scala/org/tupol/utils/io/TextReader.scala | 2 +- .../scala/org/tupol/utils/io/TextSource.scala | 2 +- .../tupol/utils/EitherTraversableSpec.scala | 2 +- .../scala/org/tupol/utils/FutureOpsSpec.scala | 6 +- .../org/tupol/utils/ToOptionNelSpec.scala | 24 +- .../tupol/utils/jdbc/RowExtractorsSpec.scala | 4 +- project/Dependencies.scala | 15 +- project/plugins.sbt | 3 +- version.sbt | 2 +- 37 files changed, 121 insertions(+), 2039 deletions(-) delete mode 100644 config-z/docs/configuration-framework.md delete mode 100644 config-z/src/main/scala/org/tupol/configz/configz.scala delete mode 100644 config-z/src/test/resources/MockRunnable/application.conf delete mode 100644 config-z/src/test/resources/io/sample-text.resource delete mode 100644 config-z/src/test/resources/reference.conf delete mode 100644 config-z/src/test/scala/examples/MyComplexExample.scala delete mode 100644 config-z/src/test/scala/examples/MySimpleExample.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/CompositeExtractorSpec.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/EitherExtractorSpec.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/MapExtractorSpec.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/OptionExtractorSpec.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/PrimitiveExtractorSpec.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/RichConfigSpec.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/SeqExtractorSpec.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/StringToRangeExtractorSpec.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/StringToRangeProp.scala delete mode 100644 config-z/src/test/scala/org/tupol/configz/StringToRangeSpec.scala diff --git a/README.md b/README.md index 5c9bba5..ee27d72 100644 --- a/README.md +++ b/README.md @@ -8,23 +8,20 @@ [![Discord](https://img.shields.io/discord/963384629572812860)](https://discord.gg/kDFsJ3y7Du)   [![Twitter](https://img.shields.io/twitter/url/https/_tupol.svg?color=%2317A2F2)](https://twitter.com/_tupol)   + ## Description This project contains some basic utilities that can help setting up a Scala project. The main utilities available: -- [Configuration framework](config-z/docs/configuration-framework.md) - Conversion to byte array in the [`byteable`](core/src/main/scala/org/tupol/utils/ByteableOps.scala) package. - `Try`, `Future`, `Either`, `Product`, `Map` and other utilities in the [`utils`](core/src/main/scala/org/tupol/utils/) package. -***Attention!*** The [Configuration framework](config-z/docs/configuration-framework.md) might be - deprecated, as the [PureConfig](https://pureconfig.github.io/) framework is much more mature and - provides a better overall solution. ## Prerequisites ## * Java 8 or higher (matching the Scala version) -* Scala 2.12 +* Scala 2.12 or 2.13 ## Getting Scala Utils ## @@ -35,11 +32,11 @@ where the latest artifacts can be found. Usage with SBT, adding a dependency to the latest version of scala utils to your sbt build definition file: ```scala - libraryDependencies += "org.tupol" %% "scala-utils-core" % "1.0.0" + libraryDependencies += "org.tupol" %% "scala-utils-core" % "2.0.0" ``` or ```scala - libraryDependencies += "org.tupol" %% "scala-utils-core" % "1.0.0" + libraryDependencies += "org.tupol" %% "scala-utils-core" % "2.0.0" ``` For Snapshots, the Sonatype snapshots repo needs to be added as well: @@ -49,39 +46,14 @@ For Snapshots, the Sonatype snapshots repo needs to be added as well: ``` -## Usage - -Some `config-z` usage examples can be found under [`config-z/src/test/scala/examples`](config-z/src/test/scala/examples). - - ## What's new? -**1.1.2** -- `jdbc` module - added `PreparedStatement` support -- `jdbc` module - added configuration and `ConnectionPool` support - -**1.1.1** -- Added an experimental `jdbc` module - -**1.1.0** - - Refactored the `configz` module - - Added a few more utility implicits to the `core` module - - -**1.0.0** - -This new major version aims to bring a new and hopefully cleaner project structure. -The `scalaz` based configuration is moved to a different module to isolate better from the core. -In the future a `cats` based version might be added as well. - -More core utilities were added and the old ones were brushed up for better consistency and clarity. - -Attention! This version is no longer cross compiling across Scala 2.11 and 2.12. -Only Scala 2.12 is supported at the moment. - -The previous versions are still available and can evolve independently on the **[`0.2.x`](https://github.com/tupol/scala-utils/tree/0.2.x)** branch. +**2.0.0** -For previous versions please consult the [release notes](RELEASE-NOTES.md). +- Cross compilation on Scala 2.12 and Scala 2.13 +- Compiled with JDK 17 targeting Java 8 +- Removed the `config-z` module +- Removed `TraversableOps` as it will be deprecated in future Scala versions ## License ## diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 323d19a..a733014 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,29 @@ # Release Notes +## 2.0.0 + +- Cross compilation on Scala 2.12 and Scala 2.13 +- Compiled with JDK 17 targeting Java 8 +- Removed the `config-z` module +- Removed `TraversableOps` as it will be deprecated in future Scala versions + + +## 1.1.2 + +- `jdbc` module - added `PreparedStatement` support +- `jdbc` module - added configuration and `ConnectionPool` support + +## 1.1.1 + +- Added an experimental `jdbc` module + +## 1.1.0 + +- Refactored the `configz` module +- Added a few more utility implicits to the `core` module + + + ## 1.0.0 - Added the Collections `toOptionNel` utility diff --git a/build.sbt b/build.sbt index 0bbe438..68a791a 100644 --- a/build.sbt +++ b/build.sbt @@ -51,7 +51,8 @@ lazy val publishSettings = Seq( lazy val coverageSettings = Seq( coverageEnabled in Test := true, - coverageMinimum in Test := 95, + coverageMinimumStmtTotal in Test := 95, + coverageMinimumBranchTotal in Test := 80, coverageFailOnMinimum in Test := true, coverageExcludedPackages := ".*BuildInfo.*" ) @@ -59,13 +60,13 @@ lazy val coverageSettings = Seq( lazy val basicSettings = Seq( organization := "org.tupol", name := "scala-utils", - scalaVersion := Versions.scala, + scalaVersion := Versions.scala_2_12, crossScalaVersions := Versions.crossScala, scalacOptions ++= Seq( "-feature", "-deprecation", "-unchecked", - "-Ywarn-unused-import" + s"-release:${Versions.targetJava}" ), updateOptions := updateOptions.value.withCachedResolution(true), libraryDependencies ++= CoreTestDependencies, @@ -99,21 +100,9 @@ lazy val jdbc = (project in file("jdbc")) ) .dependsOn(core % "test->test;compile->compile") -lazy val config_z = (project in file("config-z")) - .enablePlugins(BuildInfoPlugin) - .settings(commonSettings: _*) - .settings( - name := "scala-utils-config-z", - buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion), - buildInfoOptions := Seq[BuildInfoOption](BuildInfoOption.BuildTime, BuildInfoOption.ToMap, BuildInfoOption.ToJson), - buildInfoPackage := "org.tupol.utils.config", - libraryDependencies ++= ConfigZDependencies - ) - .dependsOn(core % "test->test;compile->compile") - lazy val scala_utils = Project( id = "scala-utils", base = file(".") ).settings(commonSettings: _*) - .dependsOn(core % "test->test;compile->compile", jdbc, config_z) - .aggregate(core, jdbc, config_z) + .dependsOn(core % "test->test;compile->compile", jdbc) + .aggregate(core, jdbc) diff --git a/config-z/docs/configuration-framework.md b/config-z/docs/configuration-framework.md deleted file mode 100644 index e2fc45d..0000000 --- a/config-z/docs/configuration-framework.md +++ /dev/null @@ -1,246 +0,0 @@ -# Configuration Framework - -[[_TOC_]] - -***Attention!*** - -The *Configuration framework* is deprecated, as other frameworks, like -[PureConfig](https://pureconfig.github.io/), are more mature and provides a better -overall solution. - -## Scope - -This section is describing the current configuration framework and most importantly the usage patterns. - - -## Introduction - -The configuration framework is based on *ScalaZ* `validation` infrastructure. - -The main reason for using the *ScalaZ* `ValidationNel` is to be able to collect all error messages found during the -configuration of the target object, and showing them to the user in one go, rather than one by one, as each problem is -fixed, application is run again and the next problem is revealed. - -The key concepts are: -- `org.tupol.configz.Configurator` -- `scalaz.ValidationNel`; for more details look [here](https://github.com/scalaz/scalaz/blob/series/7.2.x/core/src/main/scala/scalaz/Validation.scala) -- `com.typesafe.config.Config`; for more details look [here](https://github.com/typesafehub/config) - -The `Configurator` is used as a factory for case classes starting from a Typesafe `Config` instance, as shown in the -`Configurator` trait itself: - -```scala - import com.typesafe.config.Config - import scalaz.ValidationNel - - trait Configurator[T] { - def validationNel(config: Config): ValidationNel[Throwable, T] - def apply(config: Config): Try[T] = validationNel(config) - } -``` -The type parameter `T` is the type of the class instance we are constructing. - - -## Usage - -In order to create a new configuration we need the following: -1. A case class to configure or select an existing one -2. A corresponding `Configurator` containing an implementation of the `validationNel()` function - -### Simple Configuration Example - -The full example is available in [`examples.MySimpleExample`](../src/test/scala/examples/MySimpleExample.scala). - -#### 1. Create a case class to configure or select an existing one - -The normal pattern we used so far is to create a configuration case class that will only hold the configuration of the -class we actually want to configure. -This is not the only way of doing it, and we can configure any case class directly (see the `NaiveProbifier`). - -Let's say that we have an example class that we need to configure, like bellow: - -```scala -case class MySimpleExample(path: String, overwrite: Boolean) { - def write(something: String) = { - // Some code using the path and the overwrite parameters. - //... - println(s"""Writing "$something" to "$path" with the overwrite flag set to "$overwrite".""") - } -} -``` - -#### 2. Create a corresponding `Configurator` and implement the `validationNel()` function - -```scala -import org.tupol.configz.Configurator - -object MySimpleExample extends Configurator[MySimpleExample] { - - import com.typesafe.config.Config - import scalaz.ValidationNel - - override def validationNel(config: Config): ValidationNel[Throwable, MySimpleExample] = { - - import org.tupol.scala.config._ - import scalaz.syntax.applicative._ - - config.extract[String]("path") |@| config.extract[Boolean]("overwrite") apply MySimpleExample.apply - } -} -``` - - - -***Notes*** - -1. Normally we define the configurator factory in a companion object attached to the case class that we want to configure. - -2. The only top level import is `import org.tupol.configz.Configurator`. - We are quite keen on keeping the top level imports clean, and only import locally what we need. - The subsequent sets of imports are inside the `MySimpleExample` object scope and in the `validationNel` function scope. - -3. We only need to implement the `validationNel` function. - -4. Notice the `config.extract[String]("path")` function call. What happens here is the following: - - the `config` is implicitly converted to a `RichConfig` instance, that uses the *type class* pattern in the `extract` function - - the actual type classes can be found inside the `Extractor` object, all available in the `org.tupol.utils.config` package - - the `path` refers to a path inside the `config` object passed in as an argument - - the `[String]` type parameter is used to trigger the type class matching - - the `RichConfig.extract` function returns `ValidationNel[Throwable, T]` - - to conclude, each `config.extract[T]("configuration_path")` returns a `ValidationNel[Throwable, T]` - -5. Notice the `|@|` operator. - - the `|@|` operator is used to compose `ValidationNel[Throwable, T]`s and create an `ApplicativeBuilder` - - on the `ApplicativeBuilder` created we can call `apply` and pass in a function that creates our object, `MySimpleExample` - - for a case class or for smart constructor in companion objects, such an function is `apply` - - the order of the `config.extract[T]("configuration_path")` is very important and it needs to match the type - signature of the case class we are trying to construct; - In our example we can not change the order of `config.extract[String]("path")` and `config.extract[Boolean]("overwrite")` - because composing them will create a tuple of Boolean and String and there is no such constructor available - in `MySimpleExample` - - in case our target class has multiple constructors, we need to specify the constructor better; for example we - can use something like `MyComplexExample.apply(_, _, _)` - - the final result of the `validationNel` function is a `ValidationNel[Throwable, T]` - -6. When actually building a configured instance, the `apply` function will be used, that yields a `Try[T]`. - - the returned try may contain either the successfully configured instance or a failure containing a `ConfigurationException` - - the `ConfigurationException` wraps all the `Throwables` accumulated by the `ValidationNel` and has a human - friendly message that lists all the problems. - - - -### Composite Configuration Example - -Just as described previously, we can also create configuration objects that are based on other configuration objects. - -The full example is available in [`examples.MyComplexExample`](../src/test/scala/examples/MyComplexExample.scala). - - -#### 1. Create a case class to configure or select an existing one - -```scala -case class MyComplexExample(example: MySimpleExample, separatorChar: String, separatorSize: Int) { - def write(something: String) = { - // Some code using the path and the overwrite parameters. - //... - val separator = (0 until separatorSize).map(_ => separatorChar).mkString("") - println(separator) - println(s"""Writing "$something" to "${example.path}" with the overwrite flag set to "${example.overwrite}".""") - } -} -``` - -***Notes*** - -1. Notice that `MyComplexExample` has as a class parameter a `MySimpleExample`. - - -#### 2. Create a corresponding `Configurator` and implement the `validationNel()` function - -```scala -import org.tupol.configz.Configurator - -object MyComplexExample extends Configurator[MyComplexExample] { - - import com.typesafe.config.Config - import scalaz.ValidationNel - - override def validationNel(config: Config): ValidationNel[Throwable, MyComplexExample] = { - - import org.tupol.scala.config._ - import scalaz.syntax.applicative._ - import com.typesafe.config.ConfigException.BadValue - - val separatorChar = config.extract[String]("separatorChar"). - ensure(new BadValue("separatorChar", "should be a single character.").toNel)(t => t.length == 1) - - val separatorSize = config.extract[Int]("separatorSize"). - ensure(new IllegalArgumentException("The separatorSize should be between 1 and 80.").toNel)(s => s > 0 && s <= 80) - - MySimpleExample.validationNel(config) |@| separatorChar |@| separatorSize apply MyComplexExample.apply - } -} -``` - -A defined `Configurator` can also be used as an `Extractor` itself, so with a simple `implicit` the example above can be -written more elegantly like the following: - - -```scala -import org.tupol.configz.Configurator - -implicit val mySimpleExampleExtractor = MySimpleExample - -object MyComplexExample extends Configurator[MyComplexExample] { - - import com.typesafe.config.Config - import scalaz.ValidationNel - - override def validationNel(config: Config): ValidationNel[Throwable, MyComplexExample] = { - - import org.tupol.scala.config._ - import scalaz.syntax.applicative._ - import com.typesafe.config.ConfigException.BadValue - - val separatorChar = config.extract[String]("separatorChar"). - ensure(new BadValue("separatorChar", "should be a single character.").toNel)(t => t.length == 1) - - val separatorSize = config.extract[Int]("separatorSize"). - ensure(new IllegalArgumentException("The separatorSize should be between 1 and 80.").toNel)(s => s > 0 && s <= 80) - - val mySimpleExample = config.extract[MySimpleExample] - - mySimpleExample |@| separatorChar |@| separatorSize apply MyComplexExample.apply - } -} -``` - -Notice in the example above the implicit definition of the `MySimpleExample` extractor as -`implicit val mySimpleExampleExtractor = MySimpleExample`. -Also notice the actual extraction of `MySimpleExample` inside the `MyComplexExample.validationNel` function, -as `config.extract[MySimpleExample]`. -This pattern can simplify a lot the way complex configuration objects are created. - - -***Notes*** - -1. Notice the way the `separatorChar` and `separatorSize` are defined outside the `ApplicativeBuilder` composition line. - -2. Notice that both `separatorChar` and `separatorSize` have extra validation rules implemented through the `ensure` - function. - - the `ensure` function - - takes as a first argument a function that creates a `Throwable` and - - takes as a second argument a predicate that is applied to the value contained in the `ValidationNel` - - if testing the value against the predicate fails, then the `Throwable` defined as a first parameter is added - to the `NonEmptyList[Throwable]` of the `ValidationNel` - - for more details don't hesitate to check the [scalaz documentation](https://github.com/scalaz/scalaz/blob/series/7.2.x/core/src/main/scala/scalaz/Validation.scala) - - the `Throwable`s are implicitly converted to `NonEmptyList[Throwable]` by the `org.tupol.utils.config._` package - defined implicits; - This way the type signature `ValidationNel[Throwable, T]` is preserved. - -3. For the final line to work, `MySimpleExample.validationNel(config)` needs to be defined. - - -## Credits & Thanks ## - -- [Daan Hoogenboezem](https://github.com/daanhoogenboezem) For sketching the initial configuration framework. diff --git a/config-z/src/main/scala/org/tupol/configz/configz.scala b/config-z/src/main/scala/org/tupol/configz/configz.scala deleted file mode 100644 index f5da034..0000000 --- a/config-z/src/main/scala/org/tupol/configz/configz.scala +++ /dev/null @@ -1,384 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Tupol (github.com/tupol) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - */ -package org.tupol - -import com.typesafe.config.ConfigException.{ BadValue, Missing } -import com.typesafe.config.{ Config, ConfigObject } -import org.tupol.utils.implicits._ -import scalaz.syntax.validation._ -import scalaz.{ NonEmptyList, ValidationNel } - -import java.sql.{ Date, Timestamp } -import java.time.{ Duration, LocalDate, LocalDateTime } -import java.util -import scala.annotation.implicitNotFound -import scala.collection.JavaConverters._ -import scala.util.{ Failure, Success, Try } - -/** - * This package holds a framework for extracting configuration objects out of configuration files, using the - * Typesafe Config library. - * Alternative solution: PureConfig - */ -package object configz { - - /** - * An extractor is a bit of code that knows how to extract a value of type T - * from a Typesafe {{Config}} instance. - * - * @tparam T the type that this implementation can extract for us. - */ - trait Extractor[T] { - - val EmptyPath = "\"\"" - - /** - * Extract an object instance of type `T` from the given config at the given path. - * @param config the Typesafe Config instance. - * @param path the path at which the value resides. - * @return the value that was retrieved. - */ - def extract(config: Config, path: String): Try[T] - def extract(config: Config): Try[T] = extract(config.atPath(EmptyPath), EmptyPath) - - } - - /** - * Implementors of this trait how to construct a class of type T from a Typesafe Config instance. - * @tparam T, the type of class this Configurator knows how to validate/construct. - */ - trait Configurator[T] extends Extractor[T] { - protected def validationNel(config: Config): ValidationNel[Throwable, T] - override def extract(config: Config, path: String): Try[T] = - Try(config.getConfig(path)).flatMap(validationNel(_)) - } - - /** - * Adds the extract method to a Typesafe Config instance, allowing us to request values from it like so: - * {{config.extract[Double]("double")}} or {{config.extract[Option[Range]]("range")}} - * - * @param config the Typesafe Config instance to operate on. - */ - implicit class RichConfig(config: Config) { - def extract[T: Extractor](path: String): ValidationNel[Throwable, T] = - tryExtraction(Extractor[T].extract(config, path)) - - private def tryExtraction[T](extract: => Try[T]): ValidationNel[Throwable, T] = - extract match { - case Success(value) => value.success - case Failure(exception: Throwable) => exception.failureNel - } - - def extract[T: Extractor]: ValidationNel[Throwable, T] = tryExtraction(Extractor[T].extract(config)) - def validatePath(path: String): ValidationNel[Throwable, String] = - if (config.hasPath(path)) path.successNel - else (new Missing(path): Throwable).failureNel - } - - /** - * Add a convenience method to convert an Exception to a NonEmptyList[Throwable]. - * @param throwable - */ - implicit class ThrowableOps(val throwable: Throwable) extends AnyVal { - def toNel: NonEmptyList[Throwable] = NonEmptyList(throwable) - } - - /** - * Implicit conversion from ValidationNel[Exception,T] to Try[T]. Using this construct allows - * us to keep the Validation logic contained in configuration code, keeping the rest of our code scalaz agnostic. - */ - import scala.language.implicitConversions - implicit def validationNelToTry[E <: Throwable, T](validation: ValidationNel[E, T]): Try[T] = - validation match { - case scalaz.Failure(exceptions) => Failure(ConfigurationException(exceptions.list.toList)) - case scalaz.Success(value) => Success(value) - } - - /** - * Encapsulates all configuration exceptions that occurred while trying to map - * the data from a Typesafe Config object onto a case class. - */ - case class ConfigurationException(errors: Seq[Throwable]) extends Exception { - override def toString = s"ConfigurationException[${getMessage()}]" - - override def getMessage: String = - ("Invalid configuration. Please check the issue(s) listed bellow:" +: - errors.map(error => s"- ${error.getMessage}")).mkString("\n") - } - - object Extractor { - - @implicitNotFound("No compatible Extractor in scope for type ${T}") - def apply[T](implicit T: Extractor[T]): Extractor[T] = T - - implicit val stringExtractor = new Extractor[String] { - def extract(config: Config, path: String): Try[String] = Try(config.getString(path)) - } - - implicit val booleanExtractor = new Extractor[Boolean] { - def extract(config: Config, path: String): Try[Boolean] = Try(config.getBoolean(path)) - } - - implicit val characterExtractor = new Extractor[Character] { - def extract(config: Config, path: String): Try[Character] = Try(config.getString(path).charAt(0)) - } - - implicit val intExtractor = new Extractor[Int] { - def extract(config: Config, path: String): Try[Int] = Try(config.getInt(path)) - } - - implicit val longExtractor = new Extractor[Long] { - def extract(config: Config, path: String): Try[Long] = Try(config.getLong(path)) - } - - implicit val doubleExtractor = new Extractor[Double] { - def extract(config: Config, path: String): Try[Double] = Try(config.getDouble(path)) - } - - implicit val durationExtractor = new Extractor[Duration] { - def extract(config: Config, path: String): Try[Duration] = Try(config.getDuration(path)) - } - - /** Expected format: yyyy-[m]m-[d]d hh:mm:ss[.f...] */ - implicit val timestampExtractor = new Extractor[Timestamp] { - def extract(config: Config, path: String): Try[Timestamp] = Try(Timestamp.valueOf(config.getString(path))) - } - - /** Expected format: yyyy-[m]m-[d]d */ - implicit val dateExtractor = new Extractor[Date] { - def extract(config: Config, path: String): Try[Date] = Try(Date.valueOf(config.getString(path))) - } - - implicit val localDateExtractor = new Extractor[LocalDate] { - def extract(config: Config, path: String): Try[LocalDate] = Try(LocalDate.parse(config.getString(path))) - } - - implicit val localDateTimeExtractor = new Extractor[LocalDateTime] { - def extract(config: Config, path: String): Try[LocalDateTime] = Try(LocalDateTime.parse(config.getString(path))) - } - - implicit val anyListExtractor = new Extractor[Seq[Any]] { - def extract(config: Config, path: String): Try[Seq[Any]] = - Try(Seq(config.getAnyRefList(path).asScala: _*)) - } - - implicit val stringListExtractor = new Extractor[Seq[String]] { - def extract(config: Config, path: String): Try[Seq[String]] = - Try(Seq(config.getStringList(path).asScala: _*)) - } - - implicit val booleanListExtractor = new Extractor[Seq[Boolean]] { - def extract(config: Config, path: String): Try[Seq[Boolean]] = - Try(Seq(config.getBooleanList(path).asScala: _*).map(_.booleanValue())) - } - - implicit val intListExtractor = new Extractor[Seq[Int]] { - def extract(config: Config, path: String): Try[Seq[Int]] = - Try(Seq(config.getIntList(path).asScala: _*).map(_.intValue())) - } - - implicit val longListExtractor = new Extractor[Seq[Long]] { - def extract(config: Config, path: String): Try[Seq[Long]] = - Try(Seq(config.getLongList(path).asScala: _*).map(_.longValue())) - } - - implicit val doubleListExtractor = new Extractor[Seq[Double]] { - def extract(config: Config, path: String): Try[Seq[Double]] = - Try(Seq(config.getDoubleList(path).asScala: _*).map(_.doubleValue)) - } - - implicit val configListExtractor = new Extractor[Seq[Config]] { - def extract(config: Config, path: String): Try[Seq[Config]] = - Try(Seq(config.getConfigList(path).asScala.map(_.withOnlyPath(path)): _*)) - } - - /** - * Extract a list of composite types for every T that there is an extractor defined for - * @param extractor an implicit Extractor[T] that needs to be in scope - * @tparam T the extracted value - * @return A Seq(T) if we can extract a valid property of the given type or throw an Exception - */ - implicit def listExtractor[T](implicit extractor: Extractor[T]): Extractor[Seq[T]] = - new Extractor[Seq[T]] { - override def extract(config: Config, path: String): Try[Seq[T]] = { - def fromObjects = - Seq(config.getObjectList(path).asScala: _*) - .map(co => extractor.extract(co.toConfig.atPath(path), path)) - .allOkOrFail - def fromConfigs = - Seq(config.getConfigList(path).asScala: _*) - .map(co => extractor.extract(co.atPath(path), path)) - .allOkOrFail - def fromList = - Seq(config.getList(path).asScala: _*) - .map(co => extractor.extract(co.atPath(path), path)) - .allOkOrFail - (fromList orElse fromConfigs orElse fromObjects).map(_.toSeq) - } - } - - implicit val anyMapExtractor = new Extractor[Map[String, Any]] { - def extract(config: Config, path: String): Try[Map[String, Any]] = { - def fromObject(o: ConfigObject): Set[(String, Any)] = - Set( - config - .getObject(path) - .entrySet - .asScala - .map(e => (e.getKey, e.getValue.unwrapped.asInstanceOf[Any])) - .toSeq: _* - ) - def fromObjects: Seq[(String, Any)] = (config.getObjectList(path).asScala).flatMap(fromObject(_)) - def fromList: Seq[(String, Any)] = - Seq(config.getList(path).asScala: _*).flatMap { co => - val kv = co.unwrapped.asInstanceOf[util.HashMap[_, _]].asScala.toSeq - kv.map { case (k, v) => (k.toString, v.asInstanceOf[Any]) } - } - (Try(fromList) orElse Try(fromObject(config.getObject(path))) orElse Try(fromObjects)).map(_.toMap) - } - } - - implicit val stringMapExtractor = new Extractor[Map[String, String]] { - def extract(config: Config, path: String): Try[Map[String, String]] = - anyMapExtractor.extract(config, path).map(_.mapValues(_.toString)) - } - - implicit val booleanMapExtractor = new Extractor[Map[String, Boolean]] { - def extract(config: Config, path: String): Try[Map[String, Boolean]] = - stringMapExtractor.extract(config, path).map(_.mapValues(_.toBoolean)) - } - - implicit val intMapExtractor = new Extractor[Map[String, Int]] { - def extract(config: Config, path: String): Try[Map[String, Int]] = - stringMapExtractor.extract(config, path).map(_.mapValues(_.toInt)) - } - - implicit val longMapExtractor = new Extractor[Map[String, Long]] { - def extract(config: Config, path: String): Try[Map[String, Long]] = - stringMapExtractor.extract(config, path).map(_.mapValues(_.toLong)) - } - - implicit val doubleMapExtractor = new Extractor[Map[String, Double]] { - def extract(config: Config, path: String): Try[Map[String, Double]] = - stringMapExtractor.extract(config, path).map(_.mapValues(_.toDouble)) - } - - /** - * Extract a Map with String keys and composite value types for every T that there is an extractor defined for - * @param extractor an implicit Extractor[T] that needs to be in scope - * @tparam T the extracted value - * @return A Seq(T) if we can extract a valid property of the given type or throw an Exception - */ - implicit def mapExtractor[T](implicit extractor: Extractor[T]): Extractor[Map[String, T]] = - new Extractor[Map[String, T]] { - def extract(config: Config, path: String): Try[Map[String, T]] = { - def fromObject(o: ConfigObject): Try[Map[String, T]] = - config - .getObject(path) - .entrySet - .asScala - .map(e => extractor.extract(e.getValue.atPath(e.getKey), e.getKey).map((e.getKey, _))) - .allOkOrFail - .map(_.toMap) - - def fromObjects: Try[Map[String, T]] = - Try(config.getObjectList(path).asScala).flatMap(obs => - obs.map(fromObject(_)).allOkOrFail.map(_.flatten.toMap)) - (fromObject(config.getObject(path)) orElse fromObjects) - } - } - - implicit val rangeExtractor = new Extractor[Range] { - - /** - * Construct a Range from a comma separated list of ints. - * - * Case 1: A sequence of ints - * - first int: 'from' - * - second int: 'to' (bigger than from) - * - third int: 'step' a positive number - * - * Case 2: A single int - * - * @param value -> start,stop,step or just an int value - * @param path -> optional path for this property - * @return - */ - def parseStringToRange(value: String, path: String = "unknown"): Try[Range] = - Try(value.split(",").map(_.trim.toInt).toSeq) match { - case Success(start +: stop +: _ +: Nil) if start > stop => - Failure(new BadValue(path, "The start should be smaller than the stop.")) - case Success(_ +: _ +: step +: Nil) if step < 0 => - Failure(new BadValue(path, "The step should be a positive number.")) - case Success(start +: stop +: step +: Nil) => - Success(start to stop by step) - case Success(v +: Nil) => - Success(v to v) - case _ => - Failure( - new BadValue( - path, - "The input should contain either an integer or a comma separated list of 3 integers." - )) - } - def extract(config: Config, path: String): Try[Range] = parseStringToRange(config.getString(path), path) - } - - /** - * Extract an Option[T] for every T that we've defined an extractor for. - * - * @param extractor an implicit Extractor[T] that needs to be in scope - * @tparam T the extracted value - * @return A Some(T) if we can extract a valid property of the given type or a None otherwise. - */ - implicit def optionExtractor[T](implicit extractor: Extractor[T]): Extractor[Option[T]] = - new Extractor[Option[T]] { - override def extract(config: Config, path: String): Try[Option[T]] = - extractor.extract(config, path) match { - case Success(value) => Success(Some(value)) - case Failure(_) => Success(None) - } - } - - /** - * Extract an Either[A, B] for every A or B that we've defined an extractor for. - * - * @param leftExtractor an implicit Extractor[T] that needs to be in scope - * @param rightExtractor an implicit Extractor[T] that needs to be in scope - * @tparam A the extracted Left value - * @tparam B the extracted Right value - * @return A Left(A) if we could extract the A type, or a Right(B) if we could extract the right type or throw an Exception - */ - implicit def eitherExtractor[A, B](implicit - leftExtractor: Extractor[A], - rightExtractor: Extractor[B] - ): Extractor[Either[A, B]] = - new Extractor[Either[A, B]] { - override def extract(config: Config, path: String): Try[Either[A, B]] = - (leftExtractor.extract(config, path)).map(Left(_)) orElse (rightExtractor.extract(config, path)).map(Right(_)) - } - - } - -} diff --git a/config-z/src/test/resources/MockRunnable/application.conf b/config-z/src/test/resources/MockRunnable/application.conf deleted file mode 100644 index 4e0289b..0000000 --- a/config-z/src/test/resources/MockRunnable/application.conf +++ /dev/null @@ -1,6 +0,0 @@ -# Resource file for the SparkRunnableSpec test - -MockRunnable.whoami = "./src/test/resources/MockRunnable/application.conf" -MockRunnable.some.list = ["a", "b", "c"] -MockRunnable.file.application.conf: true - diff --git a/config-z/src/test/resources/io/sample-text.resource b/config-z/src/test/resources/io/sample-text.resource deleted file mode 100644 index 7bba8c8..0000000 --- a/config-z/src/test/resources/io/sample-text.resource +++ /dev/null @@ -1,2 +0,0 @@ -line 1 -line 2 diff --git a/config-z/src/test/resources/reference.conf b/config-z/src/test/resources/reference.conf deleted file mode 100644 index 598b3ef..0000000 --- a/config-z/src/test/resources/reference.conf +++ /dev/null @@ -1,12 +0,0 @@ -# The only property of the SchemaFactory application that needs to have a value is the inferSchema. -# Since inferring the schema is essentially the job of SchemaFactory this should always be true. -schemafactory: { - inferSchema = true -} - -# Configuration section for the SparkRunnableSpec test -MockRunnable: { - whoami: "./src/test/resources/reference.conf" - reference: "reference" - classpath.application.conf: false -} diff --git a/config-z/src/test/scala/examples/MyComplexExample.scala b/config-z/src/test/scala/examples/MyComplexExample.scala deleted file mode 100644 index 3d843ed..0000000 --- a/config-z/src/test/scala/examples/MyComplexExample.scala +++ /dev/null @@ -1,114 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Tupol (github.com/tupol) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - */ -package examples - -// This is an example on how the configuration framework can be used. - -/** - * This is the class that we want to configure. - * - * @param example - * @param separatorChar some line separator character - * @param separatorSize how many times should the `separatorChar` should be multiplied in forming the separator line - */ -case class MyComplexExample(example: MySimpleExample, separatorChar: String, separatorSize: Int) { - def write(something: String) = { - // Some code using the path and the overwrite parameters. - //... - val separator = (0 until separatorSize).map(_ => separatorChar).mkString("") - println(separator) - println(s"""Writing "$something" to "${example.path}" with the overwrite flag set to "${example.overwrite}".""") - } -} - -import org.tupol.configz.Configurator -import org.tupol.configz._ - -/** - * This is the configurator implementation for the MyExample case class - */ -object MyComplexExample extends Configurator[MyComplexExample] { - - import com.typesafe.config.Config - - import scalaz.ValidationNel - - override def validationNel(config: Config): ValidationNel[Throwable, MyComplexExample] = { - - import com.typesafe.config.ConfigException.BadValue - - import scalaz.syntax.applicative._ - - implicit val mySimpleExampleConfigExtractor = MySimpleExample - - val separatorChar = config - .extract[String]("separatorChar") - .ensure(new BadValue("separatorChar", "should be a single character.").toNel)(t => t.length == 1) - - val separatorSize = config - .extract[Int]("separatorSize") - .ensure(new IllegalArgumentException("The separatorSize should be between 1 and 80.").toNel)(s => - s > 0 && s <= 80) - - config.extract[MySimpleExample] |@| separatorChar |@| separatorSize apply MyComplexExample.apply - } -} - -/** - * This is a simple example on how to use the - */ -object MyComplexExampleDemo extends App { - - import com.typesafe.config.ConfigFactory - val goodConfig = ConfigFactory.parseString(""" - |myExample.path="some_path" - |myExample.overwrite=true - |myExample.separatorChar="*" - |myExample.separatorSize=15 - """.stripMargin) - - println(s"""=============== - | Positive Test - |===============""".stripMargin) - // This is the place where the "magic happens" and everything goes well - // Notice that we extract the exact configuration that we need out of the root configuration object - // by calling `goodConfig.getConfig("myExample")` - val myExample = MyComplexExample.extract(goodConfig.getConfig("myExample")).get - myExample.write("A quick brown fox...") - - println(s"""| - |=============== - | Negative Test - |===============""".stripMargin) - // This is where we see what happens when there are some problems - val wrongConfig = ConfigFactory.parseString(""" - |myExample: { - | path="some_path" - | separatorChar="--" - | separatorSize=81 - |} - """.stripMargin) - print( - MyComplexExample.extract(wrongConfig.getConfig("myExample")).recover { case (t: Throwable) => t.getMessage }.get) -} diff --git a/config-z/src/test/scala/examples/MySimpleExample.scala b/config-z/src/test/scala/examples/MySimpleExample.scala deleted file mode 100644 index d2328ce..0000000 --- a/config-z/src/test/scala/examples/MySimpleExample.scala +++ /dev/null @@ -1,89 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Tupol (github.com/tupol) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - */ -package examples - -// This is an example on how the configuration framework can be used. - -/** - * This is the class that we want to configure. - * - * @param path - * @param overwrite - */ -case class MySimpleExample(path: String, overwrite: Boolean) { - def write(something: String) = - // Some code using the path and the overwrite parameters. - //... - println(s"""Writing "$something" to "$path" with the overwrite flag set to "$overwrite".""") -} - -import org.tupol.configz.Configurator -import org.tupol.configz._ - -/** - * This is the configurator implementation for the MySimpleExample case class - */ -object MySimpleExample extends Configurator[MySimpleExample] { - - import com.typesafe.config.Config - import scalaz.ValidationNel - - override def validationNel(config: Config): ValidationNel[Throwable, MySimpleExample] = { - - import scalaz.syntax.applicative._ - - config.extract[String]("path") |@| config.extract[Boolean]("overwrite") apply MySimpleExample.apply - } -} - -/** - * This is a simple example on how to use the - */ -object MySimpleExampleDemo extends App { - - import com.typesafe.config.ConfigFactory - val goodConfig = ConfigFactory.parseString(""" - |myExample.path="some_path" - |myExample.overwrite=true - """.stripMargin) - - println(s"""=============== - | Positive Test - |===============""".stripMargin) - // This is the place where the "magic happens" and everything goes well - // Notice that we extract the exact configuration that we need out of the root configuration object - // by calling `goodConfig.getConfig("myExample")` - val myExample = MySimpleExample.extract(goodConfig.getConfig("myExample")).get - myExample.write("A quick brown fox...") - - println(s"""| - |=============== - | Negative Test - |===============""".stripMargin) - // This is where we see what happens when there are some problems - val wrongConfig = ConfigFactory.parseString(""" - |myExample: {} - """.stripMargin) - print(MySimpleExample.extract(wrongConfig.getConfig("myExample")).recover { case (t: Throwable) => t.getMessage }.get) -} diff --git a/config-z/src/test/scala/org/tupol/configz/CompositeExtractorSpec.scala b/config-z/src/test/scala/org/tupol/configz/CompositeExtractorSpec.scala deleted file mode 100644 index 5f7701d..0000000 --- a/config-z/src/test/scala/org/tupol/configz/CompositeExtractorSpec.scala +++ /dev/null @@ -1,56 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import scalaz.syntax.applicative._ -import scalaz.{ ValidationNel, Failure => ZFailure } - -class CompositeExtractorSpec extends AnyFunSuite with Matchers { - - case class CustomConf(prop1: Int, prop2: Seq[Long]) - object CustomConf extends Configurator[CustomConf] { - override def validationNel(config: Config): ValidationNel[Throwable, CustomConf] = - config.extract[Int]("prop1") |@| config.extract[Seq[Long]]("prop2") apply CustomConf.apply - - } - - test("Extract CustomConf from path should be successful") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |cc: {prop1 : 0, prop2: [21, 22]} - """.stripMargin) - val actual = config.extract[CustomConf]("cc").get - val expected = CustomConf(0, Seq(21, 22)) - actual shouldBe expected - } - - test("Extract CustomConf from path should fail for bad values") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |cc: {prop1 : 0, prop2: [21, a]} - """.stripMargin) - val actual = config.extract[CustomConf]("cc") - actual shouldBe a[ZFailure[_]] - } - - test("Extract CustomConf with no path should be successful") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |{prop1 : 0, prop2: [1, 2]} - """.stripMargin) - val actual = config.extract[CustomConf].get - val expected = CustomConf(0, Seq(1, 2)) - actual shouldBe expected - } - - test("Extract CustomConf with no path should fail for bad values") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |{prop1 : 0, prop2: [21, a]} - """.stripMargin) - val actual = config.extract[CustomConf] - actual shouldBe a[ZFailure[_]] - } - -} diff --git a/config-z/src/test/scala/org/tupol/configz/EitherExtractorSpec.scala b/config-z/src/test/scala/org/tupol/configz/EitherExtractorSpec.scala deleted file mode 100644 index c529f67..0000000 --- a/config-z/src/test/scala/org/tupol/configz/EitherExtractorSpec.scala +++ /dev/null @@ -1,46 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.matchers.should.Matchers -import org.scalatest.funsuite.AnyFunSuite -import scalaz.syntax.applicative._ -import scalaz.{ ValidationNel, Failure => ZFailure } - -class EitherExtractorSpec extends AnyFunSuite with Matchers { - - case class CustomConf(prop1: Int, prop2: Seq[Long]) - object CustomConf extends Configurator[CustomConf] { - override def validationNel(config: Config): ValidationNel[Throwable, CustomConf] = - config.extract[Int]("prop1") |@| config.extract[Seq[Long]]("prop2") apply CustomConf.apply - } - - test("Extract Either an Int or a CustomConf returns an Int") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |either: 111 - """.stripMargin) - val actual = config.extract[Either[Int, CustomConf]]("either").get - val expected = 111 - actual shouldBe Left(expected) - } - - test("Extract Either an Int or a CustomConf returns a CustomConf") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |either: {prop1 : 0, prop2: [1, 2]} - """.stripMargin) - val actual = config.extract[Either[Int, CustomConf]]("either").get - val expected = CustomConf(0, Seq(1, 2)) - actual shouldBe Right(expected) - } - - test("Extract Either an Int or a CustomConf should fail if no match") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |either: "-incorrect_value-" - """.stripMargin) - val actual = config.extract[Either[Int, CustomConf]] - actual shouldBe a[ZFailure[_]] - } - -} diff --git a/config-z/src/test/scala/org/tupol/configz/MapExtractorSpec.scala b/config-z/src/test/scala/org/tupol/configz/MapExtractorSpec.scala deleted file mode 100644 index ac36105..0000000 --- a/config-z/src/test/scala/org/tupol/configz/MapExtractorSpec.scala +++ /dev/null @@ -1,341 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import scalaz.syntax.applicative._ -import scalaz.{ ValidationNel, Failure => ZFailure } - -class MapExtractorSpec extends AnyFunSuite with Matchers { - - case class ComplexExample( - char: Character, - str: String, - bool: Boolean, - dbl: Double, - in: Int, - lng: Long, - optChar: Option[Character], - optStr: Option[String], - optBool: Option[Boolean], - optDouble: Option[Double], - optInt: Option[Int], - optLong: Option[Long] - ) - - case class CustomConf(prop1: Int, prop2: Seq[Long]) - object CustomConf extends Configurator[CustomConf] { - override def validationNel(config: Config): ValidationNel[Throwable, CustomConf] = - config.extract[Int]("prop1") |@| config.extract[Seq[Long]]("prop2") apply CustomConf.apply - } - - test("Map[String, String] should be parsed from an object") { - - val config = ConfigFactory.parseString(""" - |property { - | key1 : 600 - | key2 : "100" - |} - """.stripMargin) - val actual: Map[String, String] = config.extract[Map[String, String]]("property").get - actual shouldEqual Map("key1" -> "600", "key2" -> "100") - } - - test("Map[String, String] should be parsed from an empty object") { - val config = ConfigFactory.parseString(""" - |property {} - """.stripMargin) - val actual: Map[String, String] = config.extract[Map[String, String]]("property").get - actual shouldEqual Map() - } - - test("Map[String, String] should be parsed from a flat object") { - val config = ConfigFactory.parseString(""" - |property.key1 = value1 - |property.key2 = value2 - """.stripMargin) - val actual: Map[String, String] = config.extract[Map[String, String]]("property").get - actual shouldEqual Map("key1" -> "value1", "key2" -> "value2") - } - - test("Map[String, String] should be parsed from a list of entries") { - val config = ConfigFactory.parseString(""" - |property :[ - | {key1 : 600}, - | {key2 : "100"} - |] - """.stripMargin) - val actual: Map[String, String] = config.extract[Map[String, String]]("property").get - - actual shouldEqual Map("key1" -> "600", "key2" -> "100") - } - - test("Map[String, String] should be parsed from an empty list of entries") { - val config = ConfigFactory.parseString(""" - |property :[] - """.stripMargin) - val actual: Map[String, String] = config.extract[Map[String, String]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Boolean] should be parsed from an object") { - val config = ConfigFactory.parseString(""" - |property { - | key1 : true - | key2 : "false" - |} - """.stripMargin) - val actual: Map[String, Boolean] = config.extract[Map[String, Boolean]]("property").get - actual shouldEqual Map("key1" -> true, "key2" -> false) - } - - test("Map[String, Boolean] should be parsed from an empty object") { - val config = ConfigFactory.parseString(""" - |property {} - """.stripMargin) - val actual: Map[String, Boolean] = config.extract[Map[String, Boolean]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Boolean] should be parsed from a flat object") { - val config = ConfigFactory.parseString(""" - |property.key1 = "true" - |property.key2 = "false" - """.stripMargin) - val actual: Map[String, Boolean] = config.extract[Map[String, Boolean]]("property").get - actual shouldEqual Map("key1" -> true, "key2" -> false) - } - - test("Map[String, Boolean] should be parsed from a list of entries") { - val config = ConfigFactory.parseString(""" - |property :[ - | {key1 : true}, - | {key2 : "false"} - |] - """.stripMargin) - val actual: Map[String, Boolean] = config.extract[Map[String, Boolean]]("property").get - actual shouldEqual Map("key1" -> true, "key2" -> false) - } - - test("Map[String, Boolean] should be parsed from an empty list of entries") { - val config = ConfigFactory.parseString(""" - |property :[] - """.stripMargin) - val actual: Map[String, Boolean] = config.extract[Map[String, Boolean]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Any] should be parsed from an object") { - val config = ConfigFactory.parseString(""" - |property { - | key1 : 600 - | key2 : "100" - |} - """.stripMargin) - val actual: Map[String, Any] = config.extract[Map[String, Any]]("property").get - actual shouldEqual Map("key1" -> 600, "key2" -> "100") - } - - test("Map[String, Any] should be parsed from an empty object") { - val config = ConfigFactory.parseString(""" - |property {} - """.stripMargin) - val actual: Map[String, Any] = config.extract[Map[String, Any]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Any] should be parsed from a flat object") { - val config = ConfigFactory.parseString(""" - |property.key1 = value1 - |property.key2 = value2 - """.stripMargin) - val actual: Map[String, Any] = config.extract[Map[String, Any]]("property").get - actual shouldEqual Map("key1" -> "value1", "key2" -> "value2") - } - - test("Map[String, Any] should be parsed from a list of entries") { - val config = ConfigFactory.parseString(""" - |property :[ - | {key1 : 600}, - | {key2 : "100"} - |] - """.stripMargin) - val actual: Map[String, Any] = config.extract[Map[String, Any]]("property").get - actual shouldEqual Map("key1" -> 600, "key2" -> "100") - } - - test("Map[String, Any] should be parsed from an empty list of entries") { - val config = ConfigFactory.parseString(""" - |property :[] - """.stripMargin) - val actual: Map[String, Any] = config.extract[Map[String, Any]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Double] should be parsed from an object") { - val config = ConfigFactory.parseString(""" - |property { - | key1 : 1.1 - | key2 : "2.2" - |} - """.stripMargin) - val actual: Map[String, Double] = config.extract[Map[String, Double]]("property").get - actual shouldEqual Map("key1" -> 1.1, "key2" -> 2.2) - } - - test("Map[String, Double] should be parsed from an empty object") { - val config = ConfigFactory.parseString(""" - |property {} - """.stripMargin) - val actual: Map[String, Double] = config.extract[Map[String, Double]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Double] should be parsed from a flat object") { - val config = ConfigFactory.parseString(""" - |property.key1 = 1.1 - |property.key2 = 2.2 - """.stripMargin) - val actual: Map[String, Double] = config.extract[Map[String, Double]]("property").get - actual shouldEqual Map("key1" -> 1.1, "key2" -> 2.2) - } - - test("Map[String, Double] should be parsed from a list of entries") { - val config = ConfigFactory.parseString(""" - |property :[ - | {key1 : 1.1}, - | {key2 : "2.2"} - |] - """.stripMargin) - val actual: Map[String, Double] = config.extract[Map[String, Double]]("property").get - actual shouldEqual Map("key1" -> 1.1, "key2" -> 2.2) - } - - test("Map[String, Double] should be parsed from an empty list of entries") { - val config = ConfigFactory.parseString(""" - |property :[] - """.stripMargin) - val actual: Map[String, Double] = config.extract[Map[String, Double]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Int] should be parsed from an object") { - val config = ConfigFactory.parseString(""" - |property { - | key1 : 1 - | key2 : "2" - |} - """.stripMargin) - val actual: Map[String, Int] = config.extract[Map[String, Int]]("property").get - actual shouldEqual Map("key1" -> 1, "key2" -> 2) - } - - test("Map[String, Int] should be parsed from an empty object") { - val config = ConfigFactory.parseString(""" - |property {} - """.stripMargin) - val actual: Map[String, Int] = config.extract[Map[String, Int]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Int] should be parsed from a flat object") { - val config = ConfigFactory.parseString(""" - |property.key1 = 1 - |property.key2 = 2 - """.stripMargin) - val actual: Map[String, Int] = config.extract[Map[String, Int]]("property").get - actual shouldEqual Map("key1" -> 1, "key2" -> 2) - } - - test("Map[String, Int] should be parsed from a list of entries") { - val config = ConfigFactory.parseString(""" - |property :[ - | {key1 : 1}, - | {key2 : "2"} - |] - """.stripMargin) - val actual: Map[String, Int] = config.extract[Map[String, Int]]("property").get - actual shouldEqual Map("key1" -> 1, "key2" -> 2) - } - - test("Map[String, Int] should be parsed from an empty list of entries") { - val config = ConfigFactory.parseString(""" - |property :[] - """.stripMargin) - val actual: Map[String, Int] = config.extract[Map[String, Int]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Long] should be parsed from an object") { - val config = ConfigFactory.parseString(""" - |property { - | key1 : 1 - | key2 : "2" - |} - """.stripMargin) - val actual: Map[String, Long] = config.extract[Map[String, Long]]("property").get - actual shouldEqual Map("key1" -> 1, "key2" -> 2) - } - - test("Map[String, Long] should be parsed from an empty object") { - val config = ConfigFactory.parseString(""" - |property {} - """.stripMargin) - val actual: Map[String, Long] = config.extract[Map[String, Long]]("property").get - actual shouldEqual Map() - } - - test("Map[String, Long] should be parsed from a flat object") { - val config = ConfigFactory.parseString(""" - |property.key1 = 1 - |property.key2 = 2 - """.stripMargin) - val actual: Map[String, Long] = config.extract[Map[String, Long]]("property").get - actual shouldEqual Map("key1" -> 1, "key2" -> 2) - } - - test("Map[String, Long] should be parsed from a list of entries") { - val config = ConfigFactory.parseString(""" - |property :[ - | {key1 : 1}, - | {key2 : "2"} - |] - """.stripMargin) - val actual: Map[String, Long] = config.extract[Map[String, Long]]("property").get - actual shouldEqual Map("key1" -> 1, "key2" -> 2) - } - - test("Map[String, Long] should be parsed from an empty list of entries") { - val config = ConfigFactory.parseString(""" - |property :[] - """.stripMargin) - val actual: Map[String, Long] = config.extract[Map[String, Long]]("property").get - actual shouldEqual Map() - } - - test("Map[String, CustomConf] should be successfully parsed from an object") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |customs { - | c1: {prop1 : 1, prop2: [11, 12]} - | c2: {prop1 : 2, prop2: [21, 22]} - |} - """.stripMargin) - val actual = config.extract[Map[String, CustomConf]]("customs").get - val expected = Map("c1" -> CustomConf(1, Seq(11, 12)), "c2" -> CustomConf(2, Seq(21, 22))) - actual should contain theSameElementsAs (expected) - } - - test("Map[String, CustomConf] should fail parsing if any of the configurations are wrong") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |customs { - | c1: {prop1 : 1, prop2: [11, 12]} - | c2: {prop1 : 2, prop2: [21, xx]} - |} - """.stripMargin) - val actual = config.extract[Map[String, CustomConf]]("customs") - actual shouldBe a[ZFailure[_]] - } - -} diff --git a/config-z/src/test/scala/org/tupol/configz/OptionExtractorSpec.scala b/config-z/src/test/scala/org/tupol/configz/OptionExtractorSpec.scala deleted file mode 100644 index 4b63749..0000000 --- a/config-z/src/test/scala/org/tupol/configz/OptionExtractorSpec.scala +++ /dev/null @@ -1,111 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.ConfigFactory -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import scalaz.syntax.applicative._ - -import scala.collection.JavaConverters._ -import scala.util.{ Success, Try } - -class OptionExtractorSpec extends AnyFunSuite with Matchers { - - case class ComplexExample( - char: Character, - str: String, - bool: Boolean, - dbl: Double, - in: Int, - lng: Long, - optChar: Option[Character], - optStr: Option[String], - optBool: Option[Boolean], - optDouble: Option[Double], - optInt: Option[Int], - optLong: Option[Long] - ) - - test("Missing optional values should not result in error") { - val complexInstance = ComplexExample('*', "string", false, 0.9d, 23, 12L, None, None, None, None, None, None) - val config = ConfigFactory.parseMap( - Map("char" -> "*", "str" -> "string", "bool" -> "false", "dbl" -> "0.9", "in" -> "23", "lng" -> "12").asJava - ) - val validationResult: Try[ComplexExample] = - config.extract[Character]("char") |@| config.extract[String]("str") |@| - config.extract[Boolean]("bool") |@| config.extract[Double]("dbl") |@| - config.extract[Int]("in") |@| config.extract[Long]("lng") |@| - config.extract[Option[Character]]("optchar") |@| config.extract[Option[String]]("optstr") |@| - config.extract[Option[Boolean]]("optbool") |@| config.extract[Option[Double]]("optdbl") |@| - config.extract[Option[Int]]("optin") |@| config.extract[Option[Long]]("optlng") apply ComplexExample.apply - - validationResult shouldEqual Success(complexInstance) - } - - test("Optional values should be parsed when present") { - val complexInstance = ComplexExample( - '*', - "string", - false, - 0.9d, - 23, - 12L, - Some('*'), - Some("string"), - Some(false), - Some(0.9d), - Some(23), - Some(12L) - ) - val config = ConfigFactory.parseMap( - Map( - "char" -> "*", - "str" -> "string", - "bool" -> "false", - "dbl" -> "0.9", - "in" -> "23", - "lng" -> "12", - "optchar" -> "*", - "optstr" -> "string", - "optbool" -> "false", - "optdbl" -> "0.9", - "optin" -> "23", - "optlng" -> "12" - ).asJava - ) - val validationResult: Try[ComplexExample] = - config.extract[Character]("char") |@| config.extract[String]("str") |@| config - .extract[Boolean]("bool") |@| config.extract[Double]("dbl") |@| - config.extract[Int]("in") |@| config.extract[Long]("lng") |@| config.extract[Option[Character]]( - "optchar") |@| config - .extract[Option[String]]("optstr") |@| - config.extract[Option[Boolean]]("optbool") |@| config.extract[Option[Double]]("optdbl") |@| config - .extract[Option[Int]]("optin") |@| config.extract[Option[Long]]("optlng") apply ComplexExample.apply - - validationResult shouldEqual Success(complexInstance) - } - - case class SimpleExample(char: Character, bool: Boolean, in: Int) - - test("Missing non optional values should result in a Failure") { - val config = ConfigFactory.parseMap(Map("char" -> "*", "in" -> "23").asJava) - val validationResult: Try[SimpleExample] = - config.extract[Character]("char") |@| config.extract[Boolean]("bool") |@| config - .extract[Int]("in") apply SimpleExample.apply - - validationResult.failed.map(_.getMessage).get should include("No configuration setting found for key 'bool'") - } - - test("Non optional values of the wrong type should result in a Failure") { - val config = ConfigFactory.parseMap(Map("char" -> "*", "bool" -> "nee", "in" -> "234,34").asJava) - val validationResult: Try[SimpleExample] = - config.extract[Character]("char") |@| config.extract[Boolean]("bool") |@| config - .extract[Int]("in") apply SimpleExample.apply - validationResult.failed.map(_.getMessage).get should include( - "hardcoded value: bool has type STRING rather than BOOLEAN" - ) - validationResult.failed.map(_.getMessage).get should include( - "hardcoded value: in has type STRING rather than NUMBER" - ) - } - -} diff --git a/config-z/src/test/scala/org/tupol/configz/PrimitiveExtractorSpec.scala b/config-z/src/test/scala/org/tupol/configz/PrimitiveExtractorSpec.scala deleted file mode 100644 index c305142..0000000 --- a/config-z/src/test/scala/org/tupol/configz/PrimitiveExtractorSpec.scala +++ /dev/null @@ -1,196 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.ConfigFactory -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import scalaz.{ Failure => ZFailure } - -import java.sql.{ Date, Timestamp } -import java.time.{ Duration, LocalDate, LocalDateTime } - -class PrimitiveExtractorSpec extends AnyFunSuite with Matchers { - - case class ComplexExample( - char: Character, - str: String, - bool: Boolean, - dbl: Double, - in: Int, - lng: Long, - optChar: Option[Character], - optStr: Option[String], - optBool: Option[Boolean], - optDouble: Option[Double], - optInt: Option[Int], - optLong: Option[Long] - ) - - test("Extracting a String") { - val config = ConfigFactory.parseString(""" - |property: "10h" - """.stripMargin) - val actual = config.extract[String]("property").get - actual shouldEqual "10h" - } - - test("Extracting a true Boolean") { - val config = ConfigFactory.parseString(""" - |property: "true" - """.stripMargin) - val actual = config.extract[Boolean]("property").get - actual shouldEqual true - } - - test("Extracting a false Boolean") { - val config = ConfigFactory.parseString(""" - |property: false - """.stripMargin) - val actual = config.extract[Boolean]("property").get - actual shouldEqual false - } - - test("Extracting an unknown Boolean fails") { - val config = ConfigFactory.parseString(""" - |property: whaaat - """.stripMargin) - val actual = config.extract[Boolean]("property") - actual shouldBe a[ZFailure[_]] - } - - test("Extracting an Int") { - val config = ConfigFactory.parseString(""" - |property: 123123123 - """.stripMargin) - val actual = config.extract[Int]("property").get - actual shouldEqual 123123123 - } - - test("Extracting an Int fails") { - val config = ConfigFactory.parseString(""" - |property: whaaat - """.stripMargin) - val actual = config.extract[Int]("property") - actual shouldBe a[ZFailure[_]] - } - - test("Extracting a Long") { - val config = ConfigFactory.parseString(""" - |property: 123123123 - """.stripMargin) - val actual = config.extract[Long]("property").get - actual shouldEqual 123123123L - } - - test("Extracting a Long fails") { - val config = ConfigFactory.parseString(""" - |property: whaaat - """.stripMargin) - val actual = config.extract[Long]("property") - actual shouldBe a[ZFailure[_]] - } - - test("Extracting a Double") { - val config = ConfigFactory.parseString(""" - |property: 123123123 - """.stripMargin) - val actual = config.extract[Double]("property").get - actual shouldEqual 123123123L - } - - test("Extracting a Double fails") { - val config = ConfigFactory.parseString(""" - |property: whaaat - """.stripMargin) - val actual = config.extract[Double]("property") - actual shouldBe a[ZFailure[_]] - } - - test("Extracting a Duration") { - val config = ConfigFactory.parseString(""" - |property: "10h" - """.stripMargin) - val actual = config.extract[Duration]("property").get - actual shouldEqual Duration.ofHours(10) - } - - test("Extracting a Duration fails") { - val config = ConfigFactory.parseString(""" - |property: "x" - """.stripMargin) - val actual = config.extract[Date]("property") - actual shouldBe a[ZFailure[_]] - } - - test("Extracting a Date") { - val config = ConfigFactory.parseString(""" - |property: "2018-12-23" - """.stripMargin) - val actual = config.extract[Date]("property").get - actual shouldEqual Date.valueOf("2018-12-23") - } - - test("Extracting a Date fails") { - val config = ConfigFactory.parseString(""" - |property: "2018-12-44" - """.stripMargin) - val actual = config.extract[Date]("property") - actual shouldBe a[ZFailure[_]] - } - - test("Extracting a Timestamp with millis") { - val config = ConfigFactory.parseString(""" - |property: "2018-12-23 10:23:59.001" - """.stripMargin) - val actual = config.extract[Timestamp]("property").get - actual shouldEqual Timestamp.valueOf("2018-12-23 10:23:59.001") - } - - test("Extracting a Timestamp no millis") { - val config = ConfigFactory.parseString(""" - |property: "2018-12-23 10:23:59" - """.stripMargin) - val actual = config.extract[Timestamp]("property").get - actual shouldEqual Timestamp.valueOf("2018-12-23 10:23:59") - } - - test("Extracting a Timestamp fails") { - val config = ConfigFactory.parseString(""" - |property: "abc" - """.stripMargin) - val actual = config.extract[Timestamp]("property") - actual shouldBe a[ZFailure[_]] - } - - test("Extracting a LocalDate") { - val config = ConfigFactory.parseString(""" - |property: "2018-12-23" - """.stripMargin) - val actual = config.extract[LocalDate]("property").get - actual shouldEqual LocalDate.of(2018, 12, 23) - } - - test("Extracting a LocalDate fails") { - val config = ConfigFactory.parseString(""" - |property: "2018-12-44" - """.stripMargin) - val actual = config.extract[LocalDate]("property") - actual shouldBe a[ZFailure[_]] - } - - test("Extracting a LocalDateTime") { - val config = ConfigFactory.parseString(""" - |property: "2018-12-23T10:23:59" - """.stripMargin) - val actual = config.extract[LocalDateTime]("property").get - actual shouldEqual LocalDateTime.of(2018, 12, 23, 10, 23, 59) - } - - test("Extracting a LocalDateTime fails") { - val config = ConfigFactory.parseString(""" - |property: "2018-12-44 10:23:59" - """.stripMargin) - val actual = config.extract[LocalDateTime]("property") - actual shouldBe a[ZFailure[_]] - } - -} diff --git a/config-z/src/test/scala/org/tupol/configz/RichConfigSpec.scala b/config-z/src/test/scala/org/tupol/configz/RichConfigSpec.scala deleted file mode 100644 index 9e9d865..0000000 --- a/config-z/src/test/scala/org/tupol/configz/RichConfigSpec.scala +++ /dev/null @@ -1,25 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.ConfigFactory -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import scalaz.{ Failure => ZFailure, Success => ZSuccess } - -class RichConfigSpec extends AnyFunSuite with Matchers { - - test("validatePath should succeed if the path exists") { - val path = "prop" - val config = ConfigFactory.parseString(s""" $path: "value" """) - val result = new RichConfig(config).validatePath(path) - result shouldBe ZSuccess(path) - } - - test("validatePath should fail if the path does not exist") { - val path = "prop" - val badPath = s"bad-$path-bad-$path-bad" - val config = ConfigFactory.parseString(s""" $path: "value" """) - val result = new RichConfig(config).validatePath(badPath) - result shouldBe a[ZFailure[_]] - } - -} diff --git a/config-z/src/test/scala/org/tupol/configz/SeqExtractorSpec.scala b/config-z/src/test/scala/org/tupol/configz/SeqExtractorSpec.scala deleted file mode 100644 index 522ff9b..0000000 --- a/config-z/src/test/scala/org/tupol/configz/SeqExtractorSpec.scala +++ /dev/null @@ -1,138 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import scalaz.syntax.applicative._ -import scalaz.{ ValidationNel, Failure => ZFailure } - -class SeqExtractorSpec extends AnyFunSuite with Matchers { - - case class CustomConf(prop1: Int, prop2: Seq[Long]) - object CustomConf extends Configurator[CustomConf] { - override def validationNel(config: Config): ValidationNel[Throwable, CustomConf] = - config.extract[Int]("prop1") |@| config.extract[Seq[Long]]("prop2") apply CustomConf.apply - } - - test("Extract a Seq[Any] should be successful") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ 1, 2.3, true, false, text ] - """.stripMargin) - val actual = config.extract[Seq[Any]]("props").get - val expected = Seq(1, 2.3, true, false, "text") - actual should contain theSameElementsAs (expected) - } - - test("Extract a Seq[String] should be successful") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ abc, cde ] - """.stripMargin) - val actual = config.extract[Seq[String]]("props").get - val expected = Seq("abc", "cde") - actual should contain theSameElementsAs (expected) - } - - test("Extract a Seq[Boolean] should be successful") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ true, false, true ] - """.stripMargin) - val actual = config.extract[Seq[Boolean]]("props").get - val expected = Seq(true, false, true) - actual should contain theSameElementsAs (expected) - } - - test("Extract a Seq[Boolean] should fail if any element is not a Boolean") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ true, false, true, yummy ] - """.stripMargin) - val actual = config.extract[Seq[Boolean]]("props") - actual shouldBe a[ZFailure[_]] - } - - test("Extract a Seq[Int] should be successful") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ 1, 2 ] - """.stripMargin) - val actual = config.extract[Seq[Int]]("props").get - val expected = Seq(1, 2) - actual should contain theSameElementsAs (expected) - } - - test("Extract a Seq[Int] should fail if any element is not an Int") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ 1, a ] - """.stripMargin) - val actual = config.extract[Seq[Int]]("props") - actual shouldBe a[ZFailure[_]] - } - - test("Extract a Seq[Long] should be successful") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ 1, 2 ] - """.stripMargin) - val actual = config.extract[Seq[Long]]("props").get - val expected = Seq(1L, 2L) - actual should contain theSameElementsAs (expected) - } - - test("Extract a Seq[Long] should fail if any element is not a Long") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ 1, a ] - """.stripMargin) - val actual = config.extract[Seq[Long]]("props") - actual shouldBe a[ZFailure[_]] - } - - test("Extract a Seq[Double] should be successful") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ 1, 2.3 ] - """.stripMargin) - val actual = config.extract[Seq[Double]]("props").get - val expected = Seq(1.0, 2.3) - actual should contain theSameElementsAs (expected) - } - - test("Extract a Seq[Double] should fail if any element is not a Double") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |props :[ 1, 2.3, a ] - """.stripMargin) - val actual = config.extract[Seq[Double]]("props") - actual shouldBe a[ZFailure[_]] - } - - test("Extract a Seq[CustomConf] should be successful") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |customs :[ - | {prop1 : 1, prop2: [11, 12]}, - | {prop1 : 2, prop2: [21, 22]} - |] - """.stripMargin) - val actual = config.extract[Seq[CustomConf]]("customs").get - val expected = Seq(CustomConf(1, Seq(11, 12)), CustomConf(2, Seq(21, 22))) - actual should contain theSameElementsAs (expected) - } - - test("Extract a Seq[CustomConf] should fail for bad values") { - implicit val customConfExtractor = CustomConf - val config = ConfigFactory.parseString(""" - |customs :[ - | {prop1 : 1, prop2: [11, 12]}, - | {prop1 : c, prop2: [21, 22]} - |] - """.stripMargin) - val actual = config.extract[Seq[CustomConf]]("customs") - actual shouldBe a[ZFailure[_]] - } - -} diff --git a/config-z/src/test/scala/org/tupol/configz/StringToRangeExtractorSpec.scala b/config-z/src/test/scala/org/tupol/configz/StringToRangeExtractorSpec.scala deleted file mode 100644 index 3ba7e29..0000000 --- a/config-z/src/test/scala/org/tupol/configz/StringToRangeExtractorSpec.scala +++ /dev/null @@ -1,25 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.ConfigFactory -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class StringToRangeExtractorSpec extends AnyFunSuite with Matchers { - - test("Extract a Range of a single value should be successful") { - val config = ConfigFactory.parseString(""" - |range: "1" - """.stripMargin) - val actual = config.extract[Range]("range").get - actual shouldBe Seq(1) - } - - test("Extract a Range of multiple values should be successful") { - val config = ConfigFactory.parseString(""" - |range: "1, 3, 1" - """.stripMargin) - val actual = config.extract[Range]("range").get - actual shouldBe Seq(1, 2, 3) - } - -} diff --git a/config-z/src/test/scala/org/tupol/configz/StringToRangeProp.scala b/config-z/src/test/scala/org/tupol/configz/StringToRangeProp.scala deleted file mode 100644 index e0a5962..0000000 --- a/config-z/src/test/scala/org/tupol/configz/StringToRangeProp.scala +++ /dev/null @@ -1,56 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.ConfigException.BadValue -import org.scalacheck.Prop.forAll -import org.scalacheck.{ Gen, Properties } -import org.scalatest.matchers.should.Matchers -import org.tupol.configz.Extractor.rangeExtractor.parseStringToRange - -class StringToRangeProp extends Properties("Range") with Matchers { - - val positiveInts = for (n <- Gen.choose(0, 100)) yield n - val negativeInts = for (n <- Gen.choose(-100, -1)) yield n - - property("commons#parseStringToRange - creates sequence for a single int value") = forAll(positiveInts) { (i: Int) => - val input = s"$i" - val output = parseStringToRange(input, "somePath").get - - output.head == i - output.last == i - } - - property("commons#parseStringToRange - creates sequence for a fully defined range") = forAll(positiveInts) { - (i: Int) => - val input = s"${i}, ${i + 10}, 1" - val output = parseStringToRange(input, "somePath").get - output.head == i - output.last == i + 10 - } - - property("commons#parseStringToRange - throws BadValue for start values greater than stop values") = - forAll(positiveInts) { (i: Int) => - val input = s"${i + 1}, $i, 1" - val ex = intercept[BadValue] { - parseStringToRange(input, "somePath").get - } - ex.isInstanceOf[BadValue] - } - - property("commons#parseStringToRange - throws BadValue for negative steps") = forAll(positiveInts, negativeInts) { - (i: Int, s: Int) => - val input = s"$i, ${i + 1}, $s" - val ex = intercept[BadValue] { - parseStringToRange(input, "somePath").get - } - ex.isInstanceOf[BadValue] - } - - property("commons#parseStringToRange - throws BadValue for any string") = forAll { (input: String) => - val badInput = s"_;@#* $input;@#*&" - val ex = intercept[BadValue] { - parseStringToRange(input, "somePath").get - } - ex.isInstanceOf[BadValue] - } - -} diff --git a/config-z/src/test/scala/org/tupol/configz/StringToRangeSpec.scala b/config-z/src/test/scala/org/tupol/configz/StringToRangeSpec.scala deleted file mode 100644 index eb2297a..0000000 --- a/config-z/src/test/scala/org/tupol/configz/StringToRangeSpec.scala +++ /dev/null @@ -1,54 +0,0 @@ -package org.tupol.configz - -import com.typesafe.config.ConfigException.BadValue -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import org.tupol.configz.Extractor.rangeExtractor.parseStringToRange - -class StringToRangeSpec extends AnyFunSuite with Matchers { - - test("commons#parseStringToRange - creates sequence for a single int value") { - val i = 1 - val input = s"$i" - val output = parseStringToRange(input, "somePath").get - - output.head shouldBe i - output.last shouldBe i - } - - test("commons#parseStringToRange - creates full sequence") { - val i = 1 - val input = s"${i}, ${i + 10}, 1" - val output = parseStringToRange(input, "somePath").get - - output.head shouldBe i - output.last shouldBe i + 10 - } - - test("commons#parseStringToRange - throws BadValue for start values greater than stop values") { - val i = 1 - val input = s"${i + 1}, $i, 1" - val ex = intercept[BadValue] { - parseStringToRange(input, "somePath").get - } - ex.isInstanceOf[BadValue] - } - - test("commons#parseStringToRange - throws BadValue for negative steps") { - val i = 1 - val input = s"$i, ${i + 1}, -1" - val ex = intercept[BadValue] { - parseStringToRange(input, "somePath").get - } - ex.isInstanceOf[BadValue] - } - - test("commons#parseStringToRange - throws BadValue for any malformed string") { - val input = "sdfsdf" - val ex = intercept[BadValue] { - parseStringToRange(input, "somePath").get - } - ex.isInstanceOf[BadValue] - } - -} diff --git a/core/src/main/scala/org/tupol/utils/CollectionOps.scala b/core/src/main/scala/org/tupol/utils/CollectionOps.scala index b4e8d6b..8614a54 100644 --- a/core/src/main/scala/org/tupol/utils/CollectionOps.scala +++ b/core/src/main/scala/org/tupol/utils/CollectionOps.scala @@ -48,12 +48,6 @@ object CollectionOps { def toOptionNel: Option[Set[T]] = if (xs.isEmpty) None else Some(xs) } - implicit class TraversableOps[T](xs: Traversable[T]) { - - /** Convert a traversable into an option of None if empty or Some of elements; Nel: Non-Empty List */ - def toOptionNel: Option[Traversable[T]] = if (xs.isEmpty) None else Some(xs) - } - implicit class VectorOps[T](xs: Vector[T]) { /** Convert a vector into an option of None if empty or Some of elements; Nel: Non-Empty List */ diff --git a/core/src/main/scala/org/tupol/utils/EitherOps.scala b/core/src/main/scala/org/tupol/utils/EitherOps.scala index b40cd06..dfe4545 100644 --- a/core/src/main/scala/org/tupol/utils/EitherOps.scala +++ b/core/src/main/scala/org/tupol/utils/EitherOps.scala @@ -26,20 +26,20 @@ package org.tupol.utils /** Implicit decorator functions for `Either[A, B]` and `F[Either[A, B]]` */ object EitherOps { - /** Simple decorator for the `Traversable[Either[A, B]]` */ - implicit class TraversableEitherOps[A, B](val eithers: Traversable[Either[A, B]]) { - def flattenEithers: Either[A, Traversable[B]] = EitherUtils.flatten(eithers) - def separate: (Traversable[A], Traversable[B]) = EitherUtils.separate(eithers) - def lefts: Traversable[A] = EitherUtils.lefts(eithers) - def rights: Traversable[B] = EitherUtils.rights(eithers) + /** Simple decorator for the `Iterable[Either[A, B]]` */ + implicit class IterableEitherOps[A, B](val eithers: Iterable[Either[A, B]]) { + def flattenEithers: Either[A, Iterable[B]] = EitherUtils.flatten(eithers) + def separate: (Iterable[A], Iterable[B]) = EitherUtils.separate(eithers) + def lefts: Iterable[A] = EitherUtils.lefts(eithers) + def rights: Iterable[B] = EitherUtils.rights(eithers) } /** Simple decorator for the `Array[Either[A, B]]` */ implicit class ArrayEitherOps[A, B](val eithers: Array[Either[A, B]]) { - def flattenEithers: Either[A, Traversable[B]] = EitherUtils.flatten(eithers) - def separate: (Traversable[A], Traversable[B]) = EitherUtils.separate(eithers) - def lefts: Traversable[A] = EitherUtils.lefts(eithers) - def rights: Traversable[B] = EitherUtils.rights(eithers) + def flattenEithers: Either[A, Iterable[B]] = EitherUtils.flatten(eithers) + def separate: (Iterable[A], Iterable[B]) = EitherUtils.separate(eithers) + def lefts: Iterable[A] = EitherUtils.lefts(eithers) + def rights: Iterable[B] = EitherUtils.rights(eithers) } implicit class EitherOps[L, R](either: Either[L, R]) { diff --git a/core/src/main/scala/org/tupol/utils/EitherUtils.scala b/core/src/main/scala/org/tupol/utils/EitherUtils.scala index d9d13db..4e798bf 100644 --- a/core/src/main/scala/org/tupol/utils/EitherUtils.scala +++ b/core/src/main/scala/org/tupol/utils/EitherUtils.scala @@ -27,14 +27,14 @@ package org.tupol.utils object EitherUtils { /** The last Left prevails (if any), otherwise all Right-s collected in order of occurrence (possibly empty). */ - def flatten[A, B](eithers: Traversable[Either[A, B]]): Either[A, Traversable[B]] = { + def flatten[A, B](eithers: Iterable[Either[A, B]]): Either[A, Iterable[B]] = { val (lefts, rights) = separate(eithers) if (lefts.isEmpty) Right(rights) else Left(lefts.last) } /** Partitions a sequence of Either-s into their left and right elements, similar to Haskell's `partitionEithers` */ - def separate[A, B](eithers: Traversable[Either[A, B]]): (Traversable[A], Traversable[B]) = { + def separate[A, B](eithers: Iterable[Either[A, B]]): (Iterable[A], Iterable[B]) = { import scala.collection.immutable.Vector eithers.foldLeft((Vector.empty[A], Vector.empty[B]))((acc, x) => x match { @@ -44,11 +44,11 @@ object EitherUtils { } /** All left elements from a sequence of Either-s */ - def lefts[A, B](eithers: Traversable[Either[A, B]]): Traversable[A] = + def lefts[A, B](eithers: Iterable[Either[A, B]]): Iterable[A] = eithers collect { case Left(l) => l } /** All right elements from a sequence of Either-s */ - def rights[A, B](eithers: Traversable[Either[A, B]]): Traversable[B] = + def rights[A, B](eithers: Iterable[Either[A, B]]): Iterable[B] = eithers collect { case Right(r) => r } } diff --git a/core/src/main/scala/org/tupol/utils/FutureOps.scala b/core/src/main/scala/org/tupol/utils/FutureOps.scala index 42e4b31..77b8460 100644 --- a/core/src/main/scala/org/tupol/utils/FutureOps.scala +++ b/core/src/main/scala/org/tupol/utils/FutureOps.scala @@ -87,14 +87,14 @@ object FutureOps { future.transform((success: T) => success, (failure: Throwable) => map(failure)) } - /** Simple decorator for the `Traversable[Future[_]]` */ - implicit class TraversableFuturesOps[T](val trys: Traversable[Future[T]]) { + /** Simple decorator for the `Iterable[Future[_]]` */ + implicit class IterableFuturesOps[T](val trys: Iterable[Future[T]]) { /** - * Flatten a `Traversable[Future[T]]` to a `Future[Traversable[T]]`, which is a failure if any if the Futures is a failure. + * Flatten a `Iterable[Future[T]]` to a `Future[Iterable[T]]`, which is a failure if any if the Futures is a failure. * In case of a failure, the latest `Failure` will be returned */ - def allOkOrFail(implicit ec: ExecutionContext): Future[Traversable[T]] = FutureUtils.allOkOrFail(trys) + def allOkOrFail(implicit ec: ExecutionContext): Future[Iterable[T]] = FutureUtils.allOkOrFail(trys) } } diff --git a/core/src/main/scala/org/tupol/utils/FutureUtils.scala b/core/src/main/scala/org/tupol/utils/FutureUtils.scala index cdf7479..968b3c6 100644 --- a/core/src/main/scala/org/tupol/utils/FutureUtils.scala +++ b/core/src/main/scala/org/tupol/utils/FutureUtils.scala @@ -1,3 +1,26 @@ +/* +MIT License + +Copyright (c) 2018 Tupol (github.com/tupol) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ package org.tupol.utils import scala.concurrent.{ ExecutionContext, Future } @@ -6,10 +29,10 @@ import scala.concurrent.{ ExecutionContext, Future } object FutureUtils { /** - * Flatten a `Traversable[Future[T]]` to a `Future[Traversable[T]]`, which is a failure if ANY of the Futures is a failure. + * Flatten a `Iterable[Future[T]]` to a `Future[Iterable[T]]`, which is a failure if ANY of the Futures is a failure. * In case of a failure, the latest one will be returned. */ - def allOkOrFail[T](futures: Traversable[Future[T]])(implicit ec: ExecutionContext): Future[Traversable[T]] = { + def allOkOrFail[T](futures: Iterable[Future[T]])(implicit ec: ExecutionContext): Future[Iterable[T]] = { import scala.collection.immutable.Vector futures.foldLeft(Future.successful(Vector.empty[T])) { (acc, future) => future.flatMap(tf => acc.map(_ :+ tf)) diff --git a/core/src/main/scala/org/tupol/utils/TryOps.scala b/core/src/main/scala/org/tupol/utils/TryOps.scala index 3b679e4..a58fade 100644 --- a/core/src/main/scala/org/tupol/utils/TryOps.scala +++ b/core/src/main/scala/org/tupol/utils/TryOps.scala @@ -93,17 +93,17 @@ object TryOps { } - /** Simple decorator for the `Traversable[Try[_]]` */ - implicit class TraversableTryOps[T](val trys: Traversable[Try[T]]) { + /** Simple decorator for the `Iterable[Try[_]]` */ + implicit class IterableTryOps[T](val trys: Iterable[Try[T]]) { /** - * Flatten a `Traversable[Try[T]]` to a `Try[Traversable[T]]`, which is a failure if any if the Trys is a failure. + * Flatten a `Iterable[Try[T]]` to a `Try[Iterable[T]]`, which is a failure if any if the Trys is a failure. * In case of a failure, the latest `Failure` will be returned */ - def allOkOrFail: Try[Traversable[T]] = TryUtils.allOkOrFail(trys) + def allOkOrFail: Try[Iterable[T]] = TryUtils.allOkOrFail(trys) - /** Flatten a `Traversable[Try[T]]` to a `Try[Traversable[T]]`, which is a failure if ALL of the Trys is a failure. */ - def collectOkOrFail: Try[Traversable[T]] = TryUtils.collectOkOrFail(trys) + /** Flatten a `Iterable[Try[T]]` to a `Try[Iterable[T]]`, which is a failure if ALL of the Trys is a failure. */ + def collectOkOrFail: Try[Iterable[T]] = TryUtils.collectOkOrFail(trys) } @@ -114,9 +114,9 @@ object TryOps { * Flatten a `Array[Try[T]]` to a `Try[Array[T]]`, which is a failure if any if the Trys is a failure. * In case of a failure, the latest `Failure` will be returned */ - def allOkOrFail: Try[Traversable[T]] = TryUtils.allOkOrFail(trys) + def allOkOrFail: Try[Iterable[T]] = TryUtils.allOkOrFail(trys) - /** Flatten a `Traversable[Try[T]]` to a `Try[Traversable[T]]`, which is a failure if ALL of the Trys is a failure. */ - def collectOkOrFail: Try[Traversable[T]] = TryUtils.collectOkOrFail(trys) + /** Flatten a `Iterable[Try[T]]` to a `Try[Iterable[T]]`, which is a failure if ALL of the Trys is a failure. */ + def collectOkOrFail: Try[Iterable[T]] = TryUtils.collectOkOrFail(trys) } } diff --git a/core/src/main/scala/org/tupol/utils/TryUtils.scala b/core/src/main/scala/org/tupol/utils/TryUtils.scala index a0f815c..1cbe260 100644 --- a/core/src/main/scala/org/tupol/utils/TryUtils.scala +++ b/core/src/main/scala/org/tupol/utils/TryUtils.scala @@ -29,18 +29,18 @@ import scala.util.{ Failure, Success, Try } object TryUtils { /** - * Flatten a `Traversable[Try[T]]` to a `Try[Traversable[T]]`, which is a failure if ANY of the Trys is a failure. + * Flatten a `Iterable[Try[T]]` to a `Try[Iterable[T]]`, which is a failure if ANY of the Trys is a failure. * In case of a failure, the latest `Failure` will be returned */ - def allOkOrFail[T](trys: Traversable[Try[T]]): Try[Traversable[T]] = { + def allOkOrFail[T](trys: Iterable[Try[T]]): Try[Iterable[T]] = { import scala.collection.immutable.Vector trys.foldLeft(Try(Vector.empty[T])) { (acc, tryField) => tryField.flatMap(tf => acc.map(_ :+ tf)) } } - /** Flatten a `Traversable[Try[T]]` to a `Try[Traversable[T]]`, which is a failure if ALL of the Trys is a failure. */ - def collectOkOrFail[T](trys: Traversable[Try[T]]): Try[Traversable[T]] = + /** Flatten a `Iterable[Try[T]]` to a `Try[Iterable[T]]`, which is a failure if ALL of the Trys is a failure. */ + def collectOkOrFail[T](trys: Iterable[Try[T]]): Try[Iterable[T]] = trys match { case Nil => Success(Nil) case _ => diff --git a/core/src/main/scala/org/tupol/utils/implicits.scala b/core/src/main/scala/org/tupol/utils/implicits.scala index f3da343..c988938 100644 --- a/core/src/main/scala/org/tupol/utils/implicits.scala +++ b/core/src/main/scala/org/tupol/utils/implicits.scala @@ -29,14 +29,13 @@ import scala.util.Try /** Package wide implicits */ object implicits { - implicit class TryOpsImplicits[T](override val attempt: Try[T]) extends TryOps.TryOps[T](attempt) - implicit class TraversableTryOpsImplicits[T](override val trys: Traversable[Try[T]]) - extends TryOps.TraversableTryOps[T](trys) - implicit class ArrayTryOpsImplicits[T](override val trys: Array[Try[T]]) extends TryOps.ArrayTryOps[T](trys) + implicit class TryOpsImplicits[T](override val attempt: Try[T]) extends TryOps.TryOps[T](attempt) + implicit class IterableTryOpsImplicits[T](override val trys: Iterable[Try[T]]) extends TryOps.IterableTryOps[T](trys) + implicit class ArrayTryOpsImplicits[T](override val trys: Array[Try[T]]) extends TryOps.ArrayTryOps[T](trys) implicit class EitherOpsImplicits[L, R](either: Either[L, R]) extends EitherOps.EitherOps[L, R](either) - implicit class TraversableEitherOpsImplicits[A, B](override val eithers: Traversable[Either[A, B]]) - extends EitherOps.TraversableEitherOps(eithers) + implicit class IterableEitherOpsImplicits[A, B](override val eithers: Iterable[Either[A, B]]) + extends EitherOps.IterableEitherOps(eithers) implicit class ArrayEitherOpsImplicits[A, B](override val eithers: Array[Either[A, B]]) extends EitherOps.ArrayEitherOps(eithers) diff --git a/core/src/main/scala/org/tupol/utils/io/TextReader.scala b/core/src/main/scala/org/tupol/utils/io/TextReader.scala index a33c383..4c44963 100644 --- a/core/src/main/scala/org/tupol/utils/io/TextReader.scala +++ b/core/src/main/scala/org/tupol/utils/io/TextReader.scala @@ -37,7 +37,7 @@ trait TextReader extends TextSource { } final case class TextReaderException(private val message: String) extends Exception(message) { - def this(message: String, cause: Throwable) { + def this(message: String, cause: Throwable) = { this(message) initCause(cause) } diff --git a/core/src/main/scala/org/tupol/utils/io/TextSource.scala b/core/src/main/scala/org/tupol/utils/io/TextSource.scala index 101e87f..eaf6947 100644 --- a/core/src/main/scala/org/tupol/utils/io/TextSource.scala +++ b/core/src/main/scala/org/tupol/utils/io/TextSource.scala @@ -34,7 +34,7 @@ trait TextSource { } final case class TextSourceException(private val message: String) extends Exception(message) { - def this(message: String, cause: Throwable) { + def this(message: String, cause: Throwable) = { this(message) initCause(cause) } diff --git a/core/src/test/scala/org/tupol/utils/EitherTraversableSpec.scala b/core/src/test/scala/org/tupol/utils/EitherTraversableSpec.scala index 6297a56..483ca1b 100644 --- a/core/src/test/scala/org/tupol/utils/EitherTraversableSpec.scala +++ b/core/src/test/scala/org/tupol/utils/EitherTraversableSpec.scala @@ -3,7 +3,7 @@ package org.tupol.utils import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class EitherTraversableSpec extends AnyWordSpec with Matchers { +class EitherIterableSpec extends AnyWordSpec with Matchers { import org.tupol.utils.EitherUtils._ import org.tupol.utils.implicits._ diff --git a/core/src/test/scala/org/tupol/utils/FutureOpsSpec.scala b/core/src/test/scala/org/tupol/utils/FutureOpsSpec.scala index dcbbf68..fd655b1 100644 --- a/core/src/test/scala/org/tupol/utils/FutureOpsSpec.scala +++ b/core/src/test/scala/org/tupol/utils/FutureOpsSpec.scala @@ -3,16 +3,16 @@ package org.tupol.utils import org.scalatest.concurrent.ScalaFutures import org.scalatest.matchers.should.Matchers import org.scalatest.funsuite.AnyFunSuite -import org.tupol.utils.FutureOps.TraversableFuturesOps +import org.tupol.utils.FutureOps.IterableFuturesOps import scala.collection.mutable.ArrayBuffer -import scala.concurrent.Future +import scala.concurrent.{ ExecutionContextExecutor, Future } class FutureOpsSpec extends AnyFunSuite with Matchers with ScalaFutures { import org.tupol.utils.implicits._ - implicit val ec = scala.concurrent.ExecutionContext.global + implicit val ec: ExecutionContextExecutor = scala.concurrent.ExecutionContext.global test("Success future.logSuccess") { val logger = ArrayBuffer[Int]() diff --git a/core/src/test/scala/org/tupol/utils/ToOptionNelSpec.scala b/core/src/test/scala/org/tupol/utils/ToOptionNelSpec.scala index 656e34c..77b14ee 100644 --- a/core/src/test/scala/org/tupol/utils/ToOptionNelSpec.scala +++ b/core/src/test/scala/org/tupol/utils/ToOptionNelSpec.scala @@ -43,23 +43,11 @@ class ToOptionNelSpec extends AnyWordSpec with Matchers { } } - "Traversable.toOptionNel" should { - "return None for an empty traversable" in { - Traversable().toOptionNel shouldBe None - } - "return Some list for a non-empty traversable" in { - val col = Traversable(1, 5, 2) - val result = col.toOptionNel - result shouldBe a[Some[_]] - result.get should contain theSameElementsAs col - } - } - "Vector.toOptionNel" should { - "return None for an empty traversable" in { + "return None for an empty Iterable" in { Vector().toOptionNel shouldBe None } - "return Some list for a non-empty traversable" in { + "return Some list for a non-empty Iterable" in { val col = Vector(1, 5, 2) val result = col.toOptionNel result shouldBe a[Some[_]] @@ -68,10 +56,10 @@ class ToOptionNelSpec extends AnyWordSpec with Matchers { } "Iterable.toOptionNel" should { - "return None for an empty traversable" in { + "return None for an empty Iterable" in { Iterable().toOptionNel shouldBe None } - "return Some list for a non-empty traversable" in { + "return Some list for a non-empty Iterable" in { val col = Iterable(1, 5, 2) val result = col.toOptionNel result shouldBe a[Some[_]] @@ -80,10 +68,10 @@ class ToOptionNelSpec extends AnyWordSpec with Matchers { } "Iterator.toOptionNel" should { - "return None for an empty traversable" in { + "return None for an empty Iterable" in { Iterator().toOptionNel shouldBe None } - "return Some list for a non-empty traversable" in { + "return Some list for a non-empty Iterable" in { val col = Seq(1, 5, 2) val result = col.toIterator.toOptionNel result shouldBe a[Some[_]] diff --git a/jdbc/src/test/scala/org/tupol/utils/jdbc/RowExtractorsSpec.scala b/jdbc/src/test/scala/org/tupol/utils/jdbc/RowExtractorsSpec.scala index a59ccfe..29078da 100644 --- a/jdbc/src/test/scala/org/tupol/utils/jdbc/RowExtractorsSpec.scala +++ b/jdbc/src/test/scala/org/tupol/utils/jdbc/RowExtractorsSpec.scala @@ -21,7 +21,7 @@ class RowExtractorsSpec extends AnyWordSpec with Matchers with IdiomaticMockito val someClassReference = SomeClass("other random string", Int.MaxValue, UUID.randomUUID()) val otherClassReference = OtherClass(Int.MaxValue, "some random string") - implicit val someClassExtractor = new RowExtractor[SomeClass] { + implicit val someClassExtractor: RowExtractor[SomeClass] = new RowExtractor[SomeClass] { def extract(rs: ResultSet): Try[SomeClass] = for { col1 <- rs.extract[String](COL1) @@ -30,7 +30,7 @@ class RowExtractorsSpec extends AnyWordSpec with Matchers with IdiomaticMockito } yield SomeClass(col1, col2, col3) } - implicit val otherClassExtractor = new RowExtractor[OtherClass] { + implicit val otherClassExtractor: RowExtractor[OtherClass] = new RowExtractor[OtherClass] { def extract(rs: ResultSet): Try[OtherClass] = for { col1 <- rs.extract[Int](COL1) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 814cac3..6684d54 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -3,9 +3,11 @@ import sbt._ object Dependencies { object Versions { - val scala = "2.12.12" - val crossScala = Seq(scala) - val scalaz = "7.2.26" + val targetJava = "8" + val scala_2_12 = "2.12.18" + val scala_2_13 = "2.13.12" + val scala = scala_2_13 + val crossScala = Seq(scala_2_12, scala_2_13) val scalatest = "3.1.1" val scalacheck = "1.15.1" val json4s = "3.6.8" @@ -32,11 +34,4 @@ object Dependencies { "org.postgresql" % "postgresql" % Versions.postgresql % Test, ) - val ConfigZDependencies: Seq[ModuleID] = Seq( - "com.typesafe.scala-logging" % "scala-logging" % Versions.scala_logging cross CrossVersion.binary, - "org.scala-lang" % "scala-reflect" % Versions.scala, - "org.scalaz" % "scalaz-core" % Versions.scalaz cross CrossVersion.binary, - "com.typesafe" % "config" % Versions.typesafe_config - ) - } diff --git a/project/plugins.sbt b/project/plugins.sbt index 5ce0af5..6abbfbe 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -5,10 +5,9 @@ resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repos resolvers += Resolver.url("typesafe", url("https://repo.typesafe.com/typesafe/ivy-releases/"))(Resolver.ivyStylePatterns) addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.12") -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.1") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.10.0") addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.6") diff --git a/version.sbt b/version.sbt index d4ccae4..5408a63 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -ThisBuild / version := "1.1.3-SNAPSHOT" +ThisBuild / version := "2.0.0"