Skip to content

Instantly share code, notes, and snippets.

@Shadowfiend
Created May 25, 2012 21:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Shadowfiend/2790765 to your computer and use it in GitHub Desktop.
Save Shadowfiend/2790765 to your computer and use it in GitHub Desktop.
Lift snippet for bundle support in conjunction with Shadowfiend/sbt-resource-management.
package com.openstudy { package snippet {
import scala.xml._
import java.io.InputStreamReader
import net.liftweb.common._
import net.liftweb.http._
import LiftRules._
import net.liftweb.util._
import Helpers._
case class BundleInfo(name:String, checksumInFilename:Boolean,
parts:List[String], version:String)
object Bundles {
val assetDomain = "devsets.openstudy.com"
private def readResource(resourcePath:String) : Box[String] = {
for {
url <- LiftRules.getResource(resourcePath)
reader <- tryo(new InputStreamReader(url.openStream, "UTF-8"))
} yield {
readWholeThing(reader)
}
}
private def bundleVersionsFrom(data:String) : (Boolean, Map[String,String]) = {
var checksumInFilename = false
val filesAndVersions =
Map[String,String]() ++ data.split("\n").flatMap { line =>
line.split("=") match {
case Array("checksum-in-filename", value) =>
checksumInFilename = value == "true"
Nil
case Array(bundle, version) => List((bundle, version))
case _ => List[(String,String)]()
}
}
(checksumInFilename, filesAndVersions)
}
private def bundlesFrom(data:String) : Map[String,List[String]] = {
Map[String,List[String]]() ++ data.split("\n\n").flatMap { bundle =>
bundle.split("\n").toList match {
case bundle :: rest =>
List((bundle, rest))
case Nil =>
List[(String,List[String])]()
}
}
}
private def bundlesForType(bundleType:String) = {
Map[String,BundleInfo]() ++
(for {
bundleData <- readResource("/bundles/" + bundleType + ".bundle").toList
bundleVersionData = readResource("/bundles/" + bundleType + "-bundle-versions") openOr ""
(checksumInFilename, versions) = bundleVersionsFrom(bundleVersionData)
(bundle, parts) <- bundlesFrom(bundleData)
version = versions.getOrElse(bundle, nextNum.toString)
} yield {
(bundle, BundleInfo(bundle, checksumInFilename, parts, version))
})
}
private val scriptBundles = bundlesForType("javascript")
private val styleBundles = bundlesForType("stylesheet")
def snippetHandlers : SnippetPF = {
case List("script-bundle") => scriptBundle _
case List("style-bundle") => styleBundle _
}
/**
* Looks up the passed bundle name, finds it in the list, sets up
* the URI for it based on the given urlBase and file extension, and
* passes the resulting URI to the tag generator for the bundle.
*
* If lookup for the bundle fails, returns a NodeSeq.Empty.
*/
private def bundleTagFor(bundleList:Map[String,BundleInfo], urlBase:String, extension:String, tagGenerator:(String)=>NodeSeq) = {
{
for {
name <- S.attr("name")
bundle <- bundleList.get(name)
} yield {
val bundleFilename =
if (bundle.checksumInFilename)
bundle.name + "-" + bundle.version + "." + extension
else
bundle.name + "." + extension + "?" + bundle.version
tagGenerator("http://" + assetDomain + urlBase + "/" + bundleFilename)
}
} openOr {
NodeSeq.Empty
}
}
private def expandedTagsFor(bundleList:Map[String,BundleInfo], urlBase:String, tagGenerator:(String)=>NodeSeq) = {
def filesForBundle(bundle:BundleInfo) : List[String] = {
bundle.parts.flatMap { fileOrBundle =>
expandedFilesFor(fileOrBundle)
}
}
def expandedFilesFor(fileOrBundle:String) : List[String] = {
if (fileOrBundle.contains("."))
fileOrBundle :: Nil
else
bundleList.get(fileOrBundle).toList.flatMap(filesForBundle(_))
}
for {
name <- S.attr("name").toList
bundle <- bundleList.get(name).toList
file <- filesForBundle(bundle)
} yield {
tagGenerator(LiftRules.attachResourceId(urlBase + "/" + file))
}
}
private def scriptBundle(ns:NodeSeq) : NodeSeq = {
def scriptTag(uri:String) = <script type="text/javascript" src={uri}></script>
Props.mode match {
case Props.RunModes.Development =>
expandedTagsFor(scriptBundles, "/javascripts", scriptTag _).flatten
case _ =>
bundleTagFor(scriptBundles, "/javascripts", "js", scriptTag _)
}
}
private def styleBundle(ns:NodeSeq) : NodeSeq = {
def styleTag(uri:String) = <link type="text/css" rel="stylesheet" href={uri} />
Props.mode match {
case Props.RunModes.Development =>
expandedTagsFor(styleBundles, "/stylesheets", styleTag _).flatten
case _ =>
bundleTagFor(styleBundles, "/stylesheets", "css", styleTag _)
}
}
}
} }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment