Sprockets is a Ruby library for compiling and serving web assets. It features declarative dependency management for JavaScript and CSS assets and a very powerful preprocessor pipeline that allows you to write assets in languages like CoffeeScript, Sass or SCSS.
Sprockets is now integrated in Rails 3.1 and is the core library behind the new Rails 3.1 asset pipeline. What if you want to use Sprockets outside a Rails project?
Sprockets exposes a very powerful Rack interface to serve assets over HTTP. Integrating Sprockets in a Rack application, such as a Sinatra project, turns out to be a very straightforward task.
But in my case, I wanted to use Sprockets preprocessing and bundling features outside an HTTP application. And it turned out Sprockets is very good at doing this as well.
I have a custom shared template I created several months ago called docss. I use this for several projects such as Ruby Whois and RoboWhois. The template is composed of several CSS files. I have a Ruby rake script that merges these files and packages them into a single asset, then compresses it and publishes the result to Amazon CloudFront.
Instead of processing and merging the files using filesystem tasks I'm now delegating this task to Sprockets. The project directory structure looks like this:
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── build
│ ├── javascripts
│ │ └── all.js
│ └── stylesheets
│ ├── 960.css
│ ├── alignments.css
│ ├── all.css
│ ├── reset.css
│ ├── screen.css
│ └── syntax.css
├── lib
│ └── yuicompressor-2.4.2.jar
└── src
├── javascripts
│ └── all.js
└── stylesheets
├── 960.css
├── alignments.css
├── all.css
├── reset.css
├── screen.css.scss
└── syntax.css
And here's my compile task:
require 'rubygems'
require 'bundler'
require 'pathname'
require 'logger'
require 'fileutils'
Bundler.require
ROOT = Pathname(File.dirname(__FILE__))
LOGGER = Logger.new(STDOUT)
BUNDLES = %w( all.css all.js )
BUILD_DIR = ROOT.join("build")
SOURCE_DIR = ROOT.join("src")
task :compile do
sprockets = Sprockets::Environment.new(ROOT) do |env|
env.logger = LOGGER
end
sprockets.append_path(SOURCE_DIR.join('javascripts').to_s)
sprockets.append_path(SOURCE_DIR.join('stylesheets').to_s)
BUNDLES.each do |bundle|
assets = sprockets.find_asset(bundle)
prefix, basename = assets.pathname.to_s.split('/')[-2..-1]
FileUtils.mkpath BUILD_DIR.join(prefix)
assets.write_to(BUILD_DIR.join(prefix, basename))
assets.to_a.each do |asset|
# strip filename.css.foo.bar.css multiple extensions
realname = asset.pathname.basename.to_s.split(".")[0..1].join(".")
asset.write_to(BUILD_DIR.join(prefix, realname))
end
end
end
First I create a new Sprockets::Environment instance passing some configurations, such as a custom logger.
sprockets = Sprockets::Environment.new(ROOT) do |env|
env.logger = LOGGER
end
Then I append the asset paths.
sprockets.append_path(SOURCE_DIR.join('javascripts').to_s)
sprockets.append_path(SOURCE_DIR.join('stylesheets').to_s)
Finally, I process and package the assets to the build directory.
assets.write_to(BUILD_DIR.join(prefix, basename))
Because sometimes I need to reference a single CSS file (e.g. alignments.css) instead of the entire bundle, I also build a standalone package for each CSS source. You might not want to do that.
assets.to_a.each do |asset|
# strip filename.css.foo.bar.css multiple extensions
realname = asset.pathname.basename.to_s.split(".")[0..1].join(".")
asset.write_to(BUILD_DIR.join(prefix, realname))
end
Please keep in mind that if you want to use processors, you must include the corresponding libraries. For example, if you want to support the SASS processor you need to add the sass gem.
Here's my Gemfile.
source "https://rubygems.org"
gem 'sass'
gem 'sprockets'
That's all.