Daily Archives: 2012年3月19日

MongoURI がいい加減すぎワロエナイ

Heroku + MongoLab 環境に Casbah を使って MongoDB に接続する Play 2.0 で作成したアプリケーションをデプロイしようとしました。

それで Heroku 上で MONGOLAB_URI の設定値として提供される MongoDB の接続 URI を com.mongodb.casbah.MongoURI(com.mongodb.MongoURI)に食わせたらまったくもってでたらめなパースをするので自分でなんとかするコードを書きました。使い方は以下のコード内のコメント参照。MongoDB の接続 URI は conf/application.conf 内で mongodb.default.uri で設定するか、起動時に -D オプションで与えるようにしてください。

package mongodb

import play.api._
import Play.current
import com.mongodb.casbah.Imports._

/**
 * MongoDB をサクッと使うためのオブジェクトです。
 */
object Mongo {
  /**
   * MongoDB 接続用 URI をパースするための正規表現
   */
  val uriPattern = "mongodb://(?:([^:^@]+)(?::([^@]+))?@)?([^:^/]+)(?::(\\d+))?/(.+)".r

  /**
   * MongoDB に接続されたブロックを提供します。
   * 接続 URI は mongodb.default.uri の設定値を使用します。
   *
   * 使い方の例
   * {{{
   *   MongoDB.withDB { db =>
   *     db("employee").findAll().toSeq
   *   }
   * }}}
   */
  def withDB[A](f: MongoDB => A): A = withDB("default")(f)

  /**
   * MongoDB に接続されたブロックを提供します。
   *
   * 使い方の例
   * {{{
   *   MongoDB.withDB("mydb") { db =>
   *     db("employee").findAll().toSeq
   *   }
   * }}}
   *
   * @param name 設定名
   */
  def withDB[A](name: String)(f: MongoDB => A): A = {
    // Play を使わない場合は↓このあたりを変えるといい
    val uri = Play.configuration.getString("mongodb." + name + ".uri").getOrElse("mongodb://localhost/test")

    // URI のパース
    val (usernameOpt, passwordOpt, host, portOpt, database) = parseURI(uri)

    // MongoDB に接続
    val conn = portOpt map { port =>
      MongoConnection(host, port.toInt)
    } getOrElse {
      MongoConnection(host)
    }

    try {
      // DB 取得
      val db = conn(database)
      // 必要とあれば認証処理を行う
      (usernameOpt, passwordOpt) match {
        case (Some(username), Some(password)) => db.authenticate(username, password)
        case _ =>
      }
      // 引数で指定された処理を実行
      f(db)
    } finally {
      conn.close()
    }
  }

  /**
   * MongoDB 接続 URI をパースします。
   */
  def parseURI(uri: String) = {
    uri match {
      case uriPattern(username, password, host, port, database) =>
        (Option(username), Option(password), host, Option(port), database)
    }
  }
}