Rails plugin: Blazing fast page loads through bundled CSS and Javascript
What does it?
Basically, what the plugin does is:
- combine multiple CSS or Javascript files into one big file (bundle) on the fly
- compact this bundle by removing whitespace, comments and the like
- cache the bundle by using Rails default page caching mechanism
- create an "accesspoint" to the bundle by adding an appropriate route
It thereby removes the HTTP overhead for requesting several CSS and Javascript files to probably just request one CSS stylesheet and one Javascript file. There are some firebug screenshots in the Sitepoint article linked above which demonstrate quite nicely how significant this load overhead is in terms of user (speed) experience.
How to access the bundles?
Rakaz proposes the following syntax in his article to define a bundles contents and access them through an URL:
http://example.org/bundle/one.css,two.css,three.css
... which I think is ok, but can be done more concise - we don't really need the repetition of .css
because we wouldn't allow to combine CSS files with JS or other filetypes anyways.
It makes sense though to append the information that we're dealing with CSS files here as a regular file extension (.css
) because this helps with webserver configurations and the like. So this is preferable over:
http://example.org/bundle/css/one,two,three
Also, following Rails conventions with plural directory names like "stylesheets" and "javascripts" the better choice is "bundles" rather than "bundle" within a Rails plugin. So, combining these considerations I settled with this synatx:
http://example.org/bundles/one,two,three.css
What will be bundled?
Now, if you access the URL above and there are the files one.css
, two.css
and three.css
in your public/stylesheets
directory, the plugin will combine them into a single bundle file, compress them and page-cache them for future requests in the public/bundles
directory.
If there's a subdirectory one
(instead of a file one.css
), a recursive glob will be performed and and all CSS files in the subdirectory (and recursively all of its subdirectories) will be collected and merged into the bundle as well. This makes sense when you have a large number of separate files: you can access them through their directory name then.
If there's no correspondent file one.css
or directory one
at all this part silently will be ignored.
How does the compression work?
It's not really compressed, but compacted. I haven't build in any gzip/deflate compression because I can use Apache's mod_deflate for this task. This could be added easily though I guess.
Instead the plugin uses:
- Douglas Crockford's Ruby Javascript Minifier and
- some code from Scott Becker's AssetPackager (which by the way does something similar like this plugin but takes a different approach)
... to remove whitespace, comments and the like from Javascript and CSS files.
How to install?
You can install this plugin simply by using Rails script/plugin installer. Standing in your Rails root directory do:
script/plugin install http://svn.artweb-design.de/stuff/rails/bundled_assets/
Also, up to now you'll have to add a route to your routes setup manually. Therefor edit your config/routes.rb file and add the following line (somewhere above Rails' default routes):
map.connect 'bundles/:names.:ext', :controller => 'assets_bundle', :action => 'fetch', :ext => /css|js/, :names => /[^.]*
How to use?
After you've successfully installed this stuff (do not forget to restart your server) you can use it in your templates (again, do not forget to invalidate your page caches if necessary). E.g., the layout template of this blog contains the following lines:
<link rel="stylesheet" href="/bundles/layout,styles,search-tag.css" type="text/css" media="screen" charset="utf-8" />
<script src="/bundles/prototype,tools.js" type="text/javascript" charset="utf-8"></script>
Update: In the comments, Nick Pearson posted a super-useful tip about how to use this plugin together with the built-in Rails helpers for javascripts and stylesheets. I've posted a separate article about this setup here: How to use the Bundled assets plugin with Rails' built-in tag helpers.
Possible improvements? Problems?
Besides integrating basic gzip output compression the plugin could use some configuration options such as whether or not subdirectory contents should be bundled, whether or not the code should be compacted (and how).
Update:In the meantime some work has been done and some of these options have been added. See this article (also for much improved performance through a compiled version of JSMin): Serve CSS and Javascript even faster: improved Rails bundled_assets plugin.
Also, there's currently no solution for a possible problem with relative image URLs in CSS files that are bundled from within subdirectories (and this way "transferred" to upperlevel directories). Using absolute URLs should work in this case though.
Feedback?
What do you think?