Permalink
Browse files

Added standalone build.

  • Loading branch information...
1 parent e96c85a commit a80a366460178ffa6752e403b5aaa90b37325602 @d6y d6y committed May 27, 2012
View
24 README.md
@@ -0,0 +1,24 @@
+Scalate Lift Module
+==================
+
+This module provides integration with Scalate.
+
+---
+
+See...
+
+* [Using Scalate with Lift](http://scalate.fusesource.org/documentation/lift.html).
+
+* [Example project using Scalate](https://github.com/lift/lift/tree/master/examples/helloscalate).
+
+
+
+---
+
+Notes for module developers
+===========================
+
+* The [Jenkins build](https://liftmodules.ci.cloudbees.com/job/scalate/) is triggered on a push to master.
+
+
+
View
79 build.sbt
@@ -0,0 +1,79 @@
+name := "scalate"
+
+liftVersion <<= liftVersion ?? "2.4"
+
+version <<= liftVersion apply { _ + "-1.0-SNAPSHOT" }
+
+organization := "net.liftmodules"
+
+scalaVersion := "2.9.1"
+
+crossScalaVersions := Seq("2.8.1", "2.9.0-1", "2.9.1")
+
+resolvers += "Java.net Maven2 Repository" at "http://download.java.net/maven/2/"
+
+libraryDependencies <++= liftVersion { v =>
+ "net.liftweb" %% "lift-webkit" % v % "compile->default" ::
+ Nil
+}
+
+libraryDependencies <++= scalaVersion { sv =>
+ "org.fusesource.scalate" % "scalate-core" % "1.4.1" ::
+ "javax.servlet" % "servlet-api" % "2.5" % "provided" ::
+ "org.scala-tools.testing" %% "specs" % (sv match {
+ case "2.8.0" => "1.6.5"
+ case "2.9.1" => "1.6.9"
+ case _ => "1.6.8"
+ }) % "test" ::
+ "org.scalacheck" %% "scalacheck" % (sv match {
+ case "2.8.0" => "1.7"
+ case "2.8.1" | "2.8.2" => "1.8"
+ case _ => "1.9"
+ }) % "test" ::
+ Nil
+}
+
+publishTo <<= version { _.endsWith("SNAPSHOT") match {
+ case true => Some("snapshots" at "https://oss.sonatype.org/content/repositories/snapshots")
+ case false => Some("releases" at "https://oss.sonatype.org/service/local/staging/deploy/maven2")
+ }
+ }
+
+
+// For local deployment:
+
+credentials += Credentials( file("sonatype.credentials") )
+
+// For the build server:
+
+credentials += Credentials( file("/private/liftmodules/sonatype.credentials") )
+
+publishMavenStyle := true
+
+publishArtifact in Test := false
+
+pomIncludeRepository := { _ => false }
+
+
+pomExtra := (
+ <url>https://github.com/liftmodules/scalate</url>
+ <licenses>
+ <license>
+ <name>Apache 2.0 License</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <scm>
+ <url>git@github.com:liftmodules/scalate.git</url>
+ <connection>scm:git:git@github.com:liftmodules/scalate.git</connection>
+ </scm>
+ <developers>
+ <developer>
+ <id>liftmodules</id>
+ <name>Lift Team</name>
+ <url>http://www.liftmodules.net</url>
+ </developer>
+ </developers>
+ )
+
View
13 project/LiftModule.scala
@@ -0,0 +1,13 @@
+import sbt._
+import sbt.Keys._
+
+object LiftModuleBuild extends Build {
+
+val liftVersion = SettingKey[String]("liftVersion", "Version number of the Lift Web Framework")
+
+val project = Project("LiftModule", file("."))
+
+}
+
+
+
View
5 sonatype.credentials.template
@@ -0,0 +1,5 @@
+realm=Sonatype Nexus Repository Manager
+host=oss.sonatype.org
+user=your-jira-login
+password=you-jira-password
+
View
141 src/main/scala/net/liftweb/scalate/LiftTemplateEngine.scala
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2010-2011 WorldWide Conferencing, LLC
+ *
+ * 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 net.liftweb
+package scalate
+
+import java.io.File
+
+import tools.nsc.Global
+
+import org.fusesource.scalate.layout.DefaultLayoutStrategy
+import org.fusesource.scalate.util.{ClassPathBuilder, FileResourceLoader}
+import org.fusesource.scalate.{DefaultRenderContext, ResourceNotFoundException, Binding, TemplateEngine}
+
+import common._
+import http.LiftRules
+import http.provider.servlet.HTTPServletContext
+
+
+/**
+ * A TemplateEngine using the Lift web abstractions.
+ */
+class LiftTemplateEngine extends TemplateEngine with Loggable {
+ bindings = List(Binding("context", classOf[DefaultRenderContext].getName, true, isImplicit = true))
+
+ if (useWebInfWorkingDirectory) {
+ val path = realPath("WEB-INF")
+ if (path != null) {
+ workingDirectory = new File(path, "_scalate")
+ workingDirectory.mkdirs
+ }
+ }
+ classpath = buildClassPath
+ resourceLoader = new LiftResourceLoader(this)
+ layoutStrategy = new DefaultLayoutStrategy(this, "/WEB-INF/scalate/layouts/default.scaml", "/WEB-INF/scalate/layouts/default.ssp")
+
+ private def buildClassPath(): String = {
+ val builder = new ClassPathBuilder
+
+ // Add containers class path
+ builder.addPathFrom(getClass)
+ .addPathFrom(classOf[TemplateEngine])
+ .addPathFrom(classOf[Product])
+ .addPathFrom(classOf[Global])
+
+ // Always include WEB-INF/classes and all the JARs in WEB-INF/lib just in case
+ builder.addClassesDir(realPath("/WEB-INF/classes"))
+ .addLibDir(realPath("/WEB-INF/lib"))
+
+ builder.classPath
+ }
+
+ def useWebInfWorkingDirectory = {
+ val customWorkDir = System.getProperty("scalate.workingdir", "")
+ val property = System.getProperty("scalate.temp.workingdir", "")
+ println("using scalate.temp.workingdir: " + property)
+ property.toLowerCase != "true" && customWorkDir.length <= 0
+ }
+
+ def realPath(uri: String): String = {
+ LiftRules.context match {
+ case http: HTTPServletContext => http.ctx.getRealPath(uri)
+ case c => logger.warn("Do not know how to get the real path of: " + uri + " for context: " + c); uri
+ }
+ }
+
+ class LiftResourceLoader(context: LiftTemplateEngine) extends FileResourceLoader {
+ override protected def toFile(uri: String) = {
+ realFile(uri)
+ }
+
+ protected def toFileOrFail(uri: String): File = {
+ val file = realFile(uri)
+ if (file == null) {
+ throw new ResourceNotFoundException(resource = uri, root = context.realPath("/"))
+ }
+ file
+ }
+
+ /**
+ * Returns the real path for the given uri
+ */
+ def realPath(uri: String): String = {
+ val file = realFile(uri)
+ if (file != null) file.getPath else null
+ }
+
+ /**
+ * Returns the File for the given uri
+ */
+ def realFile(uri: String): File = {
+ def findFile(uri: String): File = {
+ /*
+ val url = LiftRules.context.resource(uri)
+ if (url != null) {
+ url.toFile
+ }
+ else {
+ null
+ }
+ */
+ val path = context.realPath(uri)
+ logger.debug("realPath for: " + uri + " is: " + path)
+
+ var answer: File = null
+ if (path != null) {
+ val file = new File(path)
+ logger.debug("file from realPath for: " + uri + " is: " + file)
+ if (file.canRead) {answer = file}
+ }
+ answer
+ }
+
+ findFile(uri) match {
+ case file: File => file
+ case _ => if (uri.startsWith("/") && !uri.startsWith("/WEB-INF")) {
+ findFile("/WEB-INF" + uri)
+ }
+ else {
+ null
+ }
+ }
+ }
+
+ }
+
+}
+
View
100 src/main/scala/net/liftweb/scalate/ScalateView.scala
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2010-2011 WorldWide Conferencing, LLC
+ *
+ * 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 net.liftweb
+package scalate
+
+import xml.NodeSeq
+
+import common._
+import util._
+import http._
+
+
+/**
+ * A {@link LiftView} which uses a <a href="http://scalate.fusesource.org/">Scalate</a>
+ * template engine to resolve a URI and render it as markup
+ */
+class ScalateView(engine: LiftTemplateEngine = new LiftTemplateEngine()) extends LiftView with Logger {
+
+ /**
+ * Registers this view with Lift's dispatcher
+ */
+ def register: Unit = {
+ val scalateView: ScalateView = this
+
+ // TODO no idea why viewDispatch doesn't work, so lets just plugin the dispatcher instead
+ LiftRules.dispatch.prepend(NamedPF("Scalate Dispatch") {
+ case Req(path, ext, GetRequest) if (scalateView.canRender(path, ext)) => scalateView.render(path, ext)
+ })
+
+ // TODO view dispatch doesn't seem to do anything....
+/*
+ LiftRules.viewDispatch.prepend(NamedPF("Scalate View") {
+ case Req(path, ext, GetRequest) =>
+ info("scalate viewDispatch Path: " + path + " ext: " + ext)
+ Right(scalateView)
+ })
+*/
+ }
+
+
+ def dispatch: PartialFunction[String, () => Box[NodeSeq]] = {
+ case v if (canLoad(v)) =>
+ () => Full(engine.layoutAsNodes(v))
+ }
+
+
+ def canRender(path: List[String], ext: String): Boolean = {
+ debug("=== attempting to find: " + path + " ext: '" + ext + "'")
+
+ if (ext == "") {
+ canLoad(createUri(path, "scaml")) || canLoad(createUri(path, "ssp"))
+ }
+ else {
+ val uri = createUri(path, ext)
+ (uri.endsWith(".ssp") || uri.endsWith(".scaml")) && canLoad(uri)
+ }
+ }
+
+
+ def render(path: List[String], ext: String): () => Box[LiftResponse] = {
+ debug("attempting to render: " + path + " extension: " + ext)
+
+ () => {
+ val uri: String = if (ext != "") createUri(path, ext) else {
+ List("scaml", "ssp").map(createUri(path, _)).find(engine.canLoad(_)).get
+ }
+ Full(TextResponse(engine.layout(uri)))
+ }
+ }
+
+
+ protected def createUri(path: List[String], ext: String): String = path.mkString("/") +
+ (if (ext.length > 0) "." + ext else "")
+
+ protected def canLoad(v: String): Boolean = {
+ engine.canLoad(v)
+ }
+
+
+ case class TextResponse(text: String, headers: List[(String, String)] = Nil, code: Int = 200, contentType: String = "text/html; charset=utf-8") extends LiftResponse {
+ def toResponse = {
+ val bytes = text.getBytes("UTF-8")
+ InMemoryResponse(bytes, ("Content-Length", bytes.length.toString) :: ("Content-Type", contentType) :: headers, Nil, code)
+ }
+ }
+}

0 comments on commit a80a366

Please sign in to comment.