More Related Content
More from Shunji Konishi (20)
Play1 to Play2
- 2. FLECTの主力言語はPlay1
◦ しかしPlay1はもはやメンテされていない模様
◦ 1.3が出ると一瞬聞いたような気もするが。。。(--
Play1開発者がPlay2を使う場合に
◦ Play1でのやり方との対比でPlay2での方法を知る
◦ 言語とFrameworkの両方を同時に学ぶのは辛いのでとりあえ
ずScalaが良く分かってなくてもなんとなく開発できる気にす
るのがゴール
◦ これどうするんだっけ?と思った時に見る資料(主に俺が)
ただしBest Practiceはまだまだ模索中なのでもっと良
い方法があるはずとも思う。
◦ というかPlay2自体まだまだそんな感じ
◦ 間違ってもここに書いてあることを鵜呑みにしてはいけない
- 3. 自力で頑張れ
◦ List, Map, Option, パターンマッチ, case classあたりがわかれ
ばとりあえず使える
禁止技
◦ 途中でReturn
◦ Nullとの比較(Optionを使用する)
Scalaでどう書けば良いのかわからん!と思った時にはと
りあえずJavaだと思って書けばOK
慣れるまでimportでワイルドカードは使わない
◦ Play(というかScala全般)でimportはワイルドカードで書く文化
でサンプル等もほとんどそうなっているが、それだとクラス構成
が全く分からない。
◦ 特にimplicit関数を把握しないまま使うのは危険だと思う
◦ 列挙型やImplicit関数などワイルドカードでインポートしないと
使いづらいものもあるが、それを見極めるためにも最初は自力で
書く
- 5. package controllers
import play.api.mvc.Controller
import play.api.mvc.Action
object Application extends Controller {
def index = Action {
Ok(views.html.index("Your new application is ready."))
}
def hello = Action {
Ok("Hello world")
}
}
デフォルトで定義されている
Action
Okは「200 OK」のレスポンス
views.xxxxはviewsフォルダにあ
るテンプレートファイルへの参照
で
renderTemplate相当
renderText相当
Play1と違ってワイルドカードルーティングはないのでroutesの
編集も必要
- 7. @(name: String)(implicit lang: Lang)
@main(Messages("hello")) {
<div>
@Messages("hello") @name
</div>
}
これがないとMessageの国際化がされない
使用するパラメータはすべて宣言する必要がある
◦ 最初はPlay1に比べてかなり面倒な気がするがパラメータの不
備がコンパイルエラーで拾えるのがメリット
マジックワードは「@」のみ
「@{…}」で自由にScalaコードを記述可能
上の例で使用している機能
◦ 親テンプレートとして@mainを呼び出している
◦ @Messagesで国際化されたメッセージリソースを出力
◦ 引数@nameを出力
- 8. ワイルドカードは使えない
Actionの参照には引数を含める必要がある
PATHの一部をActionの引数として渡せる
GETパラメータはActionの引数として渡せるがPOSTパラメータは渡せ
ない
パラメータには省略時のデフォルトが定義できる
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / controllers.Application.index
GET /hello controllers.Application.hello
GET /hello2/:name controllers.Application.hello2(name)
GET /hello2 controllers.Application.hello2(name ?= "guest")
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
Play1に比べて面倒
- 10. Formを使用する
◦ Case classを作ってそこにマッピングするのが楽
private val queryForm = Form(mapping(
"id" -> optional(text),
"kind" -> of[QueryKind],
"name" -> text,
"group" -> default(text, ""),
"sql" -> text,
"desc" -> optional(text),
"setting" -> optional(text)
)(QueryInfo.apply)(QueryInfo.unapply));
https://github.com/shunjikonishi/sqltool/blob
/master/app/controllers/QueryTool.scala
- 14. Application.confにデータベース定義を追加
Play1ではDBはひとつしか定義できなかったが複数定
義可能になった
ORMapperはない
代わりにAnormが組み込まれている
◦ 多分SelectBuilderとの相性は良い
◦ https://github.com/shunjikonishi/sqltool/blob/master/
app/models/QueryManager.scala
//グループ名の一部を抜き出す処理
SQL("""
SELECT distinct groupname FROM sqltool_sql
WHERE groupname LIKE {gl}
ORDER BY groupname
"""
).on(
"gl" -> (parent + "/%")
).apply.map{ row =>
val g = row[String]("groupname").substring(parent.length + 1);
g.split("/")(0);
}.toSet.toList;
- 17. JSONライブラリがgsonからjackson(のScalaラッ
パーであるjerkson)に変わった
Case classをJSONにparse/formatするだけなら
JSON.formatをimplicitで定義するだけで良い。
加工が必要な場合は自分でFormatを定義
◦ JSONのキーを変数名とは別にしたい
◦ Case class中のあるフィールドはJSONに含めたくない
https://github.com/shunjikonishi/sqltool/blo
b/master/app/models/QueryInfo.scala
https://github.com/shunjikonishi/sqltool/blo
b/master/app/models/SQLToolImplicits.scala
- 20. Play1と同じく実体はCookie
セッションIDがない
◦ Memcacheを使用する場合に困る
◦ 自前のFilterで対応
//OAuth認証のTokenをsessionに持たせる
Ok(views.html.login(url)).withSession(
"token" -> token.getOAuthToken()
)
- 21. Play1と同じく次のリクエストまで有効なFlashが使用でき
る
◦ 仕組みもPlay1と同じ
◦ https://github.com/shunjikonishi/sqltool/blob/master/app
/controllers/QueryTool.scala//ファイル インポート後にインポート件数を付けてメイン画面にリダイレクト
Redirect("/main").flashing(
"Import-Insert" -> insertCount.toString,
"Import-Update" -> updateCount.toString
);
取得側のコード
◦ Implicit requestが必要
◦ https://github.com/shunjikonishi/sqltool/blob/master/app
/controllers/Application.scala
def main = Action { implicit request =>
val importInsert = flash.get("Import-Insert").getOrElse("0").toInt;
val importUpdate = flash.get("Import-Update").getOrElse("0").toInt;
…
}
- 23. Ok.sendFileメソッドを使う
◦ Play1では添付ファイルのダウンロードと削除を同時に
やろうとするとすごく面倒だったのが楽になった
◦ https://github.com/shunjikonishi/sqltool/blob/ma
ster/app/controllers/QueryTool.scala
def exportSql = Action { implicit request =>
val file = File.createTempFile("temp", ".sql");
try {
man.exportTo(file);
Ok.sendFile(file, fileName={ f=> "export.sql"}, onClose={ () => file.delete()});
} catch {
case e: Exception =>
e.printStackTrace;
Ok(e.toString);
}
}
- 31. HerokuのスケジューラからPlay2を起動したい場合
◦ 重いので可能であればJavaクラス単体にするなどした方が良
い
target/startスクリプトに起動オプションを渡して起
動
target/start –Dsqltool.mode=schedule
ローカルで動かす場合はあらかじめ「play stage」コ
マンドを叩いてlibをビルドする必要がある
Windowsの場合は.batは生成されないので自分でバッ
チファイルを書く必要がある
コマンドが長いのでrakeで起動するようにしている
https://github.com/shunjikonishi/sqltool/blob/master/sqltool.bat
https://github.com/shunjikonishi/sqltool/blob/master/Rakefile