Skip to content

Simplification + Circe support #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -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 := {})
12 changes: 12 additions & 0 deletions circe/src/main/scala/enums/SimpleStringEnumSerializer.scala
Original file line number Diff line number Diff line change
@@ -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)
}
34 changes: 34 additions & 0 deletions circe/src/test/scala/enums/SerializerSpec.scala
Original file line number Diff line number Diff line change
@@ -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)
}
12 changes: 0 additions & 12 deletions core/src/main/scala/enums/EnumUtils.scala

This file was deleted.

10 changes: 10 additions & 0 deletions core/src/main/scala/enums/enumUtils.scala
Original file line number Diff line number Diff line change
@@ -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)
}
20 changes: 0 additions & 20 deletions core/src/main/scala/enums/reflection/EnumUtils.scala

This file was deleted.

12 changes: 12 additions & 0 deletions core/src/main/scala/enums/reflection/Nameable.scala
Original file line number Diff line number Diff line change
@@ -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("\\$", "")
}
7 changes: 2 additions & 5 deletions core/src/test/scala/enums/EnumUtilsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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] {
Expand All @@ -18,16 +16,15 @@ 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)
}
}
}

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)
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version = 0.13.16
sbt.version = 1.2.6
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -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] {
Expand Down

This file was deleted.

3 changes: 1 addition & 2 deletions spray-json/src/test/scala/enums/SerializerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
26 changes: 0 additions & 26 deletions spray-json/src/test/scala/enums/reflection/SerializerSpec.scala

This file was deleted.

2 changes: 1 addition & 1 deletion version.sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version in ThisBuild := "0.2.2-SNAPSHOT"
version in ThisBuild := "1.0.0-SNAPSHOT"