Skip to content

Commit

Permalink
Merge cf63e36 into ad85d45
Browse files Browse the repository at this point in the history
  • Loading branch information
lloydmeta committed Feb 3, 2015
2 parents ad85d45 + cf63e36 commit 562702e
Show file tree
Hide file tree
Showing 11 changed files with 284 additions and 7 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
sudo: false
language: scala
jdk:
- openjdk6
- openjdk7
- oraclejdk7
- oraclejdk8
scala:
- 2.11.4
- 2.11.5
- 2.10.4
script: "sbt clean coverage test"
after_success: "sbt coveralls"
File renamed without changes.
File renamed without changes.
File renamed without changes.
30 changes: 30 additions & 0 deletions enumeratum-play/src/main/scala/enumeratum/Forms.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package enumeratum

import play.api.data.format.Formatter
import play.api.data.{ FormError, Forms => PlayForms, Mapping }

/**
* Created by Lloyd on 2/3/15.
*/
object Forms {

/**
* Returns an [[Enum]] mapping
*/
def maps[A](enum: Enum[A]): Mapping[A] = PlayForms.of(format(enum))

/**
* Returns a Formatter for [[Enum]]
*/
def format[A](enum: Enum[A]): Formatter[A] = new Formatter[A] {
def bind(key: String, data: Map[String, String]) = {
play.api.data.format.Formats.stringFormat.bind(key, data).right.flatMap { s =>
scala.util.control.Exception.allCatch[A]
.either(enum.withName(s))
.left.map(e => Seq(FormError(key, "error.enum", Nil)))
}
}
def unbind(key: String, value: A) = Map(key -> value.toString)
}

}
42 changes: 42 additions & 0 deletions enumeratum-play/src/main/scala/enumeratum/Json.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package enumeratum

import play.api.libs.json._

import scala.util.control.NonFatal

/**
* Holds JSON reads and writes for [[enumeratum.Enum]]
*/
object Json {

/**
* Returns an Json Reads for a given enum [[Enum]]
*/
def reads[A](enum: Enum[A]): Reads[A] = new Reads[A] {
def reads(json: JsValue): JsResult[A] = json match {
case JsString(s) => {
try {
JsSuccess(enum.withName(s))
} catch {
case NonFatal(e) => JsError(s"Enumeration expected of type: '$enum', but it does not appear to contain the value: '$s'")
}
}
case _ => JsError("String value expected")
}
}

/**
* Returns a Json writes for a given enum [[Enum]]
*/
def writes[A](enum: Enum[A]): Writes[A] = new Writes[A] {
def writes(v: A): JsValue = JsString(v.toString)
}

/**
* Returns a Json format for a given enum [[Enum]]
*/
def enumFormat[A](enum: Enum[A]): Format[A] = {
Format(reads(enum), writes(enum))
}

}
36 changes: 36 additions & 0 deletions enumeratum-play/src/main/scala/enumeratum/UrlBinders.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package enumeratum

import play.api.mvc.PathBindable
import play.api.mvc.QueryStringBindable
import play.api.mvc.QueryStringBindable._

import scala.util.Try

/**
* Created by Lloyd on 2/3/15.
*/
object UrlBinders {

/**
* Builds a [[PathBindable]] A for a given Enum A
*/
def pathBinder[A](enum: Enum[A]): PathBindable[A] = new PathBindable[A] {
def unbind(key: String, value: A): String = value.toString
def bind(key: String, value: String): Either[String, A] = {
Try(enum.withName(value)).toOption match {
case Some(v) => Right(v)
case _ => Left(s"Unknown value supplied for $enum '" + value + "'")
}
}
}

/**
* Builds a [[QueryStringBindable]] A for a given Enum A
*/
def queryBinder[A](enum: Enum[A]): QueryStringBindable[A] = new Parsing[A](
enum.withName,
_.toString,
(key, exception) => "Cannot parse parameter %s as an Enum: %s".format(key, exception.getMessage)
)

}
38 changes: 38 additions & 0 deletions enumeratum-play/src/test/scala/enumeratum/FormSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package enumeratum

import org.scalatest._

