Skip to content

Commit

Permalink
Add integration with Play-Json
Browse files Browse the repository at this point in the history
  • Loading branch information
lloydmeta committed Aug 4, 2016
1 parent 1273b4f commit 0dc63b4
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ object EnumFormats {
/**
* Returns a Reads for the provided ValueEnum based on the given base Reads for the Enum's value type
*/
def reads[ValueType <: AnyVal, EntryType <: ValueEnumEntry[ValueType]](enum: ValueEnum[ValueType, EntryType])(implicit baseReads: Reads[ValueType]): Reads[EntryType] = new Reads[EntryType] {
def reads[ValueType, EntryType <: ValueEnumEntry[ValueType]](enum: ValueEnum[ValueType, EntryType])(implicit baseReads: Reads[ValueType]): Reads[EntryType] = new Reads[EntryType] {
def reads(json: JsValue): JsResult[EntryType] = baseReads.reads(json).flatMap { s =>
val maybeBound = enum.withValueOpt(s)
maybeBound match {
Expand All @@ -25,14 +25,14 @@ object EnumFormats {
/**
* Returns a Writes for the provided ValueEnum based on the given base Writes for the Enum's value type
*/
def writes[ValueType <: AnyVal, EntryType <: ValueEnumEntry[ValueType]](enum: ValueEnum[ValueType, EntryType])(implicit baseWrites: Writes[ValueType]): Writes[EntryType] = new Writes[EntryType] {
def writes[ValueType, EntryType <: ValueEnumEntry[ValueType]](enum: ValueEnum[ValueType, EntryType])(implicit baseWrites: Writes[ValueType]): Writes[EntryType] = new Writes[EntryType] {
def writes(o: EntryType): JsValue = baseWrites.writes(o.value)
}

/**
* Returns a Formats for the provided ValueEnum based on the given base Reads and Writes for the Enum's value type
*/
def formats[ValueType <: AnyVal, EntryType <: ValueEnumEntry[ValueType]](enum: ValueEnum[ValueType, EntryType])(implicit baseReads: Reads[ValueType], baseWrites: Writes[ValueType]): Format[EntryType] = {
def formats[ValueType, EntryType <: ValueEnumEntry[ValueType]](enum: ValueEnum[ValueType, EntryType])(implicit baseReads: Reads[ValueType], baseWrites: Writes[ValueType]): Format[EntryType] = {
Format(reads(enum), writes(enum))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import play.api.libs.json.Format
* Copyright 2016
*/

trait PlayJsonValueEnum[ValueType <: AnyVal, EntryType <: ValueEnumEntry[ValueType]] { enum: ValueEnum[ValueType, EntryType] =>
trait PlayJsonValueEnum[ValueType, EntryType <: ValueEnumEntry[ValueType]] { enum: ValueEnum[ValueType, EntryType] =>

/**
* Implicit JSON format for the entries of this enum
Expand Down Expand Up @@ -36,4 +36,11 @@ trait LongPlayJsonValueEnum[EntryType <: LongEnumEntry] extends PlayJsonValueEnu
*/
trait ShortPlayJsonValueEnum[EntryType <: ShortEnumEntry] extends PlayJsonValueEnum[Short, EntryType] { this: ShortEnum[EntryType] =>
implicit val format: Format[EntryType] = EnumFormats.formats(this)
}

/**
* Enum implementation for String enum members that contains an implicit Play JSON Format
*/
trait StringPlayJsonValueEnum[EntryType <: StringEnumEntry] extends PlayJsonValueEnum[String, EntryType] { this: StringEnum[EntryType] =>
implicit val format: Format[EntryType] = EnumFormats.formats(this)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package enumeratum.values

import org.scalatest._
import play.api.libs.json.JsString

/**
* Created by Lloyd on 4/13/16.
Expand All @@ -11,26 +12,29 @@ class EnumFormatsSpec extends FunSpec with Matchers with EnumJsonFormatHelpers {

describe(".reads") {

testReads("IntEnum", LibraryItem)
testReads("LongEnum", ContentType)
testReads("ShortEnum", Drinks)
testNumericReads("IntEnum", LibraryItem)
testNumericReads("LongEnum", ContentType)
testNumericReads("ShortEnum", Drinks)
testReads("StringEnum", OperatingSystem, JsString)

}

describe(".writes") {

testWrites("IntEnum", LibraryItem)
testWrites("LongEnum", ContentType)
testWrites("ShortEnum", Drinks)
testNumericWrites("IntEnum", LibraryItem)
testNumericWrites("LongEnum", ContentType)
testNumericWrites("ShortEnum", Drinks)
testWrites("StringEnum", OperatingSystem, JsString)

}

describe(".formats") {

testFormats("IntEnum", LibraryItem)
testFormats("LongEnum", ContentType)
testFormats("ShortEnum", Drinks)
testFormats("PlayJsonValueEnum", JsonDrinks, Some(JsonDrinks.format))
testNumericFormats("IntEnum", LibraryItem)
testNumericFormats("LongEnum", ContentType)
testNumericFormats("ShortEnum", Drinks)
testFormats("StringEnum", OperatingSystem, JsString)
testNumericFormats("PlayJsonValueEnum", JsonDrinks, Some(JsonDrinks.format))

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,33 @@ import org.scalatest.OptionValues._
*/
trait EnumJsonFormatHelpers { this: FunSpec with Matchers =>

def testWrites[EntryType <: ValueEnumEntry[ValueType], ValueType <: AnyVal: Numeric: Writes](enumKind: String, enum: ValueEnum[ValueType, EntryType], providedWrites: Option[Writes[EntryType]] = None): Unit = {
def testNumericWrites[EntryType <: ValueEnumEntry[ValueType], ValueType <: AnyVal: Numeric: Writes](enumKind: String, enum: ValueEnum[ValueType, EntryType], providedWrites: Option[Writes[EntryType]] = None): Unit = {
val numeric = implicitly[Numeric[ValueType]]
testWrites(enumKind, enum, { i: ValueType => JsNumber(numeric.toInt(i)) }, providedWrites)
}

def testWrites[EntryType <: ValueEnumEntry[ValueType], ValueType: Writes](enumKind: String, enum: ValueEnum[ValueType, EntryType], jsWrapper: ValueType => JsValue, providedWrites: Option[Writes[EntryType]] = None): Unit = {
val writes = providedWrites.getOrElse(EnumFormats.writes(enum))
describe(enumKind) {
it("should write proper JsValues") {
enum.values.foreach { entry =>
writes.writes(entry) shouldBe JsNumber(numeric.toInt(entry.value))
writes.writes(entry) shouldBe jsWrapper(entry.value)
}
}
}
}

def testReads[EntryType <: ValueEnumEntry[ValueType], ValueType <: AnyVal: Numeric: Reads](enumKind: String, enum: ValueEnum[ValueType, EntryType], providedReads: Option[Reads[EntryType]] = None): Unit = {
def testNumericReads[EntryType <: ValueEnumEntry[ValueType], ValueType <: AnyVal: Numeric: Reads](enumKind: String, enum: ValueEnum[ValueType, EntryType], providedReads: Option[Reads[EntryType]] = None): Unit = {
val numeric = implicitly[Numeric[ValueType]]
testReads(enumKind, enum, { i: ValueType => JsNumber(numeric.toInt(i)) }, providedReads)
}

def testReads[EntryType <: ValueEnumEntry[ValueType], ValueType: Reads](enumKind: String, enum: ValueEnum[ValueType, EntryType], jsWrapper: ValueType => JsValue, providedReads: Option[Reads[EntryType]] = None): Unit = {
val reads = providedReads.getOrElse(EnumFormats.reads(enum))
describe(enumKind) {
it("should read valid values") {
enum.values.foreach { entry =>
reads.reads(JsNumber(numeric.toInt(entry.value))).asOpt.value shouldBe entry
reads.reads(jsWrapper(entry.value)).asOpt.value shouldBe entry
}
}
it("should fail to read with invalid values") {
Expand All @@ -39,10 +47,15 @@ trait EnumJsonFormatHelpers { this: FunSpec with Matchers =>
}
}

def testFormats[EntryType <: ValueEnumEntry[ValueType], ValueType <: AnyVal: Numeric: Reads: Writes](enumKind: String, enum: ValueEnum[ValueType, EntryType], providedFormat: Option[Format[EntryType]] = None): Unit = {
def testNumericFormats[EntryType <: ValueEnumEntry[ValueType], ValueType <: AnyVal: Numeric: Reads: Writes](enumKind: String, enum: ValueEnum[ValueType, EntryType], providedFormat: Option[Format[EntryType]] = None): Unit = {
testNumericReads(enumKind, enum, providedFormat)
testNumericWrites(enumKind, enum, providedFormat)
}

def testFormats[EntryType <: ValueEnumEntry[ValueType], ValueType: Reads: Writes](enumKind: String, enum: ValueEnum[ValueType, EntryType], jsWrapper: ValueType => JsValue, providedFormat: Option[Format[EntryType]] = None): Unit = {
val format = providedFormat.getOrElse(EnumFormats.formats(enum))
testReads(enumKind, enum, Some(format))
testWrites(enumKind, enum, Some(format))
testReads(enumKind, enum, jsWrapper, Some(format))
testWrites(enumKind, enum, jsWrapper, Some(format))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ trait PlayValueEnumHelpers extends EnumJsonFormatHelpers { this: FunSpec with Ma
}

describe("JSON formats") {
testFormats(enumKind, enum, Some(enum.format))
testNumericFormats(enumKind, enum, Some(enum.format))
}

}
Expand Down

0 comments on commit 0dc63b4

Please sign in to comment.