Skip to content

Instantly share code, notes, and snippets.

@eboto
Created November 2, 2012 20:15
Show Gist options
  • Save eboto/4004051 to your computer and use it in GitHub Desktop.
Save eboto/4004051 to your computer and use it in GitHub Desktop.
Play 2 Assets pipeline
package assetproviders {
import play.api.mvc.Action
import play.api.mvc.Controller
import play.api.mvc.AnyContent
import play.api.mvc.Call
/**
* This simple interface is meant to mimic the existing interface in Play 2.0
* for the Assets controller it provides. By implementing this it is possible
* to mix and combine various AssetProviders to create a custom Asset controller
* with additional functionality.
*/
trait AssetProvider { this: Controller =>
/**
* This is to be implemented by the concrete class and is supposed to be a
* call to the reverse router for the at(path, file) call.
*/
protected def assetReverseRoute(file: String): Call
/**
* This is the method that will be called by the router to serve the
* asset to the client.
*/
def at(path: String, file: String): Action[AnyContent]
/**
* This is the method that will be called by the templates mostly to get
* a Call that enables them to get the external URL of the asset.
*/
def at(file: String): Call
}
/**
* An AssetProvider view of Play's built in Asset controller that can be mixed
* into other AssetProviders.
*/
trait PlayAssets extends AssetProvider { this: Controller =>
override def at(path: String, file: String): Action[AnyContent] = controllers.Assets.at(path, file)
override def at(file: String): Call = assetReverseRoute(file)
}
/**
* Pipelines fingerprinting for your static assets, which allows you to improve site
* performance by setting very long cache expiries. Assets are fingerprinted like this:
* original = foo.jpg
* fingerprinted = foo-fp-1231343451234.jpg
*
* Where '-fp-1231343451234' is the 'fingerprint' and '1231343451234' is a checksum of
* the file contents.
*
* We got some inspiration from ruby on rails, but the solution was a bit obvious.
* http://guides.rubyonrails.org/asset_pipeline.html#what-is-fingerprinting-and-why-should-i-care
*/
trait FingerprintedAssets extends AssetProvider { this: Controller =>
def defaultPath: String
val cacheControlMaxAgeInSeconds: Int
/**
* This will find files that were fingerprinted. If the file is found with the fingerprint
* striped out of its name and the checksum matches the checksum in the fingerprint. Otherwise
* it will just return whatever the super.at(path, file) returns.
*/
abstract override def at(path: String, file: String): Action[AnyContent] = { /* impl */ }
abstract override def at(file: String): Call = { /* impl */ }
}
/**
* Pipelines CDN access for static files. Mix this trait in and provide remoteContentUrl to
* have all calls from your views to your assets automatically resolve to a url with the
* correct domain.
*
* Inspired by http://www.jamesward.com/2012/08/08/edge-caching-with-play2-heroku-cloudfront
*/
trait RemoteAssets extends AssetProvider { this: Controller =>
/**
* This application's content URL with protocol. For example "https://souefusfisu.cloudfront.net"
*/
protected def remoteContentUrl: Option[String]
abstract override def at(path: String, file: String): Action[AnyContent] = { /* impl */ }
abstract override def at(file: String): Call = { /* impl */ }
}
}
package controllers {
import play.api.Play.current
import play.api.mvc.Controller
import assetproviders.RemoteAssets
import assetproviders.FingerprintedAssets
import assetproviders.PlayAssets
/** Collects all asset transformations into a single trait for use in our website app */
trait FullAssetPipeline
extends PlayAssets with RemoteAssets with FingerprintedAssets { this: Controller => }
/**
* The concrete asset implementation for our website app. This maps to /assets
* in the routes file
*/
object EgraphsAssets extends Controller with FullAssetPipeline {
// This object has a route in our routes file
override def assetReverseRoute(file: String) = controllers.routes.EgraphsAssets.at(file)
// MIA: boring implementations of abstract methods from the pipeline
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment