PlayFrameWork2.3.4 for Scalaインストールと仕様の覚書
半年振りぐらいにScalaに触った
今まで特別,必要に迫られてなかったのでScalaとかJVMはいじってなかった.
しかし,まともなサイトを作る必要性が少し出てきたので,PlayFrameWorkの勉強というかセットアップをちょろっとした.
サイトの構成要素として,重要なのはPlayFrameworkが読めるテンプレートとCSSとロジックなので,いい感じにデザイナとプログラマが分業できるものだと期待している.
ひとサイトだけ,まともに作って,実績にしてしまおうと画策している.
とりあえず,Flaskとかその辺のレベル程度までは理解した.
PlayFrameworkはできることが多い.FutureとかAkkaとかまでは今回は手が回らなかった.
何度かこのページを更新して言って,PlayFrameworkの全体を記せればよいかと思う.最初のこの一回はチラシの裏なので,期待しないでほしい.
Ubuntuにインストール
sudo apt-get install scala wget http://dl.bintray.com/sbt/debian/sbt-0.13.5.deb sudo dpkg -i sbt-0.13.5.deb sudo apt-get update sudo apt-get install sbt OException: Cannot run program "javac": java.io.IOException: error=2, No such file or directory sudo add-apt-repository ppa:webupd8team/java sudo apt-get update sudo apt-get -y install oracle-java8-installer sudo apt-get -y install oracle-java8-set-default Error reading API from class file : java.lang.UnsupportedClassVersionError: OpenJDKとOracleのJDKが混じったよくない状態. どっちかに寄せるべき.ちなみに,Oracleに寄せた.
プロジェクトの作成
activator new
プロジェクトのディレクトリの中でsbtを打つ
cd hooProject sbt
プロジェクトのコンパイル
sbtのコンソールの中で compile
sbtのコンソールの中で run
クリーン
sbtのコンソールの中で clean
requestの基本的な受付
app/controllersの中にApplication.scalaがある
def index = Action { //デフォルトのテンプレートエンジンではじめる. Ok(views.html.index("Your new application is ready.")) }
requestを確認してみる
def index = Action { req => Ok("Hello request = " + req) }
結果
Hello request = GET /
こんな書き方もできる.
def index = Action { req => Ok(<h1>Hello World</h1>).as(HTML) }
Cookieの発行
def index = Action { req => Ok("Hello World").withCookies(Cookie("theme", "blue")) }
Sessionを張る
def index = Action { req => Ok("Hello request = " + req).withSession( req.session + ("connected" -> "sample@gmail.com") ) } def sessionCheck = Action { req => req.session.get("connected").map{ u => Ok("Hello " + u) }.getOrElse{ Ok("You didn't connected") } }
Actionのtraitの実装
Actionの実装はジェネリックのtraitである.
//抽象度高い.Bodyのバッファリングのロジックが3種類ほどあるかららしい. trait Action[A] extends (Request[A] => Result) { def parser: BodyParser[A] } trait Request[+A] extends RequestHeader { def body: A }
Actionのジェネリックを指定することができる
def save = Action(parse.text) { request => Ok("Got: " + request.body) }
parse.tolerantTextって何でしょうね.
def save = Action(parse.tolerantText) { request => Ok("Got: " + request.body) }
また,parserの機能の再定義をすることもできる.
Actionの戻り値Result型がとりうる値
HTTP ルータ,HTTPルーティング
flaskのアノテーションっぽいものだと理解
PlayFrameWork使ったこと無かったけど,わかりやすい.
動的パスの設定.パス自体を型付の値(Int, Stringなど)として処理可能である.
以下のURLをルーティングするにはconfファイルを編集する必要がある.
http://localhost:9000/clients/1000
# Home page GET / controllers.Application.index # Map static resources from the /public folder to the /assets URL path GET /assets/*file controllers.Assets.at(path="/public", file) # 追加Clinetsクラスとshowメソッドが必要 GET /clients/:id controllers.Clients.show(id: Long)
Hello id = 1000
DBっぽいページのアクセスの仕方
def show(page: String) = Action { loadContentFromDatabase(page).map { htmlContent => Ok(htmlContent).as("text/html") }.getOrElse(NotFound) }
GETメソッドをルーティングの設定の引数にする.Option型でラップすることで省略可能.
# Pagination links, like /clients?page=3 GET /clients controllers.Clients.list(page: Int ?= 1)
Actionの合成
Actionを合成して独自のActionを実装できる. ActionBuilderを大本としており,テンプレみたいな形で利用する見たい.
import play.api.mvc._ object LoggingAction extends ActionBuilder[Request] { //ここはいじれないんだよね? def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[SimpleResult]) = { //ロジック記述できる Logger.info("Calling action") block(request) } } //こんな風に使える def index = LoggingAction { Ok("Hello World") }
もっといろいろ合成があり,いろいろできるが,割愛する.(重要なところなので,跡でpracticeを確認する)
例えば,httpsにアクセスを限定する,Actionのラッパー
実装の仕方はいろいろあるだろうが,コード量が少なくなって賢いと思う
import play.api.mvc._ def onlyHttps[A](action: Action[A]) = Action.async(action.parser) { request => request.headers.get("X-Forwarded-Proto").collect { case "https" => action(request) } getOrElse { Future.successful(Forbidden("Only HTTPS requests allowed")) } }
コンテンツ
メディアレンジ(text/*やapplication/json)で処理を沸けることができる.
val list = Action { implicit request => val items = Item.findAll render { //このスコープはこんな書き方ができるのか... case Accepts.Html() => Ok(views.html.list(items)) case Accepts.Json() => Ok(Json.toJson(items)) } }
非同期レスポンス
広告を表示するためのeCPMの計算などに遅延が生じることがある.
そのため,非同期的にレスポンスを返せるとうれしい.
import play.api.libs.concurrent.Execution.Implicits.defaultContext def index = Action.async { val futureInt = scala.concurrent.Future { intensiveComputation() } //concurrentな関数をscala.concurrent.Futureで定義可能 futureInt.map(i => Ok("Got result: " + i)) }
chunkResponse
動画ストリーミングで必要かな??? AjaxのD3にデータを渡すときにも必要かもしれない.
def index = Action { Ok.chunked( Enumerator("kiki", "foo", "bar").andThen(Enumerator.eof) ) }