Using Sprockets without a Rails/Rack project

September 22nd, 2011 at 5:28 pm • permalink10 comments

Sprockets is a Ruby library for compiling and serving web assets. It features declarative dependency management for JavaScript and CSS assets and a vert 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 about 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 feature 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 compress it and publish the result to Amazon CloudFront.

Instead of processing and merging the files using file system 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 SASS processor you need to add the sass gem.

Here’s my Gemfile.

source "http://rubygems.org"

gem 'sass'
gem 'sprockets'

That’s all.

  1. Heroku and Rails 3.2 asset:precompile error
  2. The Road to Rails 3: make your Rails 2.3 project more Rails 3 oriented
  3. Capistrano: Executing a command as root without using sudo
  4. Running Capistrano with Passenger (mod_rails)
  5. Understanding Ruby and Rails: Delegate

Filed in Programming • Tags: , , ,

Comments

guilleiguaran says:

Take a look to the StaticCompiler that I added to Rails 3.1.1:

https://github.com/rails/rails/blob/master/actionpack/lib/sprockets/static_compiler.rb

The unique Rails dependent thing is the Rails.application.config.assets.digest, probably it can be stubbed.

Very cool! Yes, that line should be neutralized in some way.
Thank you for your comment!

pixelboy says:

Thanks a lot. I still have some misunderstandings on how things get tied up together, or maybe it’s just me ;) Here’s the deal : I’m trying to use sprockets in a LAMP dev env (using MAMP on osx actually) to generate js files. Most of the differences between what you eplain in the previous article and my try is that i’m ‘simply’ trying to bind my apache to serve js files through something that could look like your compile task. Does that seem clear enough and possible ? Am I going in the wrong direction ? thanks a lot !

Sprockets is a Ruby library. You need a Ruby environment to make it working.

Javix says:

When runnning the code I got:

rake compile
rake aborted!
Don’t know how to build task ‘cleanup’

Tasks: TOP => compile
(See full trace by running task with –trace)

it seems that the task ‘cleanup’ is ulnown ?

Rick Graham says:

Javix, I ran into the same problem..

What you need to do is update the `BUNDLES` to include the files that will contain your “Sprocket-y” requires,

:cleanup refers to the directory that you will be compiling,
If you put all you js into /js/ then you would write:

`task :compile => :js do`

If you update those two things you should be good to go.

And remember to bundle install you gems too!

hth

john says:

In order to make coffee script and scss/sass work my Gemfile have these gems:

gem ‘sass’
gem ‘coffee-script’
gem ‘therubyracer’, :platforms => :ruby
gem ‘sprockets’

In Rakefile i simply changed the line

task :compile => :cleanup do

to

task :compile do

Thanks John, I fixed the code.

Aaron says:

Getting the same error as @Javix. :-/

Any idea on how to fix this? Would really love to get this working!

Aaron says:

I’ve modified my Rakefile to look like this: http://cl.ly/JnpN but still having issues.

I’m getting the error, “undefined method `pathname’ for nil:NilClass”

Any ideas?

Add a Comment




Follow Me
    Random Quote