Play FrameworkでScalateを使うときにハマったこと
sbt-scalate-precompiler と実行時コンパイルを共存させる
GitHub - scalate/sbt-scalate-precompiler: Scalate Templates Precompiler
これ、リリース時にプリコンパイルしてくれてとても便利なんだけど、 Playと一緒に使うと難点があって、
ホットデプロイモードでリロードするたび2回コンパイルが走ってしまう
んです。 ただでさえ遅いコンパイルがさらに遅く。
そこで、開発時には実行時コンパイル、リリース時にはプリコンパイル、という風に設定を分けてみました。 実行時コンパイルであれば、ページにアクセスしたときコンパイルするだけになり、多少早くなります。
def settings(prefix: String) = ScalatePlugin.scalateSettings ++ Seq( ScalateKeys.scalateOverwrite := false, ScalateKeys.scalateTemplateConfig in Compile <<= (baseDirectory in Compile) { base => Seq( ScalatePlugin.TemplateConfig( base / "app" / "some" / "packages" / prefix / "views", Seq(), Some(prefix) // パッケージプレフィックスを使う!! ) ) } ) lazy val compileCopyTask = taskKey[Unit](s"Copy ssp.") def devSettings(prefix: String) = Seq( compileCopyTask := { println(s"Start copying ssp") val mainVersion = scalaVersion.value.split("""\.""").take(2).mkString(".") val to = target.value / ("scala-" + mainVersion) / "classes" / prefix to.mkdirs() val from = baseDirectory.value / "app" / "some" / "packages" / prefix / "views" IO.copyDirectory(from,to) println(s"$from -> $to...done.") }, compile in Compile <<= (compile in Compile) dependsOn compileCopyTask ) val isDev = sys.env.getOrElse("MY_PLAY_ENV", "") == "dev" lazy val root = (project in file(".")).enablePlugins(PlayScala).settings(if (isDev) devSettings else settings)
こんな感じで、build.sbt
を書きます。
環境変数MY_PLAY_ENV
によって、コンパイル時の動作が変わります。
settings
はsbt-scalate-precompilerのドキュメントの通りです。
devSettings
は、target
ディレクトリ配下にテンプレートファイルを配置しています。
パッケージプレフィックスを使わなければ、
unmanagedResourceDirectories in Compile += baseDirectory.value / "app" / "some" / "packages" / "views"
の一行で済むのですが、パッケージプレフィックスを使うと、その名前のティレクトリ配下に置くようなパス指定になります。
val engine = new TemplateEngine engine.layout("/prefix/index.ssp") ^^^^^^ <- これ
なので、target/scala-2.11/prefix/index.ssp
のように配置する実装が必要になりました。
TemplateEngine
インスタンスは明示的にGC対象にする
PlayとScalateの実行時コンパイルを一緒に使うにあたって、もう一つ問題がありました。
ホットデプロイモードで数回リロードすると死ぬほど重くなる
原因ははっきりしていませんが、TemplateEngine
インスタンスを消さずにリロードすると重くなるようです。
なので、Playアプリケーションのシャットダウン時に、インスタンスを解放するようにしてみました。
// どこかの処理 var engine = new TemplateEngine // PlayのGlobalクラス object Global extends GlobalSettings { ... override def onStop(app: Application): Unit = { engine = null } ... }
(Playのバージョン低いので適宜読み替えてください)
とやると、処理が劇的に重くなることがなくなりました。
原因不明なのでスマートな解ではないかと思いますが、困っていたら試してみてください。