[[ネタ記録庫/Scala]] * 概要 [#n4560753] ScalaにはHTTPインタラクションを簡単に扱うためのDispatchライブラリというのがあります。 - http://dispatch.databinder.net/Dispatch.html - Scaladoc: http://databinder.net/dispatch-doc/#package このライブラリを使えば非常に小さなコードでHTTPアクセスを実現できますが、implicit definitionやなぞの演算子や記号クラスを使っているために少し慣れが必要になります。 また、単純な通信だけでなく、twitter,google,oauthなどの便利機能も備えています。(2011/07/03時点(v 0.8.3)) * セットアップ [#ic328084] [[ネタ記録庫/Scala/dispatch/セットアップ]] *コード例 [#e7c01641] ** Bitly API を利用してURLを短縮する [#oabe135a] ** Httpレスポンスを一行ずつ標準出力に書き出すプログラム(main含む) [#efedecdf] import dispatch.Http import dispatch.Request import scala.io.Source object HelloDispatch { def main(args: Array[String]) : Unit = { wget("http://proofcafe.org/index.html") { source => val lines = source.getLines() lines.foreach(println) } } def wget[T](uri : String)(callback : Source => T) : T = { val http = new Http() val req = new Request(uri) http(req >~ callback) } } ソースコードおよびbuild.sbtはこちら https://github.com/yoshihiro503/hello_dispatch 実行 $ sbt run ** Bitly API を利用して短縮URLを生成する関数 [#oabe135a] 使用するモジュールは以下の二つです。 - dispatch-http - dispatch-http-json project/build.scalaはこんな感じ。 ... libraryDependencies ++= Seq ( ... "net.databinder" %% "dispatch-http" % "0.8.5", "net.databinder" %% "dispatch-http-json" % "0.8.5" ) ... shorten関数の型は String => String として定義。つまりurlを文字列で渡すと短縮urlを文字列で返す。以下のように非常に小さいコードで実現できます。しかし慣れないと意味がわからないので以下に解説します。IDとKEYのところにはBitlyアカウントのそれを当てはめましょう。 import dispatch._ import dispatch.json.JsHttp._ def shorten(url: String): String = { val http = new Http() val req = :/("api.bitly.com")/"v3"/("shorten?login=[ID]&apiKey=[KEY]&longUrl="+url+"&format=json") http(req ># 'data ? ('url ? str)) } *** 解説 [#c0402e48] 使用している記号とメソッドの対応 : :/(...) | dispatcy.:/.apply : String => Request : / | RequestVerbs#/ : String => Request : ># : JsHandlers#># : JsHttp.JsF[T] => Handler[T] : ? : JsHttp.SymOp#? : Extract[T] => (implicit Option[Obj]) => Child[T, Property[T]] : ># | JsHandlers#># : JsHttp.JsF[T] => Handler[T] : ? | JsHttp.SymOp#? : Extract[T] => (implicit Option[Obj]) => Child[T, Property[T]] 注意点 - Requestクラスのオブジェクトは暗黙の変換Request.toRequestVerbsによってRequestVerbs型に変換されて、/メソッドが呼ばれている。 - Requestクラスのオブジェクトは暗黙の変換JsHttp.requestToJsHandlersによって JsHandlers型に変換されてから >#メソッドが呼ばれている。 - JsHttpオブジェクトで定義されているimplicit defによって、Request型はJsHandlers型に変換される - ラベル(シングルクォートで始まるリテラル)はscalaのシンボルでSymbol型を持つ - Symbol型のオブジェクトは暗黙の変換JsHttp.sym_add_operatorsによってJsHttp.SymOp型に変換される - JsHttp.SymOp#?メソッドは第二引数引数として暗黙のOption[Obj]を取るが、JsHttp.ctxが渡されている。 - strはJsHttpで定義されている。 JsHttp.str : Extract[String] - ># メソッドの引数として渡しているオブジェクトははChild[String, Property[String]] 型の値だが、暗黙の変換JsHttp.ext2funによってJsHttp.JsF[String]へ変換されてから>#に渡されている。そして>#の適用後の型はHandler[String]型となる。 - Http#apply : Handler[T] => Http.HttpPackage[T] なので最終的な戻り値の値はHttp.HttpPackage[String]型つまりString型になる。 - GAE(google appengine)で使用する場合はnew Http()ではなくnew gae.Http()を使う 以上をふまえて、暗黙の変換やimportなどをいっさい省略せずに使わずに同じコードを書くと以下のようになる。 import dispatch.Http import dispatch.:/ import dispatch.Request import dispatch.json.JsHandlers import dispatch.json.JsHttp def shorten(url: String): String = { val http = new Http() val req : Request = Request.toRequestVerbs( Request.toRequestVerbs((:/.apply("api.bitly.com")))./("v3") )./("shorten?login=[ID]&apiKey=[KEY]&longUrl="+url+"&format=json") http.apply( JsHttp.requestToJsHandlers(req).>#( JsHttp.ext2fun( JsHttp.sym_add_operators('data).?( JsHttp.sym_add_operators('url).?(JsHttp.str)(JsHttp.ctx) )(JsHttp.ctx) ) ) ) }