/**
* Created by Lloyd on 2/3/15.
*/
class FormSpec extends FunSpec with Matchers {

import Forms._

sealed trait DummyEnum

object DummyEnum extends Enum[DummyEnum] {
case object A extends DummyEnum
case object B extends DummyEnum
val values = findValues
}

describe("binder from #enumFormat") {

val subject = format(DummyEnum)

it("should bind proper strings into an Enum value") {
val r1 = subject.bind("hello", Map("hello" -> "A"))
val r2 = subject.bind("hello", Map("hello" -> "B"))
r1 shouldBe Right(DummyEnum.A)
r2 shouldBe Right(DummyEnum.B)
}

it("should fail to bind random strings") {
val r = subject.bind("hello", Map("hello" -> "AARSE"))
r should be('left)
}

}

}
54 changes: 54 additions & 0 deletions enumeratum-play/src/test/scala/enumeratum/JsonSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package enumeratum

import org.scalatest.{ Matchers, FunSpec }
import play.api.libs.json.{ JsNumber, JsString }

class JsonSpec extends FunSpec with Matchers {

sealed trait DummyEnum
object DummyEnum extends Enum[DummyEnum] {
case object A extends DummyEnum
case object B extends DummyEnum
case object C extends DummyEnum
val values = findValues
}

describe(".enumReads") {
val reads = Json.reads(DummyEnum)

it("should create a reads that works with valid values") {
reads.reads(JsString("A")).get should be(DummyEnum.A)
}

it("should create a reads that fails with invalid values") {
reads.reads(JsString("D")).isError should be(true)
reads.reads(JsNumber(2)).isError should be(true)
}
}

describe(".enumWrites") {
val writer = Json.writes(DummyEnum)

it("should create a writes that writes enum values to JsString") {
writer.writes(DummyEnum.A) should be(JsString("A"))
}
}

describe(".enumFormat") {
val format = Json.enumFormat(DummyEnum)

it("should create a format that works with valid values") {
format.reads(JsString("A")).get should be(DummyEnum.A)
}

it("should create a format that fails with invalid values") {
format.reads(JsString("D")).isError should be(true)
format.reads(JsNumber(2)).isError should be(true)
}

it("should create a format that writes enum values to JsString") {
format.writes(DummyEnum.A) should be(JsString("A"))
}
}

}
58 changes: 58 additions & 0 deletions enumeratum-play/src/test/scala/enumeratum/UrlBindersSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package enumeratum

import UrlBinders._
import org.scalatest._

/**
* Created by Lloyd on 2/3/15.
*/
class UrlBindersSpec extends FunSpec with Matchers {

sealed trait Dummy
object Dummy extends Enum[Dummy] {
case object A extends Dummy
case object B extends Dummy
case object C extends Dummy
val values = findValues
}

describe(".pathBinder") {

val subject = pathBinder(Dummy)

it("should create an enumeration binder that can bind strings corresponding to enum strings") {
subject.bind("hello", "A").right.get shouldBe Dummy.A
}

it("should create an enumeration binder that cannot bind strings not found in the enumeration") {
subject.bind("hello", "Z").isLeft shouldBe true
}

it("should create an enumeration binder that can unbind values") {
subject.unbind("hello", Dummy.A) shouldBe "A"
subject.unbind("hello", Dummy.B) shouldBe "B"
}

}

describe(".queryBinder") {

val subject = queryBinder(Dummy)

it("should create an enumeration binder that can bind strings corresponding to enum strings regardless of case") {
subject.bind("hello", Map("hello" -> Seq("A"))).get.right.get should be(Dummy.A)
}

it("should create an enumeration binder that cannot bind strings not found in the enumeration") {
subject.bind("hello", Map("hello" -> Seq("Z"))).get should be('left)
subject.bind("hello", Map("helloz" -> Seq("A"))) shouldBe None
}

it("should create an enumeration binder that can unbind values") {
subject.unbind("hello", Dummy.A) should be("hello=A")
subject.unbind("hello", Dummy.B) should be("hello=B")
}

}

}
30 changes: 24 additions & 6 deletions project/Enumeratum.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,31 @@ import scoverage.ScoverageSbtPlugin.ScoverageKeys._

object Enumeratum extends Build {

lazy val theVersion = "0.0.4"
lazy val theScalaVersion = "2.11.4"
lazy val theVersion = "0.0.5"
lazy val theScalaVersion = "2.11.5"

lazy val root = Project(id = "enumeratum", base = file("."), settings = commonWithPublishSettings)
lazy val root = Project(id = "enumeratum-root", base = file("."), settings = commonWithPublishSettings)
.settings(
name := "enumeratum-root",
publishArtifact := false,
crossScalaVersions := Seq("2.10.4", "2.11.5"),
crossVersion := CrossVersion.binary
).aggregate(macros, core, enumeratumPlay)

lazy val core = Project(id = "enumeratum", base = file("enumeratum-core"), settings = commonWithPublishSettings)
.settings(
name := "enumeratum",
crossScalaVersions := Seq("2.10.4", "2.11.4"),
crossScalaVersions := Seq("2.10.4", "2.11.5"),
crossVersion := CrossVersion.binary,
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "2.2.1" % "test"
)
).dependsOn(macros).aggregate(macros)
).dependsOn(macros)

lazy val macros = Project(id = "enumeratum-macros", base = file("macros"), settings = commonWithPublishSettings)
.settings(
name := "enumeratum-macros",
crossScalaVersions := Seq("2.10.4", "2.11.4"),
crossScalaVersions := Seq("2.10.4", "2.11.5"),
crossVersion := CrossVersion.binary,
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
Expand All @@ -41,6 +49,16 @@ object Enumeratum extends Build {
additionalMacroDeps }
)

lazy val enumeratumPlay = Project(id = "enumeratum-play", base = file("enumeratum-play"), settings = commonWithPublishSettings)
.settings(
crossScalaVersions := Seq("2.10.4", "2.11.5"),
crossVersion := CrossVersion.binary,
libraryDependencies ++= Seq(
"com.typesafe.play" %% "play" % "2.3.7" % "provided",
"org.scalatest" %% "scalatest" % "2.2.1" % "test"
)
).dependsOn(core)


lazy val commonSettings = Seq(
organization := "com.beachape",
Expand Down

0 comments on commit 562702e

Please sign in to comment.