-
Notifications
You must be signed in to change notification settings - Fork 64
Why asset pipeline over grails resources plugin
There are several advantages to using the asset-pipeline instead of the standard grails resources plugin.
- File dependencies are in the top of your assets. (No Resources.groovy)
- Assets in plugins become level with your app.
- On the fly processing in Development mode (No more waiting for reloads)
- Coffeescript, LESS, and others become first class citizens ( debuggable )
- Require entire folder trees with one line
- Better minification (UglifyJs) , and compiling before the WAR is built
- Faster application startup time
- Easy extensibility
Before we get too far, it is important to note a few structural differences. The asset-pipeline does not store its files in your web-app
directory, but rather a new directory generated in grails-app/assets
. Within that directory are various subfolder a useful for organizing your assets. It is also nice to note that you can make these subfolders with whatever names you like and their contents are all still treated as if they are in the root structure (i.e. if I have a file assets/libs/tinymce.js
and a file assets/javascripts/application.js
. They are still considered as being in the same relative path as each other and application can require tinymce by simply writing //=require tinymce
).
In asset-pipeline, your assets can define their own requirements. The idea being your manifest shouldn't be in a separate file that you have to keep jumping back to to maintain. Asset pipeline is also smart enough to detect doubly defined requirements and discern the correct order needed for your JavaScript or CSS to load correctly without double includes. First lets look at an example JavaScript application.js file:
//=require jquery
//=require ember
//=require handlebars
//=require_self
//=require_tree helpers
//=require_tree models
//=require_tree controllers
//=require_tree views
MyApp = Ember.Application.create({
ready: function() {
console.log("Application Launched!");
}
});
Notice the '//=' comments at the top of the file? Those are where you would define your "manifest" or list of dependent files. Asset-pipeline (by default) supports 3 types of "require" directives.
- require_self - controls the location of which this current files contents will be placed.
- require - requires a file given a relative path or absolute (if not found in relative path, the root is checked amongst all plugins)
- require_tree - requires all files of similar content type in the specified directory and subdirectories (does not span plugins).
And to include into your gsp file simply use the following tag:
<asset:javascript src="application.js"/>
Pretty nifty right? But what if I require my models yet a particular model needs to ensure another model is loaded first? How do we deal with that? Easy, lets say model Book requires model Author for an association:
/* file name: author.js */
MyApp.Author = MyApp.Model.extend({
id: null,
name: null
});
/* file name: book.js */
//= require author
MyApp.Book = MyApp.Model.extend({
id: null,
name: null,
author: MyApp.Association.BelongsTo(MyApp.Author)
});
Notice in our book.js file we require author. And in our application js we require all models with require_tree
. This little require will not cause a double inclusion and will insure a proper load order. This not only works with JavaScript, but with your CSS as well.
Assets in plugins are treated just like assets within your app, barring a few differences. The first, most important difference is that above all, your application assets take priority. If, for example, you have book.js in your assets directory and book.js in a plugins assets directory, your applications copy will take precedence and override the other file. The other difference is that the asset path for a plugin is both the grails-app/assets
folder, and the web-app
folder. This small difference was done to increase the usability if legacy plugins.
In development mode. Your assets are compiled on the fly (for the case of Coffeescript and other compiled languages as well). You may think this is slow, but recent advancements in Rhino have made this much more performant. This means you don't have to wait for the resources plugin to detect your changes and it becomes instantly available. In development mode, your assets are also automatically included into your layout file individually based on the dependency tree of the file. This preserves order and allows for easy debugging.
In production, the asset-pipeline compiles your assets on war create. This script will iterate over your assets and generated compiled, gripped, and cache-digested, minified versions of your files in the target/assets
folder. This folder is then copied into your war file.
Once your application is deployed, your compiled assets are now automatically served by tomcat. Options also exist to automatically copy these assets out to an external path for serving via a CDN or different web server (i.e. nginx).
//Config.groovy
grails {
assets {
storagePath = "/mypath/to/assets"
url = "/assets" // optional URL prefix
Because asset-pipeline doesn't preprocess your assets at startup, a significant portion of your load time goes away. It also becomes much quicker to deploy your applications and reduces the amount of downtime your server might have during the restart.
With asset-pipeline it becomes easy to extend and define new asset types. Here is a list of a few of the already available extension plugins:
- Coffee Grails Asset Pipeline (Coffeescript precompiler)
- LESS Grails Asset Pipeline (LESS precompiler)
- Handlebars Grails Asset Pipeline
- Ember Grails Asset Pipeline
While this was a rather long-winded article, by now you should have a good idea of the capabilities and ease of use of the asset pipeline. For more information check out the github or plugin page: