CLOVER🍀

That was when it all began.

Scalate 1.7を試してみる

ちょっとしたことがしたくてScalateを別のものと組み合わせて使おうとしてみたのですが、どハマりしたので切り分けを兼ねて単品で使ってみることに。

ScalateでScala 2.11に対応したものを使いたければ、Scalate 1.7を使う、でいいんですよね、きっと…。

というわけで作ったbuild.sbt。

name := "scalate-example-1.7"

version := "0.0.1-SNAPSHOT"

scalaVersion := "2.11.2"

organization := "org.litlewings"

scalacOptions ++= Seq("-Xlint", "-deprecation", "-unchecked", "-feature")

incOptions := incOptions.value.withNameHashing(true)

libraryDependencies ++= Seq(
  "org.scalatra.scalate" %% "scalate-core" % "1.7.0"
)

groupId(organization)変わった?

で、試しに作ったコード。
src/main/scala/org/littlewings/scalate/ScalateExample.scala

package org.littlewings.scalate

import java.io.File

import org.fusesource.scalate.TemplateEngine

object ScalateExample {
  def main(args: Array[String]): Unit = {
    val engine = new TemplateEngine
    engine.workingDirectory = new File("./tmp")

    val bindings =
      Map("name" -> "Scalate",
          "languages" -> List("Java", "Scala", "Groovy", "Clojure"))

    val output = engine.layout("src/main/resources/template/hello.mustache", bindings)
    println(output)
  }
}

テンプレートのコンパイル時の出力先は、「tmp」としました。

    engine.workingDirectory = new File("./tmp")

テンプレート。Mustacheにしました。
src/main/resources/template/hello.mustache

Hello {{name}} by Mustache Template
Programming Languages:
{{#languages}}
  {{.}}
{{/languages}}

実行。

> run
[info] Compiling 1 Scala source to /xxxxx/scalate-example-1.7/target/scala-2.11/classes...
[info] Running org.littlewings.scalate.ScalateExample 
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Hello Scalate by Mustache Template
Programming Languages:
Java
Scala
Groovy
Clojure

[success] Total time: 2 s, completed 2014/08/23 11:27:20

…普通に動いた。やっぱ、合ってるんだ。

クラスパスからロードしてみたい

ちょっとやりたいことのひとつとして。テンプレートを、クラスパスからロードしてみようと。

テンプレートをロードするのはResourceLoaderというものにやらせるのですが、普通にやるとFileResourceLoader(とServletResourceLoader)しかないですよね…?

というわけで、まずは先ほど書いたimport文を修正。

import java.io.{File, InputStreamReader}
import java.nio.charset.StandardCharsets

import org.fusesource.scalate.TemplateEngine
import org.fusesource.scalate.util.{Resource, ResourceLoader, StringResource}

クラス作成。

class ClassPathResourceLoader extends ResourceLoader {
  override def resource(uri: String): Option[Resource] = {
    val cl = Thread.currentThread.getContextClassLoader

    val is = cl.getResourceAsStream(uri)

    if (is != null) {
      val reader = new InputStreamReader(is, StandardCharsets.UTF_8)
      try {
        val text =
          Iterator
            .continually(reader.read())
            .takeWhile(_ != -1)
            .map(_.asInstanceOf[Char])
            .mkString

        Some(StringResource(uri, text))
      } finally {
        is.close()
      }
    } else {
      None
    }
  }
}

テンプレート利用箇所変更。

    val engine = new TemplateEngine
    engine.workingDirectory = new File("./tmp")

    engine.resourceLoader = new ClassPathResourceLoader

    val bindings =
      Map("name" -> "Scalate",
          "languages" -> List("Java", "Scala", "Groovy", "Clojure"))

    val output = engine.layout("template/hello.mustache", bindings)
    println(output)

実行。

> run
[info] Running org.littlewings.scalate.ScalateExample 
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Hello Scalate by Mustache Template
Programming Languages:
Java
Scala
Groovy
Clojure

[success] Total time: 2 s, completed 2014/08/23 11:33:21

…普通に動いた。まあ、1回のテンプレート評価でResourceLoader#resourceが何度か呼び出されているようですが、いいでしょう。

というわけで、普通に使えてしまったので、いったんここまで…。

参考)
Scalate Embedding Guide
http://scalate.fusesource.org/documentation/scalate-embedding-guide.html
scalate-project(ここから1.6-SNAPSHOTのScaladoc)
http://scalate.fusesource.org/maven/1.6.0-SNAPSHOT/

作成したコード全体は、こちら。
src/main/scala/org/littlewings/scalate/ScalateExample.scala

package org.littlewings.scalate

import java.io.{File, InputStreamReader}
import java.nio.charset.StandardCharsets

import org.fusesource.scalate.TemplateEngine
import org.fusesource.scalate.util.{Resource, ResourceLoader, StringResource}

object ScalateExample {
  def main(args: Array[String]): Unit = {
    val engine = new TemplateEngine
    engine.workingDirectory = new File("./tmp")

    engine.resourceLoader = new ClassPathResourceLoader

    val bindings =
      Map("name" -> "Scalate",
          "languages" -> List("Java", "Scala", "Groovy", "Clojure"))

    val output = engine.layout("template/hello.mustache", bindings)
    println(output)
  }
}

class ClassPathResourceLoader extends ResourceLoader {
  override def resource(uri: String): Option[Resource] = {
    val cl = Thread.currentThread.getContextClassLoader

    val is = cl.getResourceAsStream(uri)

    if (is != null) {
      val reader = new InputStreamReader(is, StandardCharsets.UTF_8)
      try {
        val text =
          Iterator
            .continually(reader.read())
            .takeWhile(_ != -1)
            .map(_.asInstanceOf[Char])
            .mkString

        Some(StringResource(uri, text))
      } finally {
        is.close()
      }
    } else {
      None
    }
  }
}