k4200’s notes and thoughts

Programmer side of k4200

Play! 2.1のroutesでOption型のパラメータを・・・

やりたいこと

あー、タイトル分かりにくくてすみません。

今作ってるサイトで、要はこういうことをやりたいんです。

GET     /$areaId<[0-9]+>                    controllers.Foo.bar(areaId: Int, subAreaId: Option[Int] = None)
GET     /$areaId<[0-9]+>/$subAreaId<[0-9]+> controllers.Foo.bar(areaId: Int, subAreaId: Option[Int])

でも、エラーが出る。

No URL path binder found for type Option[Int]. Try to implement an implicit PathBindable for this type.

ググったらStackOverflowに同じ質問があった。

Play 2.0にはあった機能が2.1で無くなったらしい。言われてみると、2.0時代に使った記憶もあるし・・・バージョンアップして機能が削られるって珍しいね。

解決方法

SOの通りなんだけど、補足。

以下の様なファイルを作る。

package com.example.play

import play.api.mvc.PathBindable

object Binders {
  implicit def OptionBindable[T : PathBindable] = new PathBindable[Option[T]] {
    def bind(key: String, value: String): Either[String, Option[T]] =
      implicitly[PathBindable[T]].
        bind(key, value).
        fold(
          left => Left(left),
          right => Right(Some(right))
        )

    def unbind(key: String, value: Option[T]): String = value map (_.toString) getOrElse ""
  }
}

で、Build.scalaをちょこっと修正。以下、自分のプロジェクトのファイルから抜粋。

    val website = play.Project(
      appName + "-website", appVersion, appDependenciesWebsite
    ).settings(
      // Add your own project settings here
      lessEntryPoints <<= baseDirectory(customLessEntryPoints),
      resolvers ++= Seq(
        Resolver.url("play-plugin-releases", new URL("http://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/"))(Resolver.ivyStylePatterns)
      ),
      routesImport += "com.example.play.Binders._"  // ← この行を追加!!
    ).dependsOn(common, common % "test->test")

まとめ

Play 2.1のroutesでOptionを取れるようにする方法を簡単に説明した。2.0では問題なかったが、2.1で問題が出たので、それの回避方法。

Play開発チームは勝手に機能を削るのはやめて欲しい。そこそこ使えそうな機能なのに・・・