All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.finatra.View.scala Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2012 Twitter Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.twitter.finatra

import com.github.mustachejava._
import com.google.common.base.Charsets
import com.twitter.mustache._
import java.io._
import java.util.concurrent.Callable
import java.util.concurrent.Executors

class FinatraMustacheFactory(baseTemplatePath:String) extends DefaultMustacheFactory {
  private def combinePaths(path1: String, path2: String): String = {
    new File(new File(path1), path2).getPath
  }

  protected def getReaderLocal(resourceName:String): BufferedReader = {
    val basePath = combinePaths(config.docRoot(), config.templatePath())
    val file: File = new File(basePath, resourceName)

    if (file.exists() && file.isFile()) {
      try {
        new BufferedReader(new InputStreamReader(new FileInputStream(file),
          Charsets.UTF_8))
      } catch {
        case exception: FileNotFoundException =>
          throw new MustacheException("Found Mustache file, could not open: " + file + " at path: " + basePath, exception)
      }
    }
    else {
      throw new MustacheException("Mustache Template '" + resourceName + "' not found at " + file + " at path: " + basePath);
    }
  }

  override def getReader(resourceName:String): Reader = {
    val mustacheTemplateName = if (resourceName contains ".mustache") resourceName else resourceName + ".mustache"

    if (!"development".equals(config.env())) {
      super.getReader(mustacheTemplateName)
    }
    // In development mode, we look to the local file
    // system and avoid using the classloader which has
    // priority in DefaultMustacheFactory.getReader
    else {
      try {
        getReaderLocal(mustacheTemplateName)
      } catch {
        // fallback to classpath, for templates distributed with dependencies
        case e: MustacheException => super.getReader(mustacheTemplateName)
      }
    }
  }

  /**
   * Invalidate template caches during development
   */
  def invalidateMustacheCaches() : Unit = {
    mustacheCache.invalidateAll()
    templateCache.invalidateAll()
  }

}

object View {
  var baseTemplatePath      = config.templatePath()
  def templatePath          = baseTemplatePath
  lazy val mustacheFactory  = new FinatraMustacheFactory(baseTemplatePath)

  mustacheFactory.setObjectHandler(new TwitterObjectHandler)
  mustacheFactory.setExecutorService(Executors.newCachedThreadPool)

  private def combinePaths(path1: String, path2: String): String = {
    new File(new File(path1), path2).getPath
  }
}

abstract class View extends Callable[String] {

  def template:String

  def templatePath: String            = View.combinePaths(baseTemplatePath, template)
  val factory: FinatraMustacheFactory = View.mustacheFactory
  var baseTemplatePath: String        = View.templatePath
  var contentType: Option[String]     = None
  def mustache: Mustache              = factory.compile(
    new InputStreamReader(FileResolver.getInputStream(templatePath)), template
  )

  def render: String = {
    // In development mode, we flush all of our template
    // caches on each render. Otherwise, partials will
    // remain unchanged in the browser while being edited.
    if ("development".equals(config.env())) {
      factory.invalidateMustacheCaches()
    }
    val output = new StringWriter
    mustache.execute(output, this).flush()
    output.toString
  }

  def call: String = render
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy