1

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"
}
Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235

2 Answers2

3

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.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • Is this correct...? 1) Directive definition (`Directive[Int :: Int :: HNil]` ) always contain information about parameters it return (extract) 2) And always its input is the HTTP-request (`RequestContext`) – Janaka Priyadarshana Oct 01 '15 at 12:26
0

Here I found a very good workshop.

https://www.youtube.com/watch?v=XPuOlpWEvmw