Category Archives: Play framework

Play 2.0 の Cache API が使うに耐えない

Play 2.0(厳密にはこの記事の執筆時の最新 2.0.3)の Cache API はほんとうに使うに耐えません(Scala での話。Java ではどうだか試してません)。Ehcache を直で扱ったほうがよっぽどよさそうです。

1. キャッシュを削除できない。

キャッシュってそんなに任意で消すようなものでもないと思いますが、それにしても任意に消せないのはどうも不便です。ちなみに以下のようなコードがキャッシュを消すコードとして一部で出回っていますがデタラメです。null 値が永遠にキャッシュされるだけです。

Cache.set("YourCacheKey", null, 0)

2. キャッシュの寿命のデフォルト値を制御できない。

現在の実装では、キャッシュの寿命を指定しないと 0 を指定したことになります。0 を指定すると永遠にキャッシュするという意味になります。設定ファイルなどで調整できません。

3. null 値をキャッシュできない(取り出せない)。

null をキャッシュする意味があるのかというと、たまにあります。非常に長い処理の結果が null だったりしたら null も重要な情報のひとつです。Scala なら None 使えよって話もありますが。

それで、null 値をキャッシュに入れることはできるのです。ところが取り出そうとすると NullPointerException で落ちます。なので、前述のデタラメキャッシュ削除コードを使っていたりすると潜在バグの温床になります。NullPointerException が出ること自体 Play のバグのような気もしますが……。

Play 2.0 の i18n のはじめかた

なにげにハマりやすい Play 2.0 の i18n。日本語最優先の場合は新たにプロジェクトを作成したら、とりあえず以下をやればいいかも。

1. アプリケーションの言語の設定

conf/application.conf 内のapplication.langsの値を”ja-JP”にする。

2. メッセージファイルを作る

conf/messages.ja-JPというファイルを作って内容を以下のようにする。

# デフォルトメッセージ

# --- Constraints
constraint.required=必須
constraint.min=最小値: {0}
constraint.max=最大値: {0}
constraint.minLength=最小長さ: {0}
constraint.maxLength=最大長さ: {0}
constraint.email=Email

# --- Formats
format.date=日付 (''{0}'')
format.numeric=数値

# --- Errors
error.invalid=不正な値
error.required=この欄は必須です
error.number=数値でなければなりません
error.min={0} 以上でなければなりません
error.max={0} 以下でなければなりません
error.minLength=最小長さは {0} です
error.maxLength=最大長さは {0} です
error.email=正しいEmailアドレスでなければなりません
error.pattern={0} の形式でなければなりません

これでとりあえず、標準のエラーメッセージが日本語化されます。あとは、お好みで文言を修正してください。

Heroku で Play! 製アプリケーションが文字化けしてお困りの方に

ローカルでは上手く行っているのに Heroku にデプロイすると上手くいかないという方に朗報! 先日 Heroku が提供する Scala の BuildPack が更新されまして、これを使用することで文字化けしないようになります。

Heroku にデプロイすると文字化けすると困っているプロジェクト下で以下のコマンドを実行してみてください。

heroku config:add BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-scala.git

これで以降のビルドから文字化けしなくなります。

Play 2.0 でカスタムフォームデータマッピング

前回の記事で、「あれ? Double(Float)型使えないの? 少数とかどうするの?」と思った方もいると思います。そうでないにしても、他の型にマッピングしたいというのは当然の要望でしょう。

そこで今回は、任意の型にフォームデータをマッピングする方法を紹介します。

まず必要なのは play.api.data.format.Formatter トレイトの実装クラスです。これを提供するメソッドを作成します。作成する場所は object であればどこでもかまいませんが、どうせ controllers でしか使用しないというのであれば、controllers のパッケージオブジェクトだと何かと楽です(controllers のパッケージオブジェクトでは上手くいかないようでした)。なぜ object かというと、以下のように implicit def なので import が必要だからです。

まず、作成するメソッドに必要なクラス等を使用する import 宣言は以下のとおりです。

import play.api.data._
import Forms._
import play.api.data.format._
import Formats._

そして作成するメソッドですが、以下は Double 型へマッピングする場合の例です。

  /**
   * リクエストデータを Double 型に変換するフォーマッタを取得します。
   */
  implicit def doubleFormat = new Formatter[Double] {

    /**
     * このフォーマットについて画面に表示する際に使用するメッセージ
     */
    override val format: Option[(String, Seq[Any])] = Some("format.double", Nil)

    /**
     * リクエストデータを Double 型に変換します。
     *
     * @param key リクエストデータを取り出す際に使用するキー値
     * @param data リクエストデータ
     * @return Double 型への変換が失敗した場合フォームエラー。成功した場合変換した値
     */
    def bind(key: String, data: Map[String, String]): Either[Seq[FormError], Double] = {
      stringFormat.bind(key, data).right.flatMap { s =>
        scala.util.control.Exception.allCatch[Double]
          .either(s.toDouble) // 文字列から Double に変換する処理
          .left.map(e => Seq(FormError(key, "error.double", Nil))) // それが失敗した場合
      }
    }

    /**
     * Double 型の値から、リクエストデータ用の文字列に変換します。
     */
    def unbind(key: String, value: Double): Map[String, String] = Map(key -> value.toString)
  }

ここまでやればもう使い物になるのですが、もう一声、上記のメソッドの直下あたりで以下のようにやっておくとより格好がつきます。

  val double = of[Double]

このようにフォームデータマッピングで使用できます。

  val helthForm = Form(
    tuple(
      "tall" -> double,
      "weight" -> optional(double)))

Play 2.0 でのフォーム定義

Play 2.0 が正式リリースされました。Play 1.x が Java 用フレームワークであとから Scala モジュールを加えることで Scala でも利用できていたのに対して Play 2.0 は最初から Scala に対応しています。というよりも、Scala メインと言ってもいいでしょう。

Play 2.0 はとても使いやすい Web アプリケーションフレームワークですが、まだまだ情報が不足しています。今回は公式ドキュメントの Handling form submission の補足を行います。

どんなフォームデータマッピングが使用きるのか?

公式ドキュメントではフォームデータのマッピングは text と number についてだけ少しだけ述べているに過ぎません。ですが、デフォルトでもたくさんのマッピングが利用できます。

text

フォームデータを String 型にマッピングします。文字数を制限したい場合は以下のように書きます(省略可)。minLength で最小文字数、maxLength で最大文字数を指定します。

"username" -> text(minLength = 3, maxLength = 100)

nonEmptyText

文字数0を許容しない text です。text に minLegnth = 1 と指定した場合と同じ動きになりますが、制約メッセージを表示した場合、text の場合だと Minimum Value: 1 と表示されるのに対してこれは Required と表示されます。nonEmptyText も text 同様に文字数を制限することができます。

 number

フォームデータを Int 型にマッピングします。以下のように値の範囲を制限することもできます(省略可)。

"size" -> number(min = 10, max = 20)

longNumber

フォームデータを Long 型にマッピングします。なぜか number のような指定の仕方で範囲の制限はできません。

date

フォームデータを java.util.Date 型にマッピングします。引数でフォーマットを指定できます(省略可)。フォーマット形式は java.text.SimpleDateFormat に準じます。

"birthday" -> number("yyyyMMdd")

sqlDate

フォームデータを java.sql.Date 型にマッピングします。date 同様フォーマットを引数で指定できます。

email

フォームデータを String 型にマッピングしますが、メールアドレスとして妥当な形式でなければなりません。といっても、ものすごく厳密にメールアドレスの形式を検証しているわけではありません(必要十分なレベル)。

boolean

フォームデータを Boolean 型にマッピングします。フォームデータが “true” であれば true、”false” であれば false になります。必ず小文字でなければならないようです。

checked

フォームデータを Boolean 型にマッピングします(boolean と同様の性質)が、必ず true にならなければなりません。true でない場合、引数で指定したエラーメッセージを表示します。

"accepted" -> checked("使用許諾に同意してください。")

ignored

フォームデータをマッピングしません。代わりに引数で指定したデフォルト値を設定します。これは O/R マッパ等のエンティティにフォームデータをマッピングするときにあるフィールドはフォームデータの値とひもづけない等といった場合に使用します。

"id" -> ignored(0)

optional

フォームデータを Option[A] 型にマップします。省略可能な値である場合に使用します。引数として、型パラメータ A に対応するマッピングを指定します。

"age" -> optional(number(0, 120))

list

フォームデータを List[A] 型にマップします。複数選択値を扱う際に使用します。引数として、型パラメータ A に対応するマッピングを指定します。

"favorite" -> list(text)

seq

フォームデータを Seq[A] 型にマップします。使い方は list と同じです。