Skip to content

Commit

Permalink
Merge pull request #23 from lloydmeta/feature/scala-js-attempt-2
Browse files Browse the repository at this point in the history
ScalaJS
  • Loading branch information
lloydmeta committed Dec 12, 2015
2 parents 14fe07f + e14ae00 commit 1ffd694
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 70 deletions.
23 changes: 20 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@ language: scala
jdk:
- oraclejdk8
scala:
- 2.11.6
script: "sbt clean coverage +test"
after_success: "sbt coveralls"
- 2.11.7
script:
- sbt clean +test:compile
- sbt +test
# Manually select the JVM projects to run coverage on until Scoverage is compatible with ScalaJS
- sbt coverage coreJVM/test enumeratum-play/test enumeratum-play-json/test
after_success:
- sbt coverageReport coverageAggregate coveralls
cache:
directories:
- $HOME/.sbt/0.13
- $HOME/.sbt/boot/scala*
- $HOME/.sbt/cache
- $HOME/.sbt/launchers
- $HOME/.ivy2
before_cache:
- du -h -d 1 $HOME/.ivy2/
- du -h -d 2 $HOME/.sbt/
- find $HOME/.sbt -name "*.lock" -type f -delete
- find $HOME/.ivy2/cache -name "ivydata-*.properties" -type f -delete
40 changes: 35 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,56 @@ Compatible with Scala 2.10.x and 2.11.x

## SBT

Set the Enumeratum version in a variable (for the latest version, use `val enumeratumVersion = "1.3.3"`).

For basic enumeratum (with no Play support):
```scala
libraryDependencies ++= Seq(
"com.beachape" %% "enumeratum" % "1.3.3"
"com.beachape" %% "enumeratum" % enumeratumVersion
)
```

For enumeratum with [uPickle](http://lihaoyi.github.io/upickle/):

```scala
libraryDependencies ++= Seq(
"com.beachape" %% "enumeratum" % enumeratumVersion,
"com.beachape" %% "enumeratum-upickle" % enumeratumVersion
)
```

For enumeratum with Play JSON:
```scala
libraryDependencies ++= Seq(
"com.beachape" %% "enumeratum" % "1.3.3",
"com.beachape" %% "enumeratum-play-json" % "1.3.3"
"com.beachape" %% "enumeratum" % enumeratumVersion,
"com.beachape" %% "enumeratum-play-json" % enumeratumVersion
)
```

For enumeratum with full Play support:
```scala
libraryDependencies ++= Seq(
"com.beachape" %% "enumeratum" % "1.3.3",
"com.beachape" %% "enumeratum-play" % "1.3.3"
"com.beachape" %% "enumeratum" % enumeratumVersion,
"com.beachape" %% "enumeratum-play" % enumeratumVersion
)
```

### ScalaJs

There is support for ScalaJs, though only for the core lib and the UPickle helper lib.

```scala
libraryDependencies ++= Seq(
"com.beachape" %%% "enumeratum" % enumeratumVersion
)
```

To use with uPickle:

```scala
libraryDependencies ++= Seq(
"com.beachape" %%% "enumeratum" % enumeratumVersion,
"com.beachape" %%% "enumeratum-upickle" % enumeratumVersion
)
```

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package enumeratum

import org.scalatest.{ FunSpec, Matchers }

class EnumJVMSpec extends FunSpec with Matchers {

describe("findValues Vector") {

// This is a fairly intense test.
it("should be in the same order that the objects were declared in") {
import scala.util._
(1 to 100).foreach { i =>
val members = Random.shuffle((1 to Random.nextInt(20)).map { m => s"Member$m" })
val membersDefs = members.map { m => s"case object $m extends Enum$i" }.mkString("\n\n")
val objDefinition =
s"""
import enumeratum._
sealed trait Enum$i extends EnumEntry

case object Enum$i extends Enum[Enum$i] {
$membersDefs
val values = findValues
}

Enum$i
"""
val obj = Eval.apply[Enum[_ <: EnumEntry]](objDefinition)
obj.values.map(_.entryName) shouldBe members
}
}

}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package enumeratum

import scala.tools.reflect.ToolBox
import java.io.File

/**
* Eval with bits and pieces stolen from here and there...
Expand All @@ -20,7 +19,7 @@ object Eval {

def macroToolboxClassPath = {
val paths = Seq(
new java.io.File(s"macros/target/scala-${scalaBinaryVersion}/classes")
new java.io.File(s"macros/.jvm/target/scala-$scalaBinaryVersion/classes")
)
paths.foreach { p =>
if (!p.exists) sys.error(s"output directory ${p.getAbsolutePath} does not exist.")
Expand Down
29 changes: 1 addition & 28 deletions enumeratum-core/src/test/scala/enumeratum/EnumSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class EnumSpec extends FunSpec with Matchers {
describe("no values") {

it("should result in findValues finding nothing") {
EmptyEnum.values shouldBe 'empty
EmptyEnum.values.size shouldBe 0
}

}
Expand Down Expand Up @@ -208,33 +208,6 @@ class EnumSpec extends FunSpec with Matchers {

}

describe("findValues Vector") {

// This is a fairly intense test.
it("should be in the same order that the objects were declared in") {
import scala.util._
(1 to 100).foreach { i =>
val members = Random.shuffle((1 to Random.nextInt(20)).map { m => s"Member$m" })
val membersDefs = members.map { m => s"case object $m extends Enum$i" }.mkString("\n\n")
val objDefinition =
s"""
import enumeratum._
sealed trait Enum$i extends EnumEntry

case object Enum$i extends Enum[Enum$i] {
$membersDefs
val values = findValues
}

Enum$i
"""
val obj = Eval[Enum[_ <: EnumEntry]](objDefinition)
obj.values.map(_.entryName) shouldBe members
}
}

}

describe("trying to use with improper types") {

it("should fail to compile for unsealed traits") {
Expand Down
15 changes: 15 additions & 0 deletions enumeratum-upickle/src/main/scala/enumeratum/UPickleEnum.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package enumeratum

import upickle.default.Aliases.RW
import upickle.default.ReadWriter

/**
* Enum mix-in with default Reader and Writers defined (case sensitive)
*/
trait UPickleEnum[A <: EnumEntry] { self: Enum[A] =>

import UPickler._

implicit val uPickleReadWriter: RW[A] = ReadWriter(writer(this).write, reader(this, false).read)

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

import upickle.Js
import upickle.default.{ Writer, Reader }

object UPickler {

/**
* Returns a UPickle [[Reader]] for a given [[Enum]]
*
* @param enum the enum you wish to make a Reader for
* @param insensitive whether or not to match case-insensitively
*/
def reader[A <: EnumEntry](enum: Enum[A], insensitive: Boolean = false): Reader[A] = {
Reader[A] {
val memberFinder: String => Option[A] = if (insensitive) enum.withNameInsensitiveOption else enum.withNameOption
val pfIfJsStr: PartialFunction[Js.Value, String] = {
case Js.Str(s) => s
}
val pfMaybeMember = pfIfJsStr.andThen(memberFinder)
val pfMaybeMemberToMember: PartialFunction[Option[A], A] = {
case Some(a) => a
}
andThenPartial(pfMaybeMember, pfMaybeMemberToMember)
}
}

/**
* Returns a [[Writer]] for a given [[Enum]]
*
* @param enum [[Enum]] to make a [[Writer]] for
*/
def writer[A <: EnumEntry](enum: Enum[A]): Writer[A] = Writer[A] {
case member => Js.Str(member.toString)
}

/**
* Private helper for composing PartialFunctions
*
* Stolen from http://stackoverflow.com/questions/23024626/compose-partial-functions
*/
private def andThenPartial[A, B, C](pf1: PartialFunction[A, B], pf2: PartialFunction[B, C]): PartialFunction[A, C] = {
Function.unlift(pf1.lift(_) flatMap pf2.lift)
}

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

/**
* Created by Lloyd on 2/4/15.
*/
sealed trait Dummy extends EnumEntry
object Dummy extends Enum[Dummy] with UPickleEnum[Dummy] {
case object A extends Dummy
case object B extends Dummy
case object C extends Dummy
val values = findValues
}

object D extends Dummy
57 changes: 57 additions & 0 deletions enumeratum-upickle/src/test/scala/enumeratum/UPickleSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package enumeratum

import org.scalatest._
import upickle.Js

/**
* Created by Lloyd on 12/12/15.
*/
class UPickleSpec extends FunSpec with Matchers {

import Dummy._

describe("Reader") {

val reader = UPickler.reader(Dummy)

it("should work with valid values") {
reader.read(Js.Str("A")) shouldBe A
}

it("should fail with invalid values") {
intercept[Exception] {
reader.read(Js.Str("D"))
}
intercept[Exception] {
reader.read(Js.Num(2))
}
}

}

describe("insensitive reader") {
val reader = UPickler.reader(Dummy, true)

it("should work with strings, disgregarding case") {
reader.read(Js.Str("A")) shouldBe A
reader.read(Js.Str("a")) shouldBe A
}

it("should work with invalid values") {
intercept[Exception](reader.read(Js.Str("D")))
intercept[Exception](reader.read(Js.Num(5)))
}

}

describe("Writer") {

val writer = UPickler.writer(Dummy)

it("should write enum values to JsString") {
writer.write(A) shouldBe Js.Str("A")
}

}

}
Loading

0 comments on commit 1ffd694

Please sign in to comment.