Skip to content

Instantly share code, notes, and snippets.

@lyricallogical
Created September 26, 2012 09:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lyricallogical/3787082 to your computer and use it in GitHub Desktop.
Save lyricallogical/3787082 to your computer and use it in GitHub Desktop.
rpscala88
!SLIDE
Play 2.0 の Action と BodyParser について学ぼう
----------------
!SLIDE
自己紹介
----------------
* 望月 慎也
* @lyrical_logical
* 株式会社レピダム
* scala 仕事で使ってる(ワー!)
!SLIDE
Play 2.0 とは
----------------
* Full-stack(?) web application framework
!SLIDE
web application とは
----------------
* Http Request にたいして適切に Http Response を返すもの
!SLIDE
Http Request とは
---------------
* RFC2616 Table 5 を見ましょう
* では酷いので…
* ざっくりいうと Request Header + Message Body(optional)
!SLIDE
Request Header とは
--------------
* ざっくり以下のようなものから構成される
* メソッド(GET とか POST とか PUT とか DELETE とか)
* Request URI
* その他諸々(Content-Length とか Conent-Type とか)
!SLIDE
Message Body とは
--------------
* 色々
* 結局のところバイト列で表現される(重要)
!SLIDE
Http Response とは
---------------
* RFC2616 Table 6 を見ましょう
* では酷いので…
* ざっくりいうと Response Header + Message Body(optional)
!SLIDE
Response Header とは
--------------
* ざっくり以下のようなものから構成される
* Status Code (200 Ok, 400 Bad Request, 404 NotFound)
* その他諸々(Content-Length とか Conent-Type とか)
!SLIDE
Message Body とは
--------------
* Request と同じ
* 色々
* 結局のところバイト列で表現される(重要)
!SLIDE
web application とは(おさらい)
----------------
* Http Request にたいし適切に Http Response を返すもの
!SLIDE
Play 2.0 では
--------------
* Routes + Handler
!SLIDE
Routes とは
--------------
* conf/routes に書いてるもの
* から生成される Scala コードで定義される
* Request Header を処理するための Handler を取り出す
!SLIDE
Handler とは
--------------
* 実はなんか色々ある…
* けど基本的には Action のことだと思っていいです
!SLIDE
Play 2.0 では(revised)
--------------
* Routes + Action(Handler)
!SLIDE
Action とは(本質編)
--------------
* 「本質的には」 RequestHeader を取り Iteratee[Array[Byte], Result] を返す関数
* type Result = Http Result と思っていいです
* 「本質的な」Action は EssentialAction として定義されている(2.1 をお待ちください)
```scala
trait EssentialAction
extends (RequestHeader => Iteratee[Array[Byte], Result])
with Handler { ... }
```
!SLIDE
Iteratee とは
--------------
* 簡単にいうと入力列をメモリ効率を良く処理するための仕組み
* Iteratee[Array[Byte], Result] は Array[Byte] => Result みたいなもの
!SLIDE
EssentialAction とは
--------------
* 以上を踏まえて、
* type EssentialAction = RequestHeader => Array[Byte] => Result みたいなもの
* Array[Byte] は Message Body
* "結局のところバイト列で表現される(重要)"を思い出そう
!SLIDE
ところで
--------------
* Array[Byte] なんて扱いたくないよ
* 本当に扱いたいもの
* plain text, json, xml, url-form encoded data, multipart-form data, etc...
!SLIDE
Action とは(実体編)
--------------
* BodyParser + (Request[A] => Result)
* A は前述の「本当に扱いたいもの」に相当
* 後者が、framework を利用する人間が普段書く部分!
!SLIDE
BodyParser とは
--------------
* Message Body を parse して扱いやすい型 A に変換するもの
* A は前述の「本当に扱いたいもの」に相当
```scala
trait BodyParser[+A]
extends (RequestHeader => Iteratee[Array[Byte], Either[Result, A]]) { ... }
```
* type BodyParser[A] = RequestHeader => Array[Byte] => Either[Result, A] みたいなもの
* Either なのは、parse に失敗する可能性があるため
* RequestHeader を取るのは、parse に必要な値が含まれていたりするため
* Validation にも使ったりする
!SLIDE
Action は EssentialAction 足り得るか?前編
--------------
* type BodyParser[A] = RequestHeader => Array[Byte] => Either[Result, A]
* type RequestGen[A] = A => Request[A]
* どこかから降ってくると仮定する…!
* type Process[A] = Request[A] => Result
* framework を利用する人間が書く部分!
!SLIDE
Action は EssentialAction 足り得るか?後編
--------------
```scala
def essential[A](parser: BodyParser,
gen: A => Request[A],
process: Request[A] => Result) =
parser.andThen {
case Right(a) => process(gen(a))
case Left(result) => result
}
```
* できました!
!SLIDE
第二部完!
--------------
* 本当に?
!SLIDE
Array[Byte] は Request だけじゃない…
--------------
* 当然 Response にも Body Message がある
* Array[Byte] なんてわざわざ作りたくないよ
* html, css, json, xml, etc...
!SLIDE
Writeable とは
--------------
* typo だよなこれ(いわない約束)
* Array[Byte] に変換可能であることを示す type class
* type class = implicit parameter + implicit definitions
```scala
case class Writeable[-A](transform: (A => Array[Byte]))
```
* こいつがよしなにやってくれてる
* やってくれなかったら自分で定義しましょう
!SLIDE
まとめ
--------------
* Play 2.0 のしてること
* RequestHeaeder から Routs が適切な Action を取り、
* BodyParser が Request の Body Message を parse し、
* 「framework の利用者が書いた処理が」適切な Result を返す
* 利用者は本質的な処理の記述に集中することができる!
!SLIDE
* 以下蛇足
!SLIDE
非情な現実
--------------
* 本当に本質的な処理の記述に集中できるの?
* デフォルトで提供されている BodyParser があまりカスタマイザブルではない
* Validation とか BodyParser 側でやろうとすると面倒
* Iteratee が分かってないといけないので難易度が高い…
* 結局 tolerantText 使って Action[String] を書いちゃってるなんてこと、ありませんか?
!SLIDE
Iteratee って本当に便利なの?
--------------
* "入力列をメモリ効率を良く処理するための仕組み" と書きました。
* 現実をお見せしましょう
!SLIDE
Enumerator 側
--------------
* Enumerator は Iteratee に入力を与えるものです
```scala
lazy val bodyEnumerator = {
val body: Array[Byte] = { ... }
Enumerator(body).andThen(Enumerator.enumInput(EOF))
}
```
* body をそのまんま一つの Array[Byte] にしてる…
* 細切れに Iteratee に入力を与えないとメモリ効率はよくならない…
!SLIDE
Iteratee 側
--------------
* 多分一番よく使われているであろう tolerantText を
```scala
def tolerantText(maxLength: Int): BodyParser[String] =
BodyParser("text, maxLength=" + maxLength) { request =>
Traversable.takeUpTo[Array[Byte]](maxLength)
.transform(Iteratee.consume[Array[Byte]]().map { c =>
new String(c, request.charset.getOrElse("utf-8")))
}.flatMap(Iteratee.eofOrElse(Results.EntityTooLarge))
}
```
* Iteratee.consume は入力列をとれるだけ取る
* 細切れに入力列を処理しないとメモリ効率はよくならない…
!SLIDE
結論
--------------
* 今のところ Play 2.0 では Iteratee は有効に活用されているとは言いがたい
* WebSocket とか色々あるけど…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment