読者です 読者をやめる 読者になる 読者になる

にほんごのれんしゅう

日本語として伝えるための訓練を兼ねたテクログ

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

webservice起動

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型がとりうる値

  1. ただのテキスト
  2. SimpleResult:httpヘッダを付加した,htmlなどのデータ
  3. None:リダイレクトとか
  4. TODO:いわゆる工事中の型
  5. XML: XMLのような形の何か

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)
  )
}