Skip to content

Instantly share code, notes, and snippets.

@kevinwright
Created July 20, 2018 08:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevinwright/359329efcb166ec4f6ce59a228ee715f to your computer and use it in GitHub Desktop.
Save kevinwright/359329efcb166ec4f6ce59a228ee715f to your computer and use it in GitHub Desktop.
Handlebars.java -> scala adaptor
import java.{util => ju}
import com.github.jknack.handlebars.context.{JavaBeanValueResolver, MapValueResolver, MethodValueResolver}
import com.github.jknack.handlebars._
import com.github.jknack.handlebars.helper.EachHelper
import com.github.jknack.handlebars.io.ClassPathTemplateLoader
import scala.reflect.runtime.{universe => ru}
import scala.util.Try
import scala.collection.JavaConverters._
class HandlebarsEngine {
val loader = new ClassPathTemplateLoader
loader.setPrefix("/WEB-INF/")
loader.setSuffix(".hbs")
val handlebars = new Handlebars(loader)
val rootMirror = ru.runtimeMirror(getClass.getClassLoader)
def methodMirrorFor(context: AnyRef, name: String): Option[ru.MethodMirror] = {
val meta = rootMirror.reflect(context)
val optAccessor = meta.symbol.info.decls find { m =>
m.isMethod && m.isPublic && m.name.toString == name
}
optAccessor.map(a => meta.reflectMethod(a.asMethod))
}
object ScalaResolver extends ValueResolver {
override def resolve(context: AnyRef, name: String): AnyRef = context match {
case m: collection.Map[_,_] => MapValueResolver.INSTANCE.resolve(m.asJava, name)
case _ =>
val optMM = methodMirrorFor(context, name)
val ret = optMM.fold(ValueResolver.UNRESOLVED)(m => resolve(m.apply())): AnyRef
println(s"...returning ${ret.toString}")
ret
}
override def resolve(context: scala.Any): AnyRef = context match {
case m: collection.Map[_,_] => MapValueResolver.INSTANCE.resolve(m.asJava)
case Some(x: AnyRef) => x
case None => null
case x: AnyRef => x
}
override def propertySet(context: scala.Any): ju.Set[ju.Map.Entry[String, AnyRef]] = context match {
case m: collection.Map[_,_] =>
MapValueResolver.INSTANCE.propertySet(m.asJava)
case _ =>
println(s"ScalaMemberResolver.propertySet in context: [${context.getClass.getName}]")
val meta = rootMirror.reflect(context)
val accessors = meta.symbol.info.decls.filter(m => m.isMethod && m.isPublic).toSeq
val results = for {
a <- accessors
v <- Try(meta.reflectMethod(a.asMethod).apply()).toOption
} yield a.name.toString -> v.asInstanceOf[AnyRef]
results.toMap.asJava.entrySet
}
}
object ScalaEachHelper extends Helper[AnyRef] {
override def apply(context: scala.AnyRef, options: Options): AnyRef = context match {
case iter: Iterable[_] => EachHelper.INSTANCE.apply(iter.asJava, options)
case _ => EachHelper.INSTANCE.apply(context, options)
}
}
handlebars.registerHelper("each", ScalaEachHelper)
handlebars.infiniteLoops(true)
def mkContext(props: Map[String, AnyRef]) = Context
.newBuilder(props)
.resolver(
ScalaResolver,
MapValueResolver.INSTANCE,
MethodValueResolver.INSTANCE,
JavaBeanValueResolver.INSTANCE,
).build()
def render(fullPath: String, props: Map[String, AnyRef]): String = {
val path = fullPath.dropRight(4)
println(s"HandlebarsEngine rendering $path")
val template = handlebars.compile(path)
template(mkContext(props))
}
def render(path: String, props: (String, AnyRef)*): String = render(path, props.toMap)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment