From e6218922e236603e247ed18fb1d24983793f1029 Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Mon, 12 Feb 2018 21:29:59 +0800 Subject: [PATCH 1/7] Add enumeratum-quill --- README.md | 85 ++++++++++++++++++- build.sbt | 28 ++++++ .../src/main/scala/enumeratum/Quill.scala | 15 ++++ .../src/main/scala/enumeratum/QuillEnum.scala | 45 ++++++++++ .../main/scala/enumeratum/values/Quill.scala | 20 +++++ .../enumeratum/values/QuillValueEnum.scala | 82 ++++++++++++++++++ .../test/scala/enumeratum/QuillEnumSpec.scala | 50 +++++++++++ .../values/QuillValueEnumSpec.scala | 50 +++++++++++ publish-integration-libs | 4 +- 9 files changed, 376 insertions(+), 3 deletions(-) create mode 100644 enumeratum-quill/src/main/scala/enumeratum/Quill.scala create mode 100644 enumeratum-quill/src/main/scala/enumeratum/QuillEnum.scala create mode 100644 enumeratum-quill/src/main/scala/enumeratum/values/Quill.scala create mode 100644 enumeratum-quill/src/main/scala/enumeratum/values/QuillValueEnum.scala create mode 100644 enumeratum-quill/src/test/scala/enumeratum/QuillEnumSpec.scala create mode 100644 enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala diff --git a/README.md b/README.md index 2ce900bf..8d65768c 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Integrations are available for: - [Argonaut](http://argonaut.io): JVM and ScalaJS - [Json4s](http://json4s.org): JVM only - [ScalaCheck](https://www.scalacheck.org): JVM only +- [Quill](http://getquill.io): JVM and ScalaJS ### Table of Contents @@ -54,8 +55,9 @@ Integrations are available for: 10. [Json4s integration](#json4s) 11. [Slick integration](#slick-integration) 12. [ScalaCheck](#scalacheck) -13. [Benchmarking](#benchmarking) -14. [Publishing](#publishing) +13. [Quill integration](#quill) +14. [Benchmarking](#benchmarking) +15. [Publishing](#publishing) ## Quick start @@ -848,6 +850,85 @@ Similarly, you can get `Arbitrary` and `Cogen` instances for every `ValueEnum` s import enumeratum.values.scalacheck._ ``` +## Quill +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.beachape/enumeratum-quill_2.11/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.beachape/enumeratum-quill_2.11) + +### SBT + +To use enumeratum with [Quill](http://getquill.io): + +```scala +libraryDependencies ++= Seq( + "com.beachape" %% "enumeratum-quill" % enumeratumQuillVersion +) +``` + +To use with ScalaJS: + +```scala +libraryDependencies ++= Seq( + "com.beachape" %%% "enumeratum-quill" % enumeratumQuillVersion +) +``` + +### Usage + +#### Enum + +```scala +import enumeratum._ + +sealed trait ShirtSize extends EnumEntry + +case object ShirtSize extends Enum[ShirtSize] with QuillEnum[ShirtSize] { + + case object Small extends ShirtSize + case object Medium extends ShirtSize + case object Large extends ShirtSize + + val values = findValues + +} + +case class Shirt(size: ShirtSize) + +import io.getquill._ + +val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) +import ctx._ + +val sql = ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))).string +assert(sql == "INSERT INTO Shirt (size) VALUES (?)") +``` + +#### ValueEnum + +```scala +import enumeratum._ + +sealed abstract class ShirtSize(val value: Int) extends IntEnumEntry + +case object ShirtSize extends IntEnum[ShirtSize] with IntQuillEnum[ShirtSize] { + + case object Small extends ShirtSize(1) + case object Medium extends ShirtSize(2) + case object Large extends ShirtSize(3) + + val values = findValues + +} + +case class Shirt(size: ShirtSize) + +import io.getquill._ + +val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) +import ctx._ + +val sql = ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))).string +assert(sql == "INSERT INTO Shirt (size) VALUES (?)") +``` + ## Slick integration [Slick](http://slick.lightbend.com) doesn't have a separate integration at the moment. You just have to provide a `MappedColumnType` for each database column that should be represented as an enum on the Scala side. diff --git a/build.sbt b/build.sbt index d682a759..633fef83 100644 --- a/build.sbt +++ b/build.sbt @@ -13,6 +13,7 @@ lazy val circeVersion = "0.9.0" lazy val uPickleVersion = "0.4.4" lazy val argonautVersion = "6.2" lazy val json4sVersion = "3.5.1" +lazy val quillVersion = "2.3.2" def thePlayVersion(scalaVersion: String) = CrossVersion.partialVersion(scalaVersion) match { @@ -301,6 +302,33 @@ lazy val enumeratumScalacheck = ) ) +lazy val quillAggregate = aggregateProject("quill", enumeratumQuillJs, enumeratumQuillJvm) +lazy val enumeratumQuill = crossProject + .crossType(CrossType.Pure) + .in(file("enumeratum-quill")) + .settings(commonWithPublishSettings: _*) + .settings(testSettings: _*) + .settings( + name := "enumeratum-quill", + version := "0.1.0-SNAPSHOT", + libraryDependencies ++= { + import org.scalajs.sbtplugin._ + val cross = { + if (ScalaJSPlugin.autoImport.jsDependencies.?.value.isDefined) + ScalaJSCrossVersion.binary + else + CrossVersion.binary + } + Seq( + impl.ScalaJSGroupID.withCross("io.getquill", "quill-core", cross) % quillVersion, + impl.ScalaJSGroupID.withCross("io.getquill", "quill-sql", cross) % quillVersion % Test, + impl.ScalaJSGroupID.withCross("com.beachape", "enumeratum", cross) % Versions.Core.stable + ) + } + ) +lazy val enumeratumQuillJs = enumeratumQuill.js +lazy val enumeratumQuillJvm = enumeratumQuill.jvm + lazy val commonSettings = Seq( organization := "com.beachape", scalafmtOnCompile := true, diff --git a/enumeratum-quill/src/main/scala/enumeratum/Quill.scala b/enumeratum-quill/src/main/scala/enumeratum/Quill.scala new file mode 100644 index 00000000..cca80be3 --- /dev/null +++ b/enumeratum-quill/src/main/scala/enumeratum/Quill.scala @@ -0,0 +1,15 @@ +package enumeratum + +import io.getquill.MappedEncoding + +object Quill { + /** + * Returns an Encoder for the given enum + */ + def encoder[A <: EnumEntry](enum: Enum[A]): MappedEncoding[A, String] = MappedEncoding(_.entryName) + + /** + * Returns a Decoder for the given enum + */ + def decoder[A <: EnumEntry](enum: Enum[A]): MappedEncoding[String, A] = MappedEncoding(enum.withName) +} diff --git a/enumeratum-quill/src/main/scala/enumeratum/QuillEnum.scala b/enumeratum-quill/src/main/scala/enumeratum/QuillEnum.scala new file mode 100644 index 00000000..3a931bd3 --- /dev/null +++ b/enumeratum-quill/src/main/scala/enumeratum/QuillEnum.scala @@ -0,0 +1,45 @@ +package enumeratum + +import io.getquill.MappedEncoding + +/** + * Helper trait that adds implicit Quill encoders and decoders for an [[Enum]]'s members + * + * Example: + * + * {{{ + * scala> import enumeratum._ + * scala> import io.getquill._ + * + * scala> sealed trait ShirtSize extends EnumEntry + * scala> case object ShirtSize extends Enum[ShirtSize] with QuillEnum[ShirtSize] { + * | case object Small extends ShirtSize + * | case object Medium extends ShirtSize + * | case object Large extends ShirtSize + * | val values = findValues + * | } + * + * scala> case class Shirt(size: ShirtSize) + * + * scala> val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + * scala> import ctx._ + * + * scala> val size: ShirtSize = ShirtSize.Small + * + * scala> ctx.run(query[Shirt].insert(_.size -> lift(size))).string + * res0: String = INSERT INTO Shirt (size) VALUES (?) + * }}} + */ +trait QuillEnum[A <: EnumEntry] { this: Enum[A] => + + /** + * Implicit Encoder for this enum + */ + implicit lazy val enumEncoder: MappedEncoding[A, String] = Quill.encoder(this) + + /** + * Implicit Decoder for this enum + */ + implicit lazy val enumDecoder: MappedEncoding[String, A] = Quill.decoder(this) + +} diff --git a/enumeratum-quill/src/main/scala/enumeratum/values/Quill.scala b/enumeratum-quill/src/main/scala/enumeratum/values/Quill.scala new file mode 100644 index 00000000..eddc3d5f --- /dev/null +++ b/enumeratum-quill/src/main/scala/enumeratum/values/Quill.scala @@ -0,0 +1,20 @@ +package enumeratum.values + +import io.getquill.MappedEncoding + +object Quill { + + /** + * Returns an Encoder for the provided ValueEnum + */ + def encoder[ValueType, EntryType <: ValueEnumEntry[ValueType]]( + enum: ValueEnum[ValueType, EntryType] + ): MappedEncoding[EntryType, ValueType] = MappedEncoding(_.value) + + /** + * Returns a Decoder for the provided ValueEnum + */ + def decoder[ValueType, EntryType <: ValueEnumEntry[ValueType]]( + enum: ValueEnum[ValueType, EntryType] + ): MappedEncoding[ValueType, EntryType] = MappedEncoding(enum.withValue) +} diff --git a/enumeratum-quill/src/main/scala/enumeratum/values/QuillValueEnum.scala b/enumeratum-quill/src/main/scala/enumeratum/values/QuillValueEnum.scala new file mode 100644 index 00000000..c540cb89 --- /dev/null +++ b/enumeratum-quill/src/main/scala/enumeratum/values/QuillValueEnum.scala @@ -0,0 +1,82 @@ +package enumeratum.values + +import io.getquill.MappedEncoding + +sealed trait QuillValueEnum[ValueType, EntryType <: ValueEnumEntry[ValueType]] { + this: ValueEnum[ValueType, EntryType] => + + /** + * Implicit Encoder for this enum + */ + implicit val quillEncoder: MappedEncoding[EntryType, ValueType] = Quill.encoder(this) + + /** + * Implicit Decoder for this enum + */ + implicit val quillDecoder: MappedEncoding[ValueType, EntryType] = Quill.decoder(this) +} + +/** + * QuillEnum for IntEnumEntry + * + * {{{ + * scala> import enumeratum.values._ + * scala> import io.getquill._ + * + * scala> sealed abstract class ShirtSize(val value:Int) extends IntEnumEntry + * scala> case object ShirtSize extends IntEnum[ShirtSize] with IntQuillEnum[ShirtSize] { + * | case object Small extends ShirtSize(1) + * | case object Medium extends ShirtSize(2) + * | case object Large extends ShirtSize(3) + * | val values = findValues + * | } + * + * scala> case class Shirt(size: ShirtSize) + * + * scala> val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + * scala> import ctx._ + * + * scala> val size: ShirtSize = ShirtSize.Small + * + * scala> ctx.run(query[Shirt].insert(_.size -> lift(size))).string + * res0: String = INSERT INTO Shirt (size) VALUES (?) + * }}} + */ +trait IntQuillEnum[EntryType <: IntEnumEntry] extends QuillValueEnum[Int, EntryType] { + this: ValueEnum[Int, EntryType] => +} + +/** + * QuillEnum for LongEnumEntry + */ +trait LongQuillEnum[EntryType <: LongEnumEntry] extends QuillValueEnum[Long, EntryType] { + this: ValueEnum[Long, EntryType] => +} + +/** + * QuillEnum for ShortEnumEntry + */ +trait ShortQuillEnum[EntryType <: ShortEnumEntry] extends QuillValueEnum[Short, EntryType] { + this: ValueEnum[Short, EntryType] => +} + +/** + * QuillEnum for StringEnumEntry + */ +trait StringQuillEnum[EntryType <: StringEnumEntry] extends QuillValueEnum[String, EntryType] { + this: ValueEnum[String, EntryType] => +} + +/** + * QuillEnum for CharEnumEntry + */ +trait CharQuillEnum[EntryType <: CharEnumEntry] extends QuillValueEnum[Char, EntryType] { + this: ValueEnum[Char, EntryType] => +} + +/** + * QuillEnum for ByteEnumEntry + */ +trait ByteQuillEnum[EntryType <: ByteEnumEntry] extends QuillValueEnum[Byte, EntryType] { + this: ValueEnum[Byte, EntryType] => +} diff --git a/enumeratum-quill/src/test/scala/enumeratum/QuillEnumSpec.scala b/enumeratum-quill/src/test/scala/enumeratum/QuillEnumSpec.scala new file mode 100644 index 00000000..344c55b4 --- /dev/null +++ b/enumeratum-quill/src/test/scala/enumeratum/QuillEnumSpec.scala @@ -0,0 +1,50 @@ +package enumeratum + +import org.scalatest.{FunSpec, Matchers} + +import scala.collection.immutable + +class QuillEnumSpec extends FunSpec with Matchers { + + describe("to SQL String") { + + // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found + it("should compile") { + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))) + """.stripMargin should compile + } + + } + + describe("from SQL String") { + + // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found + it("should compile") { + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[Shirt]) + """.stripMargin should compile + } + + } +} + +sealed trait ShirtSize extends EnumEntry + +case object ShirtSize extends Enum[ShirtSize] with QuillEnum[ShirtSize] { + + case object Small extends ShirtSize + case object Medium extends ShirtSize + case object Large extends ShirtSize + + override val values: immutable.IndexedSeq[ShirtSize] = findValues + +} + +case class Shirt(size: ShirtSize) diff --git a/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala b/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala new file mode 100644 index 00000000..25231107 --- /dev/null +++ b/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala @@ -0,0 +1,50 @@ +package enumeratum.values + +import org.scalatest.{FunSpec, Matchers} + +import scala.collection.immutable + +class QuillValueEnumSpec extends FunSpec with Matchers { + + describe("to SQL String") { + + // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found + it("should compile") { + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))) + """.stripMargin should compile + } + + } + + describe("from SQL String") { + + // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found + it("should compile") { + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[Shirt]) + """.stripMargin should compile + } + + } +} + +sealed abstract class ShirtSize(val value: Int) extends IntEnumEntry + +case object ShirtSize extends IntEnum[ShirtSize] with IntQuillEnum[ShirtSize] { + + case object Small extends ShirtSize(1) + case object Medium extends ShirtSize(2) + case object Large extends ShirtSize(3) + + override val values: immutable.IndexedSeq[ShirtSize] = findValues + +} + +case class Shirt(size: ShirtSize) diff --git a/publish-integration-libs b/publish-integration-libs index be9791ca..091f4da0 100755 --- a/publish-integration-libs +++ b/publish-integration-libs @@ -17,4 +17,6 @@ say "Done 6" && sbt "project enumeratum-json4s" +clean +publishSigned && say "Done 7" && sbt "project enumeratum-scalacheck" +clean +publishSigned && -say "All done" \ No newline at end of file +say "Done 8" && +sbt "project quill-aggregate" +clean +publishSigned && +say "All done" From c23dbd922fc07010790e0c9918cd787f741bf82b Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Tue, 13 Feb 2018 18:03:05 +0800 Subject: [PATCH 2/7] Improve QuillEnumSpec and QuillValueEnumSpec --- .../test/scala/enumeratum/QuillEnumSpec.scala | 29 +++++++-------- .../values/QuillValueEnumSpec.scala | 37 ++++++++++--------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/enumeratum-quill/src/test/scala/enumeratum/QuillEnumSpec.scala b/enumeratum-quill/src/test/scala/enumeratum/QuillEnumSpec.scala index 344c55b4..70ae7e7a 100644 --- a/enumeratum-quill/src/test/scala/enumeratum/QuillEnumSpec.scala +++ b/enumeratum-quill/src/test/scala/enumeratum/QuillEnumSpec.scala @@ -6,45 +6,42 @@ import scala.collection.immutable class QuillEnumSpec extends FunSpec with Matchers { - describe("to SQL String") { + describe("A QuillEnum") { // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found - it("should compile") { + it("should encode to String") { """ | import io.getquill._ | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) | import ctx._ - | ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))) + | ctx.run(query[QuillShirt].insert(_.size -> lift(QuillShirtSize.Small: QuillShirtSize))) """.stripMargin should compile } - } - - describe("from SQL String") { - // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found - it("should compile") { + it("should decode from String") { """ | import io.getquill._ | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) | import ctx._ - | ctx.run(query[Shirt]) + | ctx.run(query[QuillShirt]) """.stripMargin should compile } } + } -sealed trait ShirtSize extends EnumEntry +sealed trait QuillShirtSize extends EnumEntry -case object ShirtSize extends Enum[ShirtSize] with QuillEnum[ShirtSize] { +case object QuillShirtSize extends Enum[QuillShirtSize] with QuillEnum[QuillShirtSize] { - case object Small extends ShirtSize - case object Medium extends ShirtSize - case object Large extends ShirtSize + case object Small extends QuillShirtSize + case object Medium extends QuillShirtSize + case object Large extends QuillShirtSize - override val values: immutable.IndexedSeq[ShirtSize] = findValues + override val values: immutable.IndexedSeq[QuillShirtSize] = findValues } -case class Shirt(size: ShirtSize) +case class QuillShirt(size: QuillShirtSize) diff --git a/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala b/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala index 25231107..4e8fb867 100644 --- a/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala +++ b/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala @@ -6,45 +6,46 @@ import scala.collection.immutable class QuillValueEnumSpec extends FunSpec with Matchers { - describe("to SQL String") { + describe("An IntQuillEnum") { - // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found - it("should compile") { + it("should encode to Int") { + // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found """ | import io.getquill._ | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) | import ctx._ - | ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))) + | ctx.run(query[QuillBorrowerToLibraryItem].insert(_.borrower -> "Foo", _.item -> lift(QuillLibraryItem.Book: QuillLibraryItem))) """.stripMargin should compile } - } - - describe("from SQL String") { - - // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found - it("should compile") { + it("should decode from Int") { + // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found """ | import io.getquill._ | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) | import ctx._ - | ctx.run(query[Shirt]) + | ctx.run(query[QuillBorrowerToLibraryItem]) """.stripMargin should compile } } + } -sealed abstract class ShirtSize(val value: Int) extends IntEnumEntry +sealed abstract class QuillLibraryItem(val value: Int, val name: String) extends IntEnumEntry -case object ShirtSize extends IntEnum[ShirtSize] with IntQuillEnum[ShirtSize] { +case object QuillLibraryItem + extends IntEnum[QuillLibraryItem] + with IntQuillEnum[QuillLibraryItem] { - case object Small extends ShirtSize(1) - case object Medium extends ShirtSize(2) - case object Large extends ShirtSize(3) + // A good mix of named, unnamed, named + unordered args + case object Book extends QuillLibraryItem(value = 1, name = "book") + case object Movie extends QuillLibraryItem(name = "movie", value = 2) + case object Magazine extends QuillLibraryItem(3, "magazine") + case object CD extends QuillLibraryItem(4, name = "cd") - override val values: immutable.IndexedSeq[ShirtSize] = findValues + override val values: immutable.IndexedSeq[QuillLibraryItem] = findValues } -case class Shirt(size: ShirtSize) +case class QuillBorrowerToLibraryItem(borrower: String, item: QuillLibraryItem) From 1d89916e093a2e42c44b33d6caeb2ede4a5a8e7e Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Tue, 13 Feb 2018 18:03:37 +0800 Subject: [PATCH 3/7] Add tests for LongEnum, ShortEnum, StringEnum, CharEnum, ByteEnum --- .../values/QuillValueEnumSpec.scala | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala b/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala index 4e8fb867..278a2e46 100644 --- a/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala +++ b/enumeratum-quill/src/test/scala/enumeratum/values/QuillValueEnumSpec.scala @@ -30,6 +30,126 @@ class QuillValueEnumSpec extends FunSpec with Matchers { } + describe("A LongQuillEnum") { + + it("should encode to Long") { + // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillContent].insert(_.`type` -> lift(QuillContentType.Image: QuillContentType))) + """.stripMargin should compile + } + + it("should decode from Long") { + // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillContent]) + """.stripMargin should compile + } + + } + + describe("A ShortQuillEnum") { + + it("should encode to Short") { + // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillDrinkManufacturer].insert(_.name -> "Coca-Cola", _.drink -> lift(QuillDrink.Cola: QuillDrink))) + """.stripMargin should compile + } + + it("should decode from Short") { + // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillDrinkManufacturer]) + """.stripMargin should compile + } + + } + + describe("A StringQuillEnum") { + + it("should encode to String") { + // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillComputer].insert(_.operatingSystem -> lift(QuillOperatingSystem.Windows: QuillOperatingSystem))) + """.stripMargin should compile + } + + it("should decode from String") { + // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillComputer]) + """.stripMargin should compile + } + + } + + describe("A CharQuillEnum") { + + it("should encode to Char") { + // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillName].insert(_.name -> "Daniel", _.initials -> lift(QuillAlphabet.D: QuillAlphabet))) + """.stripMargin should compile + } + + it("should decode from Char") { + // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillName]) + """.stripMargin should compile + } + + } + + describe("A ByteQuillEnum") { + + it("should encode to Byte") { + // we only need to test whether it can compile because Quill will fail compilation if an Encoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillChar].insert(_.byte1 -> lift(QuillByte.ThreeByte: QuillByte), _.byte2 -> lift(QuillByte.TwoByte: QuillByte))) + """.stripMargin should compile + } + + it("should decode from Byte") { + // we only need to test whether it can compile because Quill will fail compilation if a Decoder is not found + """ + | import io.getquill._ + | val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) + | import ctx._ + | ctx.run(query[QuillChar]) + """.stripMargin should compile + } + + } + } sealed abstract class QuillLibraryItem(val value: Int, val name: String) extends IntEnumEntry @@ -49,3 +169,80 @@ case object QuillLibraryItem } case class QuillBorrowerToLibraryItem(borrower: String, item: QuillLibraryItem) + +sealed abstract class QuillContentType(val value: Long, name: String) extends LongEnumEntry + +case object QuillContentType + extends LongEnum[QuillContentType] + with LongQuillEnum[QuillContentType] { + + override val values: immutable.IndexedSeq[QuillContentType] = findValues + + case object Text extends QuillContentType(value = 1L, name = "text") + case object Image extends QuillContentType(value = 2L, name = "image") + case object Video extends QuillContentType(value = 3L, name = "video") + case object Audio extends QuillContentType(value = 4L, name = "audio") + +} + +case class QuillContent(`type`: QuillContentType) + +sealed abstract class QuillDrink(val value: Short, name: String) extends ShortEnumEntry + +case object QuillDrink extends ShortEnum[QuillDrink] with ShortQuillEnum[QuillDrink] { + + case object OrangeJuice extends QuillDrink(value = 1, name = "oj") + case object AppleJuice extends QuillDrink(value = 2, name = "aj") + case object Cola extends QuillDrink(value = 3, name = "cola") + case object Beer extends QuillDrink(value = 4, name = "beer") + + override val values: immutable.IndexedSeq[QuillDrink] = findValues + +} + +case class QuillDrinkManufacturer(name: String, drink: QuillDrink) + +sealed abstract class QuillOperatingSystem(val value: String) extends StringEnumEntry + +case object QuillOperatingSystem + extends StringEnum[QuillOperatingSystem] + with StringQuillEnum[QuillOperatingSystem] { + + case object Linux extends QuillOperatingSystem("linux") + case object OSX extends QuillOperatingSystem("osx") + case object Windows extends QuillOperatingSystem("windows") + case object Android extends QuillOperatingSystem("android") + + override val values: immutable.IndexedSeq[QuillOperatingSystem] = findValues + +} + +case class QuillComputer(operatingSystem: QuillOperatingSystem) + +sealed abstract class QuillAlphabet(val value: Char) extends CharEnumEntry + +case object QuillAlphabet extends CharEnum[QuillAlphabet] with CharQuillEnum[QuillAlphabet] { + + case object A extends QuillAlphabet('A') + case object B extends QuillAlphabet('B') + case object C extends QuillAlphabet('C') + case object D extends QuillAlphabet('D') + + override val values: immutable.IndexedSeq[QuillAlphabet] = findValues + +} + +case class QuillName(name: String, initials: QuillAlphabet) + +sealed abstract class QuillByte(val value: Byte) extends ByteEnumEntry + +object QuillByte extends ByteEnum[QuillByte] with ByteQuillEnum[QuillByte] { + override val values: immutable.IndexedSeq[QuillByte] = findValues + + case object OneByte extends QuillByte(1) + case object TwoByte extends QuillByte(2) + case object ThreeByte extends QuillByte(3) + case object FourByte extends QuillByte(4) +} + +case class QuillChar(byte1: QuillByte, byte2: QuillByte) From 77d51f6b20d7f54ad5f55efdf4d49a9cc59c8435 Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Tue, 13 Feb 2018 18:04:42 +0800 Subject: [PATCH 4/7] Encode/Decode CharEnum from/to String instead --- .../enumeratum/values/QuillValueEnum.scala | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/enumeratum-quill/src/main/scala/enumeratum/values/QuillValueEnum.scala b/enumeratum-quill/src/main/scala/enumeratum/values/QuillValueEnum.scala index c540cb89..4711c83e 100644 --- a/enumeratum-quill/src/main/scala/enumeratum/values/QuillValueEnum.scala +++ b/enumeratum-quill/src/main/scala/enumeratum/values/QuillValueEnum.scala @@ -2,18 +2,18 @@ package enumeratum.values import io.getquill.MappedEncoding -sealed trait QuillValueEnum[ValueType, EntryType <: ValueEnumEntry[ValueType]] { +sealed trait QuillValueEnum[ValueType, EntryType <: ValueEnumEntry[ValueType], QuillType] { this: ValueEnum[ValueType, EntryType] => /** * Implicit Encoder for this enum */ - implicit val quillEncoder: MappedEncoding[EntryType, ValueType] = Quill.encoder(this) + implicit val quillEncoder: MappedEncoding[EntryType, QuillType] /** * Implicit Decoder for this enum */ - implicit val quillDecoder: MappedEncoding[ValueType, EntryType] = Quill.decoder(this) + implicit val quillDecoder: MappedEncoding[QuillType, EntryType] } /** @@ -42,41 +42,61 @@ sealed trait QuillValueEnum[ValueType, EntryType <: ValueEnumEntry[ValueType]] { * res0: String = INSERT INTO Shirt (size) VALUES (?) * }}} */ -trait IntQuillEnum[EntryType <: IntEnumEntry] extends QuillValueEnum[Int, EntryType] { +trait IntQuillEnum[EntryType <: IntEnumEntry] extends QuillValueEnum[Int, EntryType, Int] { this: ValueEnum[Int, EntryType] => + implicit val quillEncoder: MappedEncoding[EntryType, Int] = Quill.encoder(this) + implicit val quillDecoder: MappedEncoding[Int, EntryType] = Quill.decoder(this) } /** * QuillEnum for LongEnumEntry */ -trait LongQuillEnum[EntryType <: LongEnumEntry] extends QuillValueEnum[Long, EntryType] { +trait LongQuillEnum[EntryType <: LongEnumEntry] extends QuillValueEnum[Long, EntryType, Long] { this: ValueEnum[Long, EntryType] => + implicit val quillEncoder: MappedEncoding[EntryType, Long] = Quill.encoder(this) + implicit val quillDecoder: MappedEncoding[Long, EntryType] = Quill.decoder(this) } /** * QuillEnum for ShortEnumEntry */ -trait ShortQuillEnum[EntryType <: ShortEnumEntry] extends QuillValueEnum[Short, EntryType] { +trait ShortQuillEnum[EntryType <: ShortEnumEntry] extends QuillValueEnum[Short, EntryType, Short] { this: ValueEnum[Short, EntryType] => + implicit val quillEncoder: MappedEncoding[EntryType, Short] = Quill.encoder(this) + implicit val quillDecoder: MappedEncoding[Short, EntryType] = Quill.decoder(this) } /** * QuillEnum for StringEnumEntry */ -trait StringQuillEnum[EntryType <: StringEnumEntry] extends QuillValueEnum[String, EntryType] { +trait StringQuillEnum[EntryType <: StringEnumEntry] extends QuillValueEnum[String, EntryType, String] { this: ValueEnum[String, EntryType] => + implicit val quillEncoder: MappedEncoding[EntryType, String] = Quill.encoder(this) + implicit val quillDecoder: MappedEncoding[String, EntryType] = Quill.decoder(this) } /** * QuillEnum for CharEnumEntry */ -trait CharQuillEnum[EntryType <: CharEnumEntry] extends QuillValueEnum[Char, EntryType] { +trait CharQuillEnum[EntryType <: CharEnumEntry] extends QuillValueEnum[Char, EntryType, String] { this: ValueEnum[Char, EntryType] => + + /** + * Because all existing Quill contexts do not have built-in Encoders for Char, convert it to a String instead. + */ + implicit val quillEncoder: MappedEncoding[EntryType, String] = MappedEncoding(enum => String.valueOf(enum.value)) + + /** + * Because all existing Quill contexts do not have built-in Decoders for Char, convert it from a String instead. + */ + implicit val quillDecoder: MappedEncoding[String, EntryType] = MappedEncoding(str => withValue(str.charAt(0))) } /** * QuillEnum for ByteEnumEntry */ -trait ByteQuillEnum[EntryType <: ByteEnumEntry] extends QuillValueEnum[Byte, EntryType] { +trait ByteQuillEnum[EntryType <: ByteEnumEntry] extends QuillValueEnum[Byte, EntryType, Byte] { this: ValueEnum[Byte, EntryType] => + implicit val quillEncoder: MappedEncoding[EntryType, Byte] = Quill.encoder(this) + implicit val quillDecoder: MappedEncoding[Byte, EntryType] = Quill.decoder(this) } From d0340fd1fc49fc61f709687a2ede3ef05811aae4 Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Tue, 13 Feb 2018 18:05:31 +0800 Subject: [PATCH 5/7] Improve Quill Integration examples in README --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8d65768c..89f05762 100644 --- a/README.md +++ b/README.md @@ -894,12 +894,14 @@ case class Shirt(size: ShirtSize) import io.getquill._ -val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) +lazy val ctx = new PostgresJdbcContext(SnakeCase, "ctx") import ctx._ -val sql = ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))).string -assert(sql == "INSERT INTO Shirt (size) VALUES (?)") +ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))) + +ctx.run(query[Shirt]).foreach(println) ``` +- Note that an explicit cast to the `EnumEntry` trait (eg. `ShirtSize.Small: ShirtSize`) is required when binding hardcoded `EnumEntry`s #### ValueEnum @@ -922,12 +924,14 @@ case class Shirt(size: ShirtSize) import io.getquill._ -val ctx = new SqlMirrorContext(MirrorSqlDialect, Literal) +lazy val ctx = new PostgresJdbcContext(SnakeCase, "ctx") import ctx._ -val sql = ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))).string -assert(sql == "INSERT INTO Shirt (size) VALUES (?)") +ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))) + +ctx.run(query[Shirt]).foreach(println) ``` +- Note that an explicit cast to the `ValueEnumEntry` abstract class (eg. `ShirtSize.Small: ShirtSize`) is required when binding hardcoded `ValueEnumEntry`s ## Slick integration From 8a62517840641984a531c206eb1b53bdd9333b99 Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Tue, 13 Feb 2018 18:06:24 +0800 Subject: [PATCH 6/7] Add note on quill-cassandra and quill-orientdb in README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 89f05762..1b2b56b8 100644 --- a/README.md +++ b/README.md @@ -932,6 +932,8 @@ ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))) ctx.run(query[Shirt]).foreach(println) ``` - Note that an explicit cast to the `ValueEnumEntry` abstract class (eg. `ShirtSize.Small: ShirtSize`) is required when binding hardcoded `ValueEnumEntry`s +- `quill-cassandra` currently does not support `ShortEnum` and `ByteEnum` (see getquill/quill#1009) +- `quill-orientdb` currently does not support `ByteEnum` (see getquill/quill#1029) ## Slick integration From 16869e1e97b2d4660cb658b0a8300e1b898092cd Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Tue, 13 Feb 2018 18:18:51 +0800 Subject: [PATCH 7/7] Fix Quill Issue links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c457bb22..1412de73 100644 --- a/README.md +++ b/README.md @@ -932,8 +932,8 @@ ctx.run(query[Shirt].insert(_.size -> lift(ShirtSize.Small: ShirtSize))) ctx.run(query[Shirt]).foreach(println) ``` - Note that an explicit cast to the `ValueEnumEntry` abstract class (eg. `ShirtSize.Small: ShirtSize`) is required when binding hardcoded `ValueEnumEntry`s -- `quill-cassandra` currently does not support `ShortEnum` and `ByteEnum` (see getquill/quill#1009) -- `quill-orientdb` currently does not support `ByteEnum` (see getquill/quill#1029) +- `quill-cassandra` currently does not support `ShortEnum` and `ByteEnum` (see [getquill/quill#1009](https://github.com/getquill/quill/issues/1009)) +- `quill-orientdb` currently does not support `ByteEnum` (see [getquill/quill#1029](https://github.com/getquill/quill/issues/1029)) ## Slick integration