Sprayの紹介

## Sprayとは

HTTP処理をScalaで書けるOSSで、

- 非同期
- Actorベース
- 速くて軽量
- モジュールで分かれている
- テストしやすい

という特徴がある。

[このようにモジュールが](http://spray.io/documentation/1.2.3/)分離されていて、必要なものをsbtの依存に入れて書くようになっている。

主要なものだけ説明。

## spray-can

HTTPを受け取ってレスポンスするサーバーとしての機能と、HTTPを送るクライアントとしての機能を、低レベルなAPIで提供している。

サーバーとしての簡単な例を見てみる。

```scala:PING/PONGの例
// Actorのメッセージで、HTTPリクエストの情報かが詰まったオブジェクトを受け取る。
// そしてHTTPレスポンスのオブジェクトを、メッセージで返す。
class FooActor extends Actor {
  def receive = {
    case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
      sender ! HttpResponse(entity = "PONG")
  }
}

// Listenする。
val fooActor: ActorRef = ...
IO(Http) ! Http.Bind(fooActor, interface = "localhost", port = 8080)
```

Actorのメッセージでやり取りするので、今までのwebフレームワークと違って新しい感じだ。

ただ、このままだと書きづらいので、書きやすいDSLを提供するのがspray-routingだ。

## spray-routing

spray-canを、より簡単に扱える高レベルAPIを提供するもの。
さっきのPING/PONGをspray-routingで書いてみると、

```scala
object Main extends App with SimpleRoutingApp {
  startServer(interface = "localhost", port = 8080) {
    get { // GETのリクエストで
      path("ping") { // /pingへのリクエストで
        complete("PONG") // PONGをレスポンスする
      }
    }
  }
}
```

とてもシンプルに書けるようになる。

## 様々なspray-routingの機能

さっきの例に出てきた、`get`や`path`、`complete`といったDSLは、**Directive**と呼ばれている。多種多様なDirectiveが用意されていて、うまく組み合わせることでシンプルで直感的な記述が可能になる。

## Directiveの使用例

### TODOアプリの例

```scala
get {
  path("todos" / IntNumber) { id =>
    complete {
      findById(id).map("todo is " + _) // 文字列で返す。Noneなら404。
    }
  }
}

def findById(id: Int): Option[Todo] = ...
```

Optionをcompleteに渡すとうまいこと扱ってくれるのは、`spray-httpx`のmarshallingという機能があるからだ。他にもFutureやTry、Eitherなどうまいこと扱ってくれる。

POSTだとこんな感じ。

```scala
post {
  path("todos") {
    formFields('content) { content =>
      complete(service.create(content))
    }
  }
}
```

Jsonでやり取りする簡単な[TODOアプリ](https://github.com/ryoppy/spray-example-todo)を作ったので参考にどうぞ。

もう一つ例を。

```scala
get {
  path("check") {
    cookie("uid") { uid => // クッキーから値を取り
      validate(uid.length > 0, "uidが不正") { // バリデーションして
        onSuccess(existsByUid(uid)) { // Futureを剥がして
          case true => complete("OK")
          case false => complete("NG") 
        }
      }
    }
  }
}

def existsByUid(uid: String): Future[Boolean] = ...
```

Directiveは[たくさん用意されている](http://spray.io/documentation/1.2.3/spray-routing/predefined-directives-alphabetically/)ので、一通り目を通しておこう。

もちろん自分で定義することもできる。

### カスタムDirective

例えば検索条件を取得するdirectiveを定義してみる。

```scala
case class SearchParams(id: Int, name: Option[String], isDebug: Boolean)

// 値を取得してcase classに詰める。
// cookieにisDebug=1があればtrueに
val searchParams: Directive1[SearchParams] = {
  parameter(('id.as[Int], 'name.?.as[String])) &
  optionalCookie("isDebug")
    .hmap { case id :: name :: isDebugCookie :: HNil =>
      SearchParams(id, name, isDebugCookie.exists(_.content == "1")) 
    }
}
```

Directive1は、値を1つ渡すからDirective`1`だ。

使うときはいつも通り。

```scala
get {
  path("search") {
    searchParams { sp: SearchParams =>
      findByParam(sp)
      ...
    }
  }
}
```

ちなみに、Directive1はforでも書ける。

さっきのをforで書いてみると

```scala
val searchParams: Directive1[SearchParams] = {
  for {
    id <- parameter('id.as[Int])
    name <- parameter('name.as[String])
    isDebugCookie <- optionalCookie("isDebug")
  } yield {
    SearchParams(id, name, isDebugCookie.exists(_.content == "1"))
  }
}
```

こっちのほうが分かりやすいかな?

AND`&`やOR`|`でも繋げれるので工夫次第で色んな書き方がある。

今までのwebフレームワークと全然違っておもしろいですねぇ(´-` )