S-JIS[2012-03-11] 変更履歴

Play!2.0の雛形

Play framework 2.0の雛形の処理の流れを追ってみたメモ。


呼ばれる順序

Play2.0の雛形アプリケーションを実行した際に処理(クラス・テンプレート)が呼ばれる順序。

  1. サーバーにアクセスされると、まずconf/routesの設定を参照する。
    # Home page
    GET     /                           controllers.Application.index
    
  2. controllers.Applicationオブジェクト(app/conrollers/Application.scala)のindexメソッド(コントローラー)が呼ばれる。
    package controllers
    object Application extends Controller {
    
      def index = Action {
        Ok(views.html.index("Your new Application is ready."))
      }
    
    }
  3. views.html.index(ビュー)が呼ばれる。
    これは、Scalaとしてはviews.html.indexオブジェクトのapplyメソッドらしい。
    このオブジェクトは、(たぶん)app/views/index.scala.htmlから生成される。
    @(message: String)
    
    @main("Welcome to Play 2.0") {
    
      @play20.welcome(message)
    
    }
  4. @mainが呼ばれる。実行時にはScalaオブジェクトになっていると思われる。
    (@mainの第2引数が@play20.welcomeなので、先にそちらが呼ばれて、その結果がmainへの第2引数contentに渡される)
    このオブジェクトは、(たぶん)app/views/main.scala.htmlから生成される。
    @(title: String)(content: Html)
    
    <!DOCTYPE html>
    
    <html>
        <head>
            <title>@title</title>
            <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
            <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>
        </head>
        <body>
            @content
        </body>
    </html>

routesファイル

routes(ルート定義ファイル)には、HTTPでアクセスされたときのメソッド(GETとか)とパスと処理内容(どのオブジェクト を呼ぶか)を指定する。

conf/routes:

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

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

assetsは、別言語(CoffeeScriptとか)へコンパイルされるものを置く場所らしい。


routesファイル上のパスが「/」だと、「http://localhost:9000/」にしかマッチしない。
「/zzz」だと「http://localhost:9000/zzz」にマッチする。

routes マッチするURIの例 コントローラーの記述例
パス メソッド呼び出し
/ controllers.Application.index

http://localhost:9000
http://localhost:9000/

def index = Action { Ok("root") }
/zzz controllers.Application.zzz0 http://localhost:9000/zzz
http://localhost:9000/zzz?abc=123
def zzz0 = Action { Ok("zzz") }
/zzz/:id controllers.Application.zzz1(id) http://localhost:9000/zzz/aaa def zzz1(id: String) = Action {
  Ok("zzz:" + id)
}
/zzz/*id controllers.Application.zzz1(id) http://localhost:9000/zzz/aaa
http://localhost:9000/zzz/aaa/bbb
/zzz/:id1/*id2 controllers.Application.zzz2(id1, id2) http://localhost:9000/zzz/aaa/bbb def zzz2(id1: String, id2: String) = Action {
  Ok("zzz:" + id1 + " " + id2)
}

パスの部分に文字列を書くと、それに完全一致するものだけマッチする。
:」から始めると、スラッシュ「/」で区切られた文字列1つを入れる変数になる。
*」から始めると、スラッシュ「/」も含めてそれ以降全部を入れる変数になる。


コントローラー

どのビューへ遷移するかを決めるのがコントローラーとアクション
たぶん複雑なロジックもここに書く。

app/conrollers/Application.scala:

package controllers
import play.api._
import play.api.mvc._
object Application extends Controller {

  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }

}

Actionは、play.api.mvc.Actionオブジェクトのapplyメソッド呼び出し。
(Scalaではapplyメソッドはメソッド名自体が省略できる。また、引数が1つであれば丸括弧でなく波括弧が使える


OkはScalaの文法的にはよく分からない(爆)が、HTTPレスポンスの「200 OK」を返すもの。
以下の様にボディー部を記述できる。

  def index = Action {
    Ok("test")
  }

ボディー部をplay.api.templates.Htmlクラスで指定すると、HTMLを記述できる。

  def index = Action {
    Ok(new Html("<strong>test</strong>"))
  }

で、実用上はHTMLを直接記述するのではなく、ビュー(テンプレート)を指定する。

  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }

ActionはHTTPリクエストを引数に取ることが出来る。

ソース アクセス方法(URL) 表示されるもの
  def index = Action { request =>
    Ok("request=[%s]".format(request))
  }
http://localhost:9000 request=[GET /]
http://localhost:9000/?abc=123 request=[GET /?abc=123]
  def index = Action { request =>
    Ok("zzz=" + request.queryString.getOrElse("zzz", ""))
  }
http://localhost:9000
http://localhost:9000/?abc=zzz
zzz=
http://localhost:9000/?zzz=abc zzz=Buffer(abc)
http://localhost:9000/?zzz=abc&zzz=def zzz=Buffer(abc, def)

request.queryStringの型は、Map[String,Seq[String]]
クエリーのパラメーター名をキーとし、その一覧(Seqだが、実体はBufferらしい)を返す。


ビュー(テンプレート)

ビューはテンプレート(HTMLファイルのようなもの)で記述する。
基本はHTMLだが、「@」を付けてScalaの文を書ける。

app/views/index.scala.html:

@(message: String)

@main("Welcome to Play 2.0") {

    @play20.welcome(message)

}

テンプレートはScalaのオブジェクトに変換されるようだ。
views/index.scala.html」→「views.html.indexオブジェクト」


テンプレートでは、1行目が「そのテンプレートへの引数」になる。

@(message: String)

↓(このように変換されると思われる)

  def apply(message: String) = 〜

サンプル(雛形)では、コントローラーから以下の様に引数を1つ指定して呼び出している。

  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }

1行目以外の「@」は、Scalaの式(メソッド呼び出し等)になる。

@main("Welcome to Play 2.0") {

    @play20.welcome(message)

}

@main(title){ content }」は、mainオブジェクト(のapplyメソッド)の呼び出し。
引数リストが2つある形式となっている。ここでは、最初の引数は丸括弧、後の引数は波括弧でくくられている( 引数が1つずつしか無いので、文法上は丸括弧でも波括弧でもどちらでもOK)。
(play20.welcomeもPlay2.0で用意されたwelcomeオブジェクトの呼び出し)

app/views/main.scala.html:

@(title: String)(content: Html)
〜

index.scala.htmlをもっと単純なものに変えてみると分かりやすい。

app/views/index.scala.html:

@(message: String)

<html>
<head>
<title>@message</title>
</head>
<body>
<p>@message</p>
</body>
</html>

HTMLの中に可変部だけ@付きで変数を書いているような感じになる。

したがって、HTMLエディターを使ってテンプレートを書くことが出来る。
一番上の行だけちょっと変だし、スタイルシート等のパスもPlay!2.0形式で書いちゃうと正しい場所を指せないけどね(苦笑)


@」からScalaの文が始まるが、どこで終わるかについては自動的に判別してくれる。
@()」のように丸括弧で囲むと、その内部で演算などの式も書ける。
@{}」のように波括弧で囲むと、その内部で複数の文が書ける。
@@」でアットマークの文字そのものを記述(エスケープ)できる。(メールアドレスを「hoge@@hoge.com」と書く等)
サーバーサイドコメントは「@**@」。

forループなんかも書くことが出来る。(自動でyieldが付く扱いになるらしい)

<ul>
@for(i <- 0 until 4) {
	<li>@message @(i + 1)</li>
}
</ul>

こういう風になってくると、HTMLエディター上では乱れてくるけど(苦笑)

@ifとか@importとかサブルーチン作成(ブロックの再利用)とか色々あるので、テンプレートの説明は必見。


Play目次へ戻る / Scalaへ戻る / 技術メモへ戻る
メールの送信先:ひしだま