Janaka Priyadarshana
Janaka Priyadarshana

Reputation: 550

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"
}

Upvotes: 1

Views: 318

Answers (2)

Janaka Priyadarshana
Janaka Priyadarshana

Reputation: 550

Here I found a very good workshop.

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

Upvotes: 0

Gabriele Petronella
Gabriele Petronella

Reputation: 108159

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.

Upvotes: 3

Related Questions