2012年12月27日木曜日

sbt で設定ファイルを本番用に切り替え

Scala の勉強をかなりさぼっていたわけで・・・
構文も大分忘れている状態なんですが、またちょっとはじめることに。
というか実践。

sbt を使ってちょっとしたバッチを開発時に jar に含める設定ファイル(ログレベル設定等)を、開発、ステージング、本番用に切り替えたくなった。そして嵌った・・・約 2 日・・・

とりあえず設定ファイルの構成は、Java の時にやっていた構成で、
+ src
  + main (開発)
    + scala
    + resources
      + logback.xml
      + ...
  + product (本番)
    + resources
      + logback.xml
  + staging (ステージング)
    + resources
      + logback.xml
の構成で、product/resources, staging/resources には上書きするもののみを置くこととする。

始める sbt のスコープ軸を見ると、タスク毎に設定が出来そう。ということで、最初のアプローチは、pack-staging, pack-product のタスクを生成して、タスクのスコープで unmanagedResourceDirectory を追加しようとした。が、そこから呼び出す copy-resources は copy-resources in Compile になってしまい挫折・・・これに 1 日以上費やしたわけだが・・・結局わからなかった・・・

で、maven の profile 的なことができないかと検索してみると参考になるサイトがあった。
ここを参考に、
case class Profile(name: String)

object Profile {
 val staging = Profile("staging")
 val product = Profile("product")

 implicit def toOption(profile: Profile): Option[Profile] = Option(profile)
}
として、
  unmanagedResourceDirectories in Compile <<= (sourceDirectory, selectedProfile, unmanagedResourceDirectories in Compile)
    ((dir, env, res) => env.map((dir / _.name / "resources")).toSeq ++ res),
  unmanagedResources in Compile~= (_.groupBy((_: java.io.File).getName).map(_._2.head).toSeq),
リソースディレクトリの設定時に振り分け。さらに、リソースファイル設定時に一意になるようグループ化しているようなしていないような・・・いまいち何をやっているかわからないが完成・・・ 

記号がさっぱりわからん・・・Perl とまでは言わんが・・・演算子でなくメソッドなので色々できるわけだが、C++ みたいに標準的な演算子のみオーバーライドできればいいのに・・・ ライブラリ毎に覚えるのは大変だ・・・ 標準的な意味合いの記号があるのだろうか。

 とりあえず sbt の API ドキュメントより、Setting[T] の <<= メソッドを見てみると trait DefinableSetting[S] に定義があり、
final def <<=(app: Initialize[T]): Setting[T]
なるほど、Initialize[T] を受け取る。 unmanagedResourceDirectories は SettingKey[Seq[File]] なので、右辺の
(sourceDirectory, selectedProfile, unmanagedResourceDirectories in Compile)
    ((dir, env, res) => env.map((dir / _.name / "resources")).toSeq ++ res)
は、Initialize[Seq[File]] を返すのであろう。

で、右辺部分だが、
(sourceDirectory, selectedProfile, unmanagedResourceDirectories in Compile)(なんかの処理)
つまり、
(タプル).apply(なんかの処理)
のように見えるが、タプルに対してそんなことできるのか・・・おそらくは暗黙の型変換・・・
というわけで、ソースコードを implisit で grep すると、Structure.scala で
implicit def t3ToApp3[A,B,C](t3: (Initialize[A], Initialize[B], Initialize[C]) ): Apply3[A,B,C] = new Apply3(t3)
というのを発見。さらに、Apply3 をみると、
def apply[T](z: (A,B,C) => T) = Project.app( k3(t3) )( hf3(z) )
となっている。さらに、object Project の API ドキュメントに、Init[Scope] に app の定義があり
def app[HL <: HList, T](inputs: KList[Initialize, HL])(f: (HL) ⇒ T): Initialize[T]
なので、Initialize[Seq[File]] が返りそう。なんか予想が多いが・・・

次に、なんかの処理部分は、Apply3 の apply の引き数なので、
(A,B,C) => T
最初のタプルの設定の値がそれぞれ渡り、T つまり、Seq[File] を返す関数を渡せばよさそう。
unmanagedResourceDirectories が SettingKey[Seq[File]] なので、res は Seq[File] とおもわれる。
そして、生成したパスと res を連結したものが返ると。

最終的に返った、Initialize[Seq[File]] で unmanagedResourceDirectories in Compile を設定しているようだ。
とりあえず、今回はここまで。
追うのだけでも大変・・・自分でカスタマイズ出来る気がしない・・・Scala は嫌いじゃないけど慣れないと、読むのも困難だな・・・

0 件のコメント:

コメントを投稿