SE(たぶん)の雑感記

一応SEやっている筆者の思ったことを書き連ねます。会計学もやってたので、両方を生かした記事を書きたいと考えています。 でもテーマが定まってない感がすごい。

Callについて(Play framework読み始めた)

経緯

上司兼メンターから、

Scala製のオープンソースとか読んでみるのも良いかもしれない

との助言をいただきました。なぜそう言われたか、については端折ります。そこで、Scalaオープンソースで有名なものと言えばPlay frameworkだろうと思い至り、読み始めました。仕事でも使っていますし、ちょうどよいです。

Play frameworkとは

www.playframework.com

バージョンは2.8が最新ですが、日本語ではないので2.4を貼っています。

一言で言うと、MVCをベースにしたWebフレームワークです。他の言語だと

などがあります。MVCパターンについての説明は世の中にあふれていますので、この記事に辿り着くような方であれば自力で調べられると思います。(丸投げ)

Play frameworkの特徴

Play framework自体は、JavaScalaで書かれています。そして、利用する際もJava及びScalaのどちらからでも利用できます。

Javaでの利用

www.playframework.com

Scalaでの利用

www.playframework.com

違いはそれほどありませんが、Scalaでは言語機能のおかげでより楽に使えるようになっているという印象を受けます。

利用する場合、言語によって名前空間参照先が異なります。

Javaplay.mvc など
Scalaplay.api.mvcなど

これ以上の詳細は、この記事の主題から外れますので、気になる方は各自ドキュメントを読むなどお願いします。

どこを読むのか

まずは、それほど難しくない、かつ、私がそれなりに使う部分にしました。結果、play.api.mvc.Callになりました。

Callとは

Callは、リバースルーティングする際に取得できるオブジェクトの型です。たいていのWebフレームワークでは自身のURLとControllerの対応関係を定義します。Play frameworkの場合、routesというファイルに

GET /     homeController.home()
GET /shop shopController.list()

のような形で定義します。

このとき、例えば/shopへのリンクを他のページに埋め込む場合、

<a href="/shop">shop</a>

という形で文字列埋め込みするのはあまり得策とは言えません。これをやってしまうと、他ページリンクする際にURL構成を知らなければなりませんし、万が一サイトのURL構成を変更した場合に、変更箇所の特定が困難になります。

Play frameworkでは、一度コンパイルするとroutes定義からルーティング定義のオブジェクトを生成してくれます。その定義オブジェクトの個々のURLを指すのが、Callオブジェクトです。Twirlで使用する場合は

<a href="@routes.shopController.list()">shop</a>

のような形になります。これで、/shop/shop-listに変わったような場合であっても使用箇所には影響が出ません。

基本的にはフレームワークが自動生成するものであり、Javaから利用する場合はコンストラクタすらありません。自動生成されたCallにfragmentを追加した後のCallを生成する、といった程度です。

Callの定義

Call自体は、二か所に定義があります。

  • play.mvc.Call
  • play.api.mvc.Call

違いですが、先述の通り、前者がJava用、後者がScala用です。

Java定義

playframework/Call.java at master · playframework/playframework · GitHub

下記は、コメントを除いた部分です。

public abstract class Call {
  public abstract String url();

  public abstract String method();

  public abstract String fragment();

  // 以下、公開メソッド
}

Scala定義

playframework/Call.scala at master · playframework/playframework · GitHub

case class Call(method: String, url: String, fragment: String = null) extends play.mvc.Call {
  // 省略
}

Javaでabstract定義されていたフィールドをcase classの引数で受け取るようになっています。

言えること

上記の通り、Javaで定義しているのは抽象部分、Scalaで定義しているのは具象部分となっています。そして、Scala部分については、よりScalaで使いやすいよう、定義が増えている個所があります。

例として、absoluteURLというメソッドは、Javaだと

public String absoluteURL(Http.Request request)
public String absoluteURL(Http.Request request, boolean secure)
public String absoluteURL(boolean secure, String host)

の3つのオーバーロードがあります。Scalaではこれらに加えて

def absoluteURL()(implicit request: RequestHeader): String
def absoluteURL(secure: Boolean)(implicit request: RequestHeader): String

という定義が追加されています。implicit parameterが定義されていることにより、

// どこかで implicit val request = ... みたいな定義があること前提
val url = call.absoluteURL

という感じで、request変数を意識せずに使えます。よりScalaらしく扱えます。便利。

気になった部分

Java定義が抽象、Scala定義が具象なのはわかりました。しかし、Javaのほうで気になった定義がありまして。

public Call unique() {
  return new play.api.mvc.Call(method(), this.uniquify(this.url()), fragment());
}

のように、Scala(play.api.mvc.Callで定義しているコンストラクタを見に行っています。これっていいんだっけ…?という疑問が。

似た定義は他の場所にもあります。例えば

play.mvc.Filter

public abstract class Filter extends EssentialFilter {
  public play.api.mvc.Filter asScala() {
    return new play.api.mvc.Filter() {
// 以下略

のような感じです。これはそういうものなのだろうと考えることにします。(おい)

Scalaが具象と考えれば特に変でもないですし、私ごときが良い方法を思いつくはずもないです。今後さらに読み進めるときに何か気づくかもしれません。Scalaで実装が提供されることが前提なら、全く問題ない方法です。

気になった部分その2

JavaabsoluteURL

  public String absoluteURL(boolean secure, String host) {
    return "http" + (secure ? "s" : "") + "://" + host + this.url() + this.appendFragment();
  }

というメソッドがあり、Scalaにも

  def absoluteURL(secure: Boolean)(implicit request: RequestHeader): String =
    "http" + (if (secure) "s" else "") + "://" + request.host + this.url + this.appendFragment

というメソッドがあります。内容が実質一緒です。DRY原則に反している感じがします。というわけで、ここは修正の上issueに上げてみようと思っています。ScalaのCallにはテストも無いため、そちらもセットで実装中です。

おわりに

Callは便利ですが、大したクラスではないです。定義も簡単です。でも、こういうところから読み進め、徐々に範囲を広げることも重要だと思うので、このペースで続けます。

今は、routesからどうやってリバースルーティング用のソースを生成しているのか調べようと思っている最中です。

Play frameworkを読むに際し気になっているのは、JavaScalaでいろいろ分かれている部分をどういう指針で分けて、どうやって実装しているのかです。そこに注目して読んでいきます。

次週予定

下記のどれかです。

  • Django3.0が出ているので内容見てみる
  • 『Design it!』感想
  • Play frameworkさらに読んでみた

こうご期待(?)