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 controllersimport 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作ればすぐに動かせるのでかなり楽チン
sbtプラグインを追加
sbt でeclipseコマンドを使えるようにする
・project/plugins.sbtに追加
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0")
・eclipsifyコマンドとどう違うのかは永遠の謎。
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
Play1.Xならcase classはすんなり返せていたのに、Play2.0だとちとメンドクサイ。
こちらを参考にしたが、うまく動かなかったので、
trait Protocolではなくobject Protocolにしたところ、動作できました。
・case classと変換クラス
package modelsimport 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>
}