Created
September 26, 2012 09:50
-
-
Save lyricallogical/3787082 to your computer and use it in GitHub Desktop.
rpscala88
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
!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