まーぽんって誰がつけたの?

iOS→Scala→インフラなおじさん技術メモ

Play Framework 2.3 For Java ことはじめ #2 テンプレートエンジン編

f:id:masato47744:20140720125846p:plainPlay Framework 2.3 For Java 入門記事一覧

第2回はPlayで最初にプロジェクト内のファイルを見て何が起きてるか一番意味が分からなかったviewのテンプレートエンジンについてです。

ルーターの仕組みとかMVCの仕組みとかはRailsとかCakePHPとかと同じような感じなんだろうということでなんとなく分かる。でも、viewはほとんど何も書いてないのに、サイドバーとか色々なリンクが貼られたページが出てきてなんじゃこりゃとなった。しかも、scala.htmlっておれJavaのサンプル作ったんですけど。
Playで使ってるテンプレートエンジンについてちゃんと知らないと人に教えられないなと思ったのでまずはここを調べる。

Play Frameworkのテンプレートエンジン最高

いきなり話は飛びますが、そういうことです。
公式のJavaTemplatesの説明を読み進めながらplay-javaのサンプルコード見てたら理解できました。ちょっと時間かかったけど分かったらすごくいい。

すごい!!!とにかくすごい!!!感動した!!!

今まで、RailsのERBとかHamlとか、Node.jsのJadeとか、Javaで言えば、jspmayaaとか触ってきた。 今までのやつだと漠然と以下のようなところに不安を感じてはいた。

  • テンプレートエンジンに誰がどこで値を渡しているかという部分の見通しが悪い
  • view側では変数名があればラッキーで間違ってるかは動かすまで分からない
  • JadeとかHamlみたいに違う記述方式をわざわざ覚えなきゃいけない

まぁ別にこういうもんなんだろうなと思って受け入れてたけど、その不安が全て解消されていたのがこのPlayのテンプレートエンジン。どんな特徴があるかというと

  1. view側で受け取る変数は宣言しないと使用できない
  2. 変数を宣言する際に型を指定する必要がある
  3. 関数が作れる
  4. 変数展開が直感的
  5. 最終的にはただのクラスにコンパイルされるのでview単体でユニットテストできる

はぁーこれ理解するの大変だったけど、ほんといいっす。このテンプレートエンジンの正体はtwirlというもので、Scalaでできたテンプレートエンジン。だからscala.htmlという拡張子だったんすね。理解しました。文句いってごめんなさい。

では、本題のサンプルプロジェクトを解体してみましょう。

play-javaのサンプルプロジェクトを解読する

play-javaのサンプルプロジェクトを見ながらテンプレートエンジンの仕組み、どうやって画面が表示されているのかをみていきます。

1. はじまりはControllerから

まず当然ですが、ControllerのApplication.javaからスタートします。

public class Application extends Controller {

    public static Result index() {
        return ok(index.render("ここがindex.scala.htmlへパラメータとしてわたります"));
    }

}

ここで、index.renderとしているところが、index.scala.htmlを呼び出しています。renderメソッドは、テンプレートエンジンのお約束的なメソッドです。index.scala.htmlはただのクラスにコンパイルされる訳ですから、呼び出しも自然ですね。公式にもこう書いてあります。

If you create a views/Application/index.scala.html template file, it will generate a views.html.Application.index class that has a render() method.

2. index.scala.html

次にviews/index.scala.htmlの中を見てみます。

@(message: String)

@main("ここがmain.scala.htmlにパラメータとして渡ります") {

    @play20.welcome(message, style = "Java")

}

まず一番上の@(message: String)ですが、ここが、私(index.scala.html)はString型のmessageという名前の変数を受け取りますよと宣言しています。ここで、宣言しないと、viewの中で使用することはできません。とても安全だし、見通しもすごくいいですよね!
次に@mainのところですが、なんじゃこりゃと思いますが、これは、mainという関数を呼び出しているところで、main.scala.htmlにパラメータを2つ渡しています。renderメソッドないのになんで呼ばれるかはよく分からない。誰か教えてください><
中括弧のなかもmain.scala.htmlに渡すパラメータです。そのなかにある@play20.welcome(message, style = "Java")というところは、play20のwelcomeメソッドを呼び出して、messageと名前付き引数の"Java"という文字列を渡しています。
サンプルプロジェクトを起動するとサイドバーとかでてきたり、たくさんのコンテンツがいきなり出てきますが、これは、このwelcomeメソッドがhtmlを生成してくれているおかげだったんですね。きっと、このwelcomeメソッドの中には色々htmlが書かれているんだろうと予想できます。だんだん見えてきましたね。

3. main.scala.html

最後に、本体のhtmlとなる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/hello.js")" type="text/javascript"></script>
    </head>
    <body>
        @content
    </body>
</html>

シンタックスハイライトしてみると分かりますがほぼhtmlとしてもちゃんと読めます。さらに、どこがScalaの部分もすぐに分かります。このhtmlとScalaの融合具合が素晴らしいですよね!
先頭にある@(title: String)(content: Html)ですが、これは、変数を2つ受け取るよと宣言しています。このように複数変数を受け取ることももちろんできます。String型のtitleという変数と、Html型のcontentという変数を受け取ると言ってますね。
<title>@title</title>のところで、index.scala.htmlから渡された文字列"ここがmain.scala.htmlにパラメータとして渡ります"が展開されます。 @routes.Assets.atのところは、conf/routesを見ると以下のような記述があるので、assets配下へのパスに展開されます。

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

あとは、ここの部分です。

    <body>
        @content
    </body>

これは、index.scala.htmlの@play20.welcomeメソッドがApplication.javaで渡した文字列"ここがindex.scala.htmlへパラメータとしてわたります"をもとにHtml型のオブジェクトを生成したものが展開されます。h1にでかでかとメッセージが表示されてますね。

まとめ

どうでしょうか。テンプレートエンジンがどんなことをしてるか分かってもらえたでしょうか。(<-自分が作ったテンプレートエンジンじゃないのに偉そうw)
変数宣言するのダルいとか型がむしろウザいと思う人もいるかもしれませんが、大人数で色々なスキルの人で開発することを前提にしているとすごく嬉しいことです。

まだチュートリアルしかやってないけど、このテンプレートエンジンのことかなり好きになってしまいました。

次はデータベース接続をする第3回 -> Play Framework 2.3 For Java ことはじめ #3 データベース接続(JDBC)編 です。