Enums for scala

Scala has very limited implementation of Enumeration. Enumerated objects can’t extends other classes. Partial replacement for it is to use sealed classes. You can do pattern matching on them. When you ommit some possible value you will get compiler wa…

Scala has very limited implementation of Enumeration. Enumerated objects can’t extends other classes. Partial replacement for it is to use sealed classes. You can do pattern matching on them. When you ommit some possible value you will get compiler warning for not exhaustive pattern matching. One missing feature is that you can’t get sorted values of all objects extending them. You can simple got it using my (40-lines) EnumOf class from scala-enum. Examples below.

Declaration

sealed abstract class Color(red: Double, green: Double, blue: Double)

object Color extends EnumOf[Color] {
  case object Red   extends Color(1, 0, 0)
  case object Green extends Color(0, 1, 0)
  case object Blue  extends Color(0, 0, 1)
  case object White extends Color(0, 0, 0)
  case object Black extends Color(1, 1, 1)
}

Usage

Color.values shouldEqual List(Red, Green, Blue, White, Black)

Color.valueOfOpt("Blue").value shouldEqual Blue
Color.valueOfOpt("NotExisiting").isEmpty shouldBe true

You can also enumerate on objects nested in instances

Declaration

case class DistanceFrom(srcCity: String, srcCoordinates: Coordinate) extends EnumOf[DistanceBetween] {

    case object ToBerlin extends DistanceFromSrcCityTo("Berlin", Coordinate(52.5075419, 13.4251364))
    case object ToNewYork extends DistanceFromSrcCityTo("New York", Coordinate(40.7033127, -73.979681))
    abstract class DistanceFromSrcCityTo(val destCity: String, val destCoordinates: Coordinate) extends DistanceBetween {
        override def srcCoordinates: Coordinate = DistanceFrom.this.srcCoordinates
    } 
}

sealed abstract class DistanceBetween {
    def srcCoordinates: Coordinate
    def destCity: String
    def destCoordinates: Coordinate
    def inKm: Int = Coordinate.distanceInKm(srcCoordinates, destCoordinates).toInt
}

Usage

val DistanceFromWarsaw = DistanceFrom("Warsaw", Coordinate(52.232938, 21.0611941))
DistanceFromWarsaw.ToBerlin.inKm shouldEqual 519
DistanceFromWarsaw.ToNewYork.inKm shouldEqual 6856
DistanceFromWarsaw.values.map(_.inKm) shouldEqual List(519, 6856)
You May Also Like

Grails with Spock unit test + IntelliJ IDEA = No thread-bound request found

During my work with Grails project using Spock test in IntelliJ IDEA I've encountered this error:

java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.codehaus.groovy.grails.plugins.web.api.CommonWebApi.currentRequestAttributes(CommonWebApi.java:205)
at org.codehaus.groovy.grails.plugins.web.api.CommonWebApi.getParams(CommonWebApi.java:65)
... // and few more lines of stacktrace ;)

It occurred when I tried to debug one of test from IDEA level. What is interesting, this error does not happen when I'm running all test using grails test-app for instance.

So what was the issue? With little of reading and tip from Tomek Kalkosiński (http://refaktor.blogspot.com/) it turned out that our test was missing @TestFor annotation and adding it solved all problems.

This annotation, according to Grails docs (link), indicates Spock what class is being tested and implicitly creates field with given type in test class. It is somehow strange as problematic test had explicitly and "manually" created field with proper controller type. Maybe there is a problem with mocking servlet requests?