enchant.js + play2.0 + CoffeeScript

ちょっと気になって、enchant.jsを調べ始めた。

ExtJSもそうだけど、結局クライアント側のソースがJavaScriptだけ記述すれば良いのであれば、
JavaScriptを呼び出すだけのHTMLって余計というかいらないんだよね。

ということで、JavaScriptだけ指定して、実行できる環境をPlay2.0で作ってみた。

要は、こんな感じで指定すると、どこか知らないHTMLからXXX.jsを呼び出してくれるという感じ。
http://localhost/enchant/XXX.js

・conf/routes

GET    /enchant/:script        controllers.Application.enchant(script:String)

・app/controllers/Application.scala

package controllers

import play.api._
import play.api.mvc._

object Application extends Controller {

def enchant(script:String) = Action {
Ok(views.html.enchantmain(script))
}
}

・app/views/enchantmain.scala.html

@(scriptpath: String)
<!DOCTYPE html>
<html>
<head>
<title>@scriptpath</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-var-style" content="black-translucent">
<style type="text/css">
body {
margin: 0;
}
</style>

<link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
<script src="@routes.Assets.at("javascripts/enchant/enchant.js")" type="text/javascript"></script>
<script src="@routes.Assets.at("javascripts/enchant/plugins/ui.enchant.js")" type="text/javascript"></script>
<script src="@routes.Assets.at("javascripts/enchant/enchant.js")" type="text/javascript"></script>
<script src="@routes.Assets.at("javascripts/" + scriptpath)" type="text/javascript"></script>
</head>
<body>
</body>
</html>

・app/assets/javascripts/test001_bear.coffee

enchant()

window.onload =->
game = new Game(320,320)

imagePath = "/assets/javascripts/enchant/images/chara1.gif"
game.preload(imagePath)

game.onload = ->
bear = new Sprite(32,32)
bear.image = game.assets[imagePath]
bear.frame = 4

bear.addEventListener Event.ENTER_FRAME , ->
this.x += 3
if this.x > 320
this.x = -32

game.rootScene.addChild bear

game.start()

んで、http://localhost:9000/enchant/test001_bear.js にアクセス

いろいろ気づいたところ
・apps/assets/javascriptsにはJavaScript,CoffeeScriptの両ファイルは混在できる
JavaScript,CoffeeScript内では@routes.asset.Atは使えない(Scalaテンプレートじゃないのであたりまえか)
CoffeeScript作ればすぐに動かせるのでかなり楽チン

Jsonを返却する その3

いちいちJSON変換クラスなんか作っていられるか!!とお怒りの方に。
Gsonで変換してしまいましょう。

・project/Build.scalaにGsonの追加

    val appDependencies = Seq(
// Add your project dependencies here,
"com.google.code.gson" % "gson" % "1.7.1"
)

・sbtコンソールにてupdate
・sbtコンソールにてeclipse(要SBT-Eclipseプラグイン)
Eclipseでプロジェクトをリフレッシュ

・コード作成(関数の外にcase class を作らないとダメ)

    import com.google.gson._
def RenderJson(obj:AnyRef) = Ok(new Gson().toJson(obj)).as("application/json; charset=utf-8")

case class ResultData2(name:String,zip:String,age:Int)
def sample4 = Action { implicit request =>
val obj = Array(ResultData2("hoge","123-4567",17),ResultData2("fuga","123-4567",17))
RenderJson(obj)
}

Jsonを返却する その2.1

・配列、リストもOK

    def sample3 = Action { implicit request =>
val obj = Array(ResultData("hoge","123-4567",17),ResultData("fuga","123-4567",17))
Ok(toJson(obj))
}

・結果

[{"name":"hoge","zip":"123-4567","age":17},{"name":"fuga","zip":"123-4567","age":17}]

Jsonを返却する その2

Play1.Xならcase classはすんなり返せていたのに、Play2.0だとちとメンドクサイ。
こちらを参考にしたが、うまく動かなかったので、
trait Protocolではなくobject Protocolにしたところ、動作できました。

・case classと変換クラス

package models

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.json.Writes._

case class ResultData(name:String,zip:String,age:Int)

object Protocol {
implicit object ResultDataFormat extends Format[ResultData] {
def reads(json: JsValue): ResultData = ResultData(
(json \ "name").as[String]
,(json \ "zip").as[String]
,(json \ "age").as[Int]
)

def writes(p: ResultData): JsValue = JsObject(List(
"name" -> JsString(p.name)
,"zip" -> JsString(p.zip)
,"age" -> JsNumber(p.age)
))
}
}

・呼び出し側

    import play.api.libs.json.Json._
import models.ResultData
import models.Protocol._
def sample3 = Action { implicit request =>
val obj = ResultData("hoge","123-4567",17)
Ok(toJson(obj))
}

・結果

{"name":"hoge","zip":"123-4567","age":17}

extjsを組み込む

とりたてて難しい話ではないけれど、main.scala.htmlをコピって、extjsをインクルードする行を追加した
extjsmain.scala.htmlを用意して、テンプレートからはextjsmain()を呼ぶように変更。

・extjsmain.scala.html


@(title: String)(content: Html)

<!DOCTYPE html>

<html>
<head>
<title>@title</title>
<meta charset="UTF-8" />
<link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
<script src="@routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>

<link rel="stylesheet" media="screen" href="@routes.Assets.at("javascripts/ext-4.0.7/resources/css/ext-all.css")"">
<script src="@routes.Assets.at("javascripts/ext-4.0.7/ext-all.js")" type="text/javascript"></script>

</head>
<body>
@content
</body>
</html>

・sample2.scala.html

@(ctrl : controllers.Sample.type)(implicit req:Request[AnyContent])

@extjsmain(ctrl.test) {

<script src="@routes.Assets.at("javascripts/main.js")" ></script>

<script type="text/javascript">
Ext.onReady(initComponent);

function initComponent(){
Ext.create("Ext.Viewport",getViewport());
}

function getViewport(){
var cfg = {
layout:"border"
,items:[
{region:'center',title:'@ctrl.test'}
]
};

return cfg;
}


</script>


}