scalasprayspray-routing

Read and understand the spray-routing DSL


I am new to spray. I have work with several strange (for me) programming languages like python, JQuery, etc... With them I could at least understand what some code segment do. Unfortunately with Spray, I can not read and understand even a simple code.

Could some one please help me to read (describe with word, what the code do) the following simple code block.

Note: Very high level I know, this will pick the url parameter and add them together. But what I want is, understand this code block crystal clearly, as I could teach to someone else. HNil, Directive1, Directive1, :: are some what strange for me.

val twoIntParameters: Directive[Int :: Int :: HNil] =
  parameters('a.as[Int], 'b.as[Int])

val myDirective: Directive1[String] =
  twoIntParameters.hmap {
    case a :: b :: HNil => (a + b).toString
  }

// test `myDirective` using the testkit DSL
Get("/?a=2&b=5") ~> myDirective(x => complete(x)) ~> check {
  responseAs[String] === "7"
}

Solution

  • spray-routing is build around the concept of Directive.

    You can think of a Directive as a transformation over an HTTP request.

    The cardinality associated with a directive is the number of arguments is passes down the transform chain after performing the transformation.

    Directive0 is a directive that doesn't provide (or extract) any argument.

    Directive1[A] provides one argument of type A.

    Directive[A :: B :: HNil] provides 2 arguments of types A and B, or - to be more precise - provides an heterogeneous list made of A and B (the implementation is a shapeless's HList).

    Let's take the examples in your code

    val twoIntParameters: Directive[Int :: Int :: HNil] =
      parameters('a.as[Int], 'b.as[Int])
    

    You're defining a new directive that extracts two integers from the HTTP request, i.e. has the type Directive[Int :: Int :: HNil]. The implementation simply leverages a directive provided already by spray, i.e. parameters. parameters is a directive that allows to extract the query parameters from a HTTP request and convert them to a specific type, in this case Int for both parameters.

    val myDirective: Directive1[String] =
      twoIntParameters.hmap {
        case a :: b :: HNil => (a + b).toString
      }
    

    myDirective is a directive that extracts one parameter of type String.

    Its implementation uses the previously defined twoIntParameters directive and maps over its result, applying a transformation to it. In this case we're taking the two Int, summing them and turning the result into a String.

    So, what's with the hmap? That's just a way provided by spray of working with Directives that return a shapeless HList. hmap requires a function that HList to anything, in this case a String.

    HLists can be pattern matched over, just like a normal scala List, and that's what you're seeing in the example.

    Finally, this is just an idea of how directives work from a functional point of view. If you want to understand the fine details of the DSL syntax, you will have to dig a little further and read about the Magnet Pattern.