From 80314c937327d444c4e2e2ddffbe4b921b5cfa6b Mon Sep 17 00:00:00 2001 From: Dan Brooke Date: Fri, 30 Nov 2018 15:51:22 +0100 Subject: [PATCH 1/2] Simplify --- core/src/main/scala/enums/EnumUtils.scala | 12 --------- core/src/main/scala/enums/enumUtils.scala | 10 +++++++ .../scala/enums/reflection/EnumUtils.scala | 20 -------------- .../scala/enums/reflection/Nameable.scala | 12 +++++++++ core/src/test/scala/enums/EnumUtilsSpec.scala | 7 ++--- .../enums/SimpleStringEnumSerializer.scala | 2 -- .../SimpleStringEnumSerializer.scala | 16 ------------ .../src/test/scala/enums/SerializerSpec.scala | 3 +-- .../enums/reflection/SerializerSpec.scala | 26 ------------------- version.sbt | 2 +- 10 files changed, 26 insertions(+), 84 deletions(-) delete mode 100644 core/src/main/scala/enums/EnumUtils.scala create mode 100644 core/src/main/scala/enums/enumUtils.scala delete mode 100644 core/src/main/scala/enums/reflection/EnumUtils.scala create mode 100644 core/src/main/scala/enums/reflection/Nameable.scala delete mode 100644 spray-json/src/main/scala/enums/reflection/SimpleStringEnumSerializer.scala delete mode 100644 spray-json/src/test/scala/enums/reflection/SerializerSpec.scala diff --git a/core/src/main/scala/enums/EnumUtils.scala b/core/src/main/scala/enums/EnumUtils.scala deleted file mode 100644 index 6fd5f5b..0000000 --- a/core/src/main/scala/enums/EnumUtils.scala +++ /dev/null @@ -1,12 +0,0 @@ -package enums - - -object EnumUtils { - trait Nameable { - def name: String - } - trait Enumerable[T <: Nameable] { - def values: Iterable[T] - def withName(name: String): Option[T] = values.find(_.name == name) - } -} diff --git a/core/src/main/scala/enums/enumUtils.scala b/core/src/main/scala/enums/enumUtils.scala new file mode 100644 index 0000000..1e2899d --- /dev/null +++ b/core/src/main/scala/enums/enumUtils.scala @@ -0,0 +1,10 @@ +package enums + +trait Nameable { + def name: String +} + +trait Enumerable[T <: Nameable] { + def values: Iterable[T] + def withName(name: String): Option[T] = values.find(_.name == name) +} diff --git a/core/src/main/scala/enums/reflection/EnumUtils.scala b/core/src/main/scala/enums/reflection/EnumUtils.scala deleted file mode 100644 index 2941393..0000000 --- a/core/src/main/scala/enums/reflection/EnumUtils.scala +++ /dev/null @@ -1,20 +0,0 @@ -package enums.reflection - -/** - * This version of the enum utils can - * only be used in top-level definitions, - * otherwise the reflective .getSimpleName - * call throws an exception. - */ -object EnumUtils { - - trait Nameable { - def name: String = this.getClass.getSimpleName - .replaceAll("minus", "-").replaceAll("\\$", "")} - - trait Enumerable[T <: Nameable] { - def values: Iterable[T] - def withName(name: String): Option[T] = values.find(_.name == name) - } - -} diff --git a/core/src/main/scala/enums/reflection/Nameable.scala b/core/src/main/scala/enums/reflection/Nameable.scala new file mode 100644 index 0000000..2612a8a --- /dev/null +++ b/core/src/main/scala/enums/reflection/Nameable.scala @@ -0,0 +1,12 @@ +package enums.reflection + +/** + * This version of the enum utils can + * only be used in top-level definitions, + * otherwise the reflective .getSimpleName + * call throws an exception. + */ +trait Nameable extends enums.Nameable { + def name: String = this.getClass.getSimpleName + .replaceAll("minus", "-").replaceAll("\\$", "") +} diff --git a/core/src/test/scala/enums/EnumUtilsSpec.scala b/core/src/test/scala/enums/EnumUtilsSpec.scala index a976e57..1c3dd20 100644 --- a/core/src/test/scala/enums/EnumUtilsSpec.scala +++ b/core/src/test/scala/enums/EnumUtilsSpec.scala @@ -6,8 +6,6 @@ import org.specs2.specification.Scope class EnumUtilsSpec extends Specification { "EnumUtils" should { - import enums.EnumUtils._ - "enable searching for name" in new Scope { sealed trait SomeEnum extends Nameable object SomeEnum extends Enumerable[SomeEnum] { @@ -18,7 +16,7 @@ class EnumUtilsSpec extends Specification { } } - "reflection.EnumUtils defined at top-level" should { + "reflection.Nameable defined at top-level" should { "enable searching for name without explicitly defining the name in the enum" in { SomeReflectiveEnum.withName("SomeReflectiveValue") mustEqual Some(SomeReflectiveEnum.SomeReflectiveValue) @@ -26,8 +24,7 @@ class EnumUtilsSpec extends Specification { } } -import enums.reflection.EnumUtils._ -sealed trait SomeReflectiveEnum extends Nameable +sealed trait SomeReflectiveEnum extends reflection.Nameable object SomeReflectiveEnum extends Enumerable[SomeReflectiveEnum] { case object SomeReflectiveValue extends SomeReflectiveEnum val values = Set(SomeReflectiveValue) diff --git a/spray-json/src/main/scala/enums/SimpleStringEnumSerializer.scala b/spray-json/src/main/scala/enums/SimpleStringEnumSerializer.scala index dc5f6de..f2c38dd 100644 --- a/spray-json/src/main/scala/enums/SimpleStringEnumSerializer.scala +++ b/spray-json/src/main/scala/enums/SimpleStringEnumSerializer.scala @@ -1,8 +1,6 @@ package enums - import spray.json.{JsString, JsValue, JsonFormat, _} -import enums.EnumUtils._ class SimpleStringEnumSerializer[T <: Nameable](enum: Enumerable[T]) { implicit val enumFormat = new JsonFormat[T] { diff --git a/spray-json/src/main/scala/enums/reflection/SimpleStringEnumSerializer.scala b/spray-json/src/main/scala/enums/reflection/SimpleStringEnumSerializer.scala deleted file mode 100644 index 6224a91..0000000 --- a/spray-json/src/main/scala/enums/reflection/SimpleStringEnumSerializer.scala +++ /dev/null @@ -1,16 +0,0 @@ -package enums.reflection - - -import spray.json.{JsString, JsValue, JsonFormat, _} -import enums.reflection.EnumUtils._ - -class SimpleStringEnumSerializer[T <: Nameable](enum: Enumerable[T]) { - implicit val enumFormat = new JsonFormat[T] { - def write(x: T) = JsString(x.name) - def read(value: JsValue) = value match { - case JsString(x) => enum.withName(x).getOrElse(serializationError(s"Unknown enum " + - s"value: $x")) - case x => deserializationError("Expected value as JsString, but got " + x) - } - } -} diff --git a/spray-json/src/test/scala/enums/SerializerSpec.scala b/spray-json/src/test/scala/enums/SerializerSpec.scala index 8b95f93..d904f08 100644 --- a/spray-json/src/test/scala/enums/SerializerSpec.scala +++ b/spray-json/src/test/scala/enums/SerializerSpec.scala @@ -18,9 +18,8 @@ class SerializerSpec extends Specification with DefaultJsonProtocol{ } } -import enums.EnumUtils._ sealed trait SomeEnum extends Nameable object SomeEnum extends Enumerable[SomeEnum] { case object SomeValue extends SomeEnum {def name = "SomeValue"} val values = Set(SomeValue) -} \ No newline at end of file +} diff --git a/spray-json/src/test/scala/enums/reflection/SerializerSpec.scala b/spray-json/src/test/scala/enums/reflection/SerializerSpec.scala deleted file mode 100644 index d3db4d3..0000000 --- a/spray-json/src/test/scala/enums/reflection/SerializerSpec.scala +++ /dev/null @@ -1,26 +0,0 @@ -package enums.reflection - -import org.specs2.mutable.Specification -import spray.json._ - -class SerializerSpec extends Specification with DefaultJsonProtocol{ - - "simple string enums defined using reflection" should { - "serialize/deserialize to json" in { - import SomeReflectiveEnum._ - case class Enclosing(value: SomeReflectiveEnum) - implicit val EnumJF = new SimpleStringEnumSerializer[SomeReflectiveEnum](SomeReflectiveEnum).enumFormat - implicit val EnclosingJF = jsonFormat1(Enclosing) - - Enclosing(SomeReflectiveValue).toJson mustEqual """{"value": "SomeReflectiveValue"}""".parseJson - """{"value": "SomeReflectiveValue"}""".parseJson.convertTo[Enclosing] mustEqual Enclosing(SomeReflectiveValue) - } - } -} - -import enums.reflection.EnumUtils._ -sealed trait SomeReflectiveEnum extends Nameable -object SomeReflectiveEnum extends Enumerable[SomeReflectiveEnum] { - case object SomeReflectiveValue extends SomeReflectiveEnum - val values = Set(SomeReflectiveValue) -} \ No newline at end of file diff --git a/version.sbt b/version.sbt index 9904311..6dc0588 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.2.2-SNAPSHOT" +version in ThisBuild := "1.0.0-SNAPSHOT" From c26bae597b929041755a664a912060e44ef19798 Mon Sep 17 00:00:00 2001 From: Dan Brooke Date: Fri, 30 Nov 2018 16:09:15 +0100 Subject: [PATCH 2/2] Add circe module --- build.sbt | 43 +++++++++++++------ .../enums/SimpleStringEnumSerializer.scala | 12 ++++++ .../src/test/scala/enums/SerializerSpec.scala | 34 +++++++++++++++ project/build.properties | 2 +- project/plugins.sbt | 2 +- 5 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 circe/src/main/scala/enums/SimpleStringEnumSerializer.scala create mode 100644 circe/src/test/scala/enums/SerializerSpec.scala diff --git a/build.sbt b/build.sbt index 858d305..d1e74d8 100644 --- a/build.sbt +++ b/build.sbt @@ -1,28 +1,47 @@ import sbt.Keys._ -val core = Project("enum-utils", file("core")) - .enablePlugins(OssLibPlugin) - .settings(organization := "com.thenewmotion", - name := "enum-utils", - libraryDependencies ++= Seq( - "org.specs2" %% "specs2-core" % "3.9.5" % "test" +val specs2 = "org.specs2" %% "specs2-core" % "3.9.5" % "test" + +def baseProject(id: String, base: String) = + Project(id, file(base)) + .enablePlugins(OssLibPlugin) + .settings( + organization := "com.thenewmotion", + name := id ) + +val core = baseProject("enum-utils", "core") + .settings( + libraryDependencies ++= Seq(specs2) ) -val sprayJson = Project("enum-utils-spray-json", file("spray-json")) - .enablePlugins(OssLibPlugin) +val sprayJson = baseProject("enum-utils-spray-json", "spray-json") .dependsOn(core) .settings( - organization := "com.thenewmotion", - name := "enum-utils-spray-json", libraryDependencies ++= Seq( "io.spray" %% "spray-json" % "1.3.3", - "org.specs2" %% "specs2-core" % "3.9.5" % "test" + specs2 + ) + ) + +val circeV = "0.10.0" + +val circe = baseProject("enum-utils-circe", "circe") + .dependsOn(core) + .settings( + scalacOptions ++= Seq( + "-Ywarn-macros:after" // Get rid of "is never used" warnings for implicit encoders and decoders + ), + libraryDependencies ++= Seq( + "io.circe" %% "circe-core" % circeV, + "io.circe" %% "circe-generic" % circeV % "test", + "io.circe" %% "circe-parser" % circeV % "test", + specs2 ) ) val parent = Project("enum-utils-parent", file(".")) - .aggregate(core, sprayJson) + .aggregate(core, sprayJson, circe) .enablePlugins(LibPlugin) .settings(publish := {}) diff --git a/circe/src/main/scala/enums/SimpleStringEnumSerializer.scala b/circe/src/main/scala/enums/SimpleStringEnumSerializer.scala new file mode 100644 index 0000000..fa84695 --- /dev/null +++ b/circe/src/main/scala/enums/SimpleStringEnumSerializer.scala @@ -0,0 +1,12 @@ +package enums + +import io.circe.{Decoder, Encoder} +import cats.implicits._ + +class SimpleStringEnumSerializer[T <: Nameable](enum: Enumerable[T]) { + implicit val decoder: Decoder[T] = Decoder.decodeString.emap(x => + Either.fromOption(enum.withName(x), s"Unknown enum " + s"value: $x") + ) + + implicit val encoder: Encoder[T] = Encoder.encodeString.contramap(_.name) +} diff --git a/circe/src/test/scala/enums/SerializerSpec.scala b/circe/src/test/scala/enums/SerializerSpec.scala new file mode 100644 index 0000000..e5ddda4 --- /dev/null +++ b/circe/src/test/scala/enums/SerializerSpec.scala @@ -0,0 +1,34 @@ +package enums + +import org.specs2.mutable.Specification +import io.circe._, io.circe.generic.semiauto._ +import io.circe.syntax._ +import io.circe.parser._ + +class SerializerSpec extends Specification { + + case class Enclosing(value: SomeEnum) + import SomeEnum._ + + "simple string enums" should { + "serialize to json" in { + implicit val e = new SimpleStringEnumSerializer[SomeEnum](SomeEnum).encoder + implicit val enclosingEncoder: Encoder[Enclosing] = deriveEncoder[Enclosing] + + val json = parse("""{"value": "SomeValue"}""").getOrElse(throw new RuntimeException("Invalid Json!!")) + Enclosing(SomeValue).asJson mustEqual json + } + + "deserialize from json" in { + implicit val d = new SimpleStringEnumSerializer[SomeEnum](SomeEnum).decoder + implicit val enclosingDecoder: Decoder[Enclosing] = deriveDecoder[Enclosing] + parse("""{"value": "SomeValue"}""").flatMap(_.as[Enclosing]) must beRight(Enclosing(SomeValue)) + } + } +} + +sealed trait SomeEnum extends Nameable +object SomeEnum extends Enumerable[SomeEnum] { + case object SomeValue extends SomeEnum {def name = "SomeValue"} + val values = Set(SomeValue) +} diff --git a/project/build.properties b/project/build.properties index cddd489..091249b 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 0.13.16 +sbt.version = 1.2.6 diff --git a/project/plugins.sbt b/project/plugins.sbt index eb754f0..69b16ad 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,3 @@ resolvers += "TNM" at "http://nexus.thenewmotion.com/content/groups/public" -addSbtPlugin("com.newmotion" % "sbt-build-seed" % "3.1.0") +addSbtPlugin("com.newmotion" % "sbt-build-seed" % "4.1.2")