技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

Thymeleaf をPlayframework2.2.Xで無理やり使ってみた。

Java のテンプレートエンジン - 謎言語使いの徒然 でさくっと見たテンプレートを、じゃぁPlayframeworkでどうやるんだろうと、ドキュメント眺めながらやってみた。

で、まずはファイルが何処に配置されてるのかを返すクラスを作成。

package utils

import org.thymeleaf.resourceresolver.{FileResourceResolver, IResourceResolver}
import org.thymeleaf.TemplateProcessingParameters
import java.io.InputStream

class PlayThymeResourceResolver(viewPath: String) extends IResourceResolver {
  lazy val fileResourceResolver = new FileResourceResolver

  def getName: String = "PLAY_RESOURCE_RESOLVER"

  def getResourceAsStream(templateParameters: TemplateProcessingParameters, resourceName: String): InputStream = {
    this.fileResourceResolver.getResourceAsStream(templateParameters, s"$viewPath:$resourceName")
  }
}

それをテンプレートで食うようにして

package utils

import org.thymeleaf.templateresolver.TemplateResolver

class PlayThymeTemplateResolver(val viewLookupPath: String) extends TemplateResolver {
  setResourceResolver(new PlayThymeResourceResolver(viewLookupPath))
}

テンプレートエンジンでは多言語化対応と、Mapで突っ込めるようにしておく

package utils

import org.thymeleaf.{Arguments, TemplateEngine}
import org.thymeleaf.messageresolver.{MessageResolution, AbstractMessageResolver}
import play.api.i18n.Messages
import org.thymeleaf.context.Context
import scala.collection.JavaConversions._

class PlayTemplateEngine extends TemplateEngine {
  setMessageResolver(new AbstractMessageResolver {
    def resolveMessage(p1: Arguments, p2: String, p3: Array[AnyRef]): MessageResolution = {
      new MessageResolution(Messages(p2, p3))
    }
  })

  def process(path:String, variables: Map[String, AnyRef]):String = {
    val ctx = new Context()
    ctx.setVariables(variables)
    process(path, ctx)
  }
}

後はこれをシングルトンにでもしておけば

package utils

import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver
import org.thymeleaf.resourceresolver.ClassLoaderResourceResolver
import org.thymeleaf.TemplateEngine
import play.api.Play

object PlayTemplateEngine {
  lazy val templateEngine = {
    val config = Play.current.configuration
    val engine = new PlayTemplateEngine

    val path   = config.getString("thymeleaf.path").getOrElse("app/views/")
    val suffix = config.getString("thymeleaf.suffix").getOrElse(".html")
    val mode   = config.getString("thymeleaf.mode").getOrElse("HTML5")
    val ttl    = config.getLong("thymeleaf.ttl").getOrElse(60000L) // default 1minute

    val templateResolver = new PlayThymeTemplateResolver(path)
    templateResolver.setSuffix(suffix)
    templateResolver.setTemplateMode(mode)
    templateResolver.setCacheTTLMs(ttl)
    templateResolver.setCharacterEncoding("UTF-8")

    engine.setTemplateResolver(templateResolver)
    engine
  }
}

Controller で呼べる

  def sample = Action { implicit request =>
    val ctx = new Context()
    ctx.setLocale(lang.toLocale)
    ctx.setVariable("name", "行くぜおい!")

    val res = Template.templateEngine.process("sample", ctx)
    Ok(res).as("text/html")
  }

そして、views にテンプレートを配置して出来上がり。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Test</title>
</head>
<body>
  <h1 th:text="${name}">Hello Sample</h1>
</body>
</html>

日本語化対応の扱いが Play と Thymeleaf で異なるので作ったメソッドがそのまま呼べない図。 ただ、レスポンスに「as」入れないと、text/plain で返ってしまうので、何か考えないとだめっぽい